13
13
14
14
<#
15
15
. SYNOPSIS
16
- Get users via Microsoft Graph based on sign-in activity
16
+ Get users via Microsoft Graph based on lack of sign-in activity
17
17
18
18
. DESCRIPTION
19
19
This script will retrieve a list of users who have not signed in for at least a specified number of days.
20
20
Requires Microsoft Entra P1 or P2 license in the tenant.
21
- Requires Microsoft.Graph.Authentication module.
22
- Requires the signed in user to have User.Read.All (or higher) delegated scope. Permissions: https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#permissions
23
- Requires the signed in user to have AuditLog.Read.All delegated scope and a sufficient Entra role (Reports Reader is least privileged role). Permissions: https://learn.microsoft.com/en-us/graph/api/signin-list?view=graph-rest-1.0&tabs=http#permissions
21
+ Requires the signed in user to have User.Read.All (or higher) delegated scope.
22
+ Permissions: https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#permissions
23
+ Requires the signed in user to have AuditLog.Read.All delegated scope and a sufficient Entra role. (Reports Reader is least privileged role.)
24
+ Permissions: https://learn.microsoft.com/en-us/graph/api/signin-list?view=graph-rest-1.0&tabs=http#permissions
24
25
25
26
. PARAMETER SignInType
26
27
Filter users on the type of sign-in: interactive (successful or unsuccessful), non-interactive (successful or unsuccessful),
27
28
or successful (for either type). Valid values are Interactive, NonInteractive, and Successful.
28
29
Successful is the default.
29
30
30
- . PARAMETER DaysOfInactivity
31
- The number of days of sign-in inactivity for the user to be returned. Default value is 30.
31
+ . PARAMETER MemberDaysOfInactivity
32
+ The number of days of sign-in inactivity for a member user to be returned. Default value is 30.
33
+ Note: Users with a null value for the date/time of the sign-in type will not be returned.
34
+
35
+ . PARAMETER GuestDaysOfInactivity
36
+ The number of days of sign-in inactivity for a guest user to be returned. Default value is 90.
37
+ Cannot be less than MemberDaysOfInactivity if members are included.
32
38
Note: Users with a null value for the date/time of the sign-in type will not be returned.
33
39
34
40
. PARAMETER CloudEnvironment
42
48
Switch to skip exporting the results to CSV and instead output the result objects to the host.
43
49
44
50
. NOTES
45
- Version 1.4.2
46
- January 7 , 2025
51
+ Version 1.5
52
+ July 30 , 2025
47
53
48
54
. LINK
49
55
about_functions_advanced
52
58
[CmdletBinding ()]
53
59
param (
54
60
[ValidateSet (' Interactive' , ' NonInteractive' , ' Successful' )]$SignInType = ' Successful' ,
55
- [int ]$DaysOfInactivity = 30 ,
61
+ [int ]$MemberDaysOfInactivity = 30 ,
62
+ [int ]$GuestDaysOfInactivity = 90 ,
56
63
[ValidateSet (" Member" , " Guest" )][string []]$UserType = @ (" Member" , " Guest" ),
57
64
[ValidateSet (" Commercial" , " USGovGCC" , " USGovGCCHigh" , " USGovDoD" , " China" )][string ]$CloudEnvironment = " Commercial" ,
58
65
[switch ]$DoNotExportToCSV
59
66
)
60
67
68
+ if ($UserType -contains ' Member' -and $GuestDaysOfInactivity -lt $MemberDaysOfInactivity ) {
69
+ Write-Error - Message " GuestDaysOfInactivity cannot be less than MemberDaysOfInactivity when UserType includes Members."
70
+ exit
71
+ }
72
+
61
73
# Start-Transcript -Path "Transcript-inactiveusers.txt" -Append
62
74
switch ($CloudEnvironment ) {
63
75
" Commercial" {$cloud = " Global" }
@@ -94,7 +106,14 @@ if ($neededScopes) {
94
106
Connect-MgGraph - ContextScope CurrentUser - Scopes $neededScopes - Environment $cloud - NoWelcome
95
107
}
96
108
97
- $targetdate = (Get-Date ).ToUniversalTime().AddDays(- $DaysOfInactivity ).ToString(" o" )
109
+ if ($UserType -contains ' Member' ) {
110
+ $targetdate = (Get-Date ).ToUniversalTime().AddDays(- $MemberDaysOfInactivity ).ToString(" o" )
111
+ } else {
112
+ $targetdate = (Get-Date ).ToUniversalTime().AddDays(- $GuestDaysOfInactivity ).ToString(" o" )
113
+ }
114
+ # Used for client-side filtering of results for guest users
115
+ $guestTargetDate = (Get-Date ).ToUniversalTime().AddDays(- $GuestDaysOfInactivity )
116
+
98
117
$result = New-Object - TypeName System.Collections.ArrayList
99
118
switch ($SignInType ) {
100
119
Interactive {$siFilter = ' signInActivity/lastSignInDateTime' }
@@ -106,7 +125,14 @@ switch ($SignInType) {
106
125
# https://learn.microsoft.com/en-us/entra/identity/monitoring-health/howto-manage-inactive-user-accounts
107
126
$apiUrl = " /v1.0/users?`$ filter=$siFilter lt $ ( $targetdate ) &`$ select=accountEnabled,id,userType,signInActivity,userprincipalname"
108
127
Write-Verbose " Initial URL: $apiUrl "
109
- Write-Host - ForegroundColor Green " $ ( Get-Date ) Getting users based on $DaysOfInactivity days of inactivity..."
128
+ $typeMessage = @ ()
129
+ if ($UserType -contains ' Member' ) {
130
+ $typeMessage += " member users with $MemberDaysOfInactivity + days"
131
+ }
132
+ if ($UserType -contains ' Guest' ) {
133
+ $typeMessage += " guest users with $GuestDaysOfInactivity + days"
134
+ }
135
+ Write-Host - ForegroundColor Green " $ ( Get-Date ) Getting $ ( $typeMessage -join ' and ' ) of sign-in inactivity..."
110
136
do {
111
137
# Get data via Graph and continue paging until complete
112
138
$response = Invoke-MgGraphRequest - Method GET $apiUrl - OutputType PSObject
@@ -122,14 +148,16 @@ if ($result.Count -gt 0) {
122
148
123
149
$return = @ ()
124
150
foreach ($item in $result ) {
125
- if (($UserType -contains " Member" -and $item.UserType -eq " Member" ) -or ($UserType -contains " Guest" -and $item.UserType -eq " Guest" )) {
151
+ if (($UserType -contains ' Member' -and $item.userType -eq ' Member' ) -or
152
+ ($UserType -contains ' Guest' -and $item.userType -eq ' Guest' -and $item .' signInActivity' .$ ($siFilter.SubString ($siFilter.IndexOf (' /' )+1 )) -lt $guestTargetDate )) {
153
+
126
154
if ($null -ne $item.userPrincipalName -and $item.accountEnabled -eq $true ) {
127
155
$return += New-Object - TypeName PSObject - Property @ {
128
- UserPrincipalName = $item.userprincipalname
129
- LastSuccessfulSignIn = $item.signinactivity .lastSuccessfulSignInDateTime
130
- LastInteractiveSignIn = $item.signinactivity.lastsignindatetime
131
- LastNonInteractiveSignIn = $item.signinactivity .lastNonInteractiveSignInDateTime
132
- UserType = $item.usertype
156
+ UserPrincipalName = $item.userPrincipalName
157
+ LastSuccessfulSignIn = $item.signInActivity .lastSuccessfulSignInDateTime
158
+ LastInteractiveSignIn = $item.signInActivity.lastSignInDateTime
159
+ LastNonInteractiveSignIn = $item.signInActivity .lastNonInteractiveSignInDateTime
160
+ UserType = $item.userType
133
161
}
134
162
}
135
163
}
0 commit comments