Skip to content

Commit 8586ee5

Browse files
committed
Allow different member and guest inactivity thresholds
1 parent 821bff5 commit 8586ee5

File tree

1 file changed

+45
-17
lines changed

1 file changed

+45
-17
lines changed

Get-InactiveUsers.ps1

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,28 @@
1313

1414
<#
1515
.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
1717
1818
.DESCRIPTION
1919
This script will retrieve a list of users who have not signed in for at least a specified number of days.
2020
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
2425
2526
.PARAMETER SignInType
2627
Filter users on the type of sign-in: interactive (successful or unsuccessful), non-interactive (successful or unsuccessful),
2728
or successful (for either type). Valid values are Interactive, NonInteractive, and Successful.
2829
Successful is the default.
2930
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.
3238
Note: Users with a null value for the date/time of the sign-in type will not be returned.
3339
3440
.PARAMETER CloudEnvironment
@@ -42,8 +48,8 @@
4248
Switch to skip exporting the results to CSV and instead output the result objects to the host.
4349
4450
.NOTES
45-
Version 1.4.2
46-
January 7, 2025
51+
Version 1.5
52+
July 30, 2025
4753
4854
.LINK
4955
about_functions_advanced
@@ -52,12 +58,18 @@
5258
[CmdletBinding()]
5359
param (
5460
[ValidateSet('Interactive','NonInteractive','Successful')]$SignInType = 'Successful',
55-
[int]$DaysOfInactivity = 30,
61+
[int]$MemberDaysOfInactivity = 30,
62+
[int]$GuestDaysOfInactivity = 90,
5663
[ValidateSet("Member", "Guest")][string[]]$UserType = @("Member", "Guest"),
5764
[ValidateSet("Commercial", "USGovGCC", "USGovGCCHigh", "USGovDoD", "China")][string]$CloudEnvironment="Commercial",
5865
[switch]$DoNotExportToCSV
5966
)
6067

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+
6173
# Start-Transcript -Path "Transcript-inactiveusers.txt" -Append
6274
switch ($CloudEnvironment) {
6375
"Commercial" {$cloud = "Global"}
@@ -94,7 +106,14 @@ if ($neededScopes) {
94106
Connect-MgGraph -ContextScope CurrentUser -Scopes $neededScopes -Environment $cloud -NoWelcome
95107
}
96108

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+
98117
$result = New-Object -TypeName System.Collections.ArrayList
99118
switch ($SignInType) {
100119
Interactive {$siFilter = 'signInActivity/lastSignInDateTime'}
@@ -106,7 +125,14 @@ switch ($SignInType) {
106125
# https://learn.microsoft.com/en-us/entra/identity/monitoring-health/howto-manage-inactive-user-accounts
107126
$apiUrl = "/v1.0/users?`$filter=$siFilter lt $($targetdate)&`$select=accountEnabled,id,userType,signInActivity,userprincipalname"
108127
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..."
110136
do {
111137
# Get data via Graph and continue paging until complete
112138
$response = Invoke-MgGraphRequest -Method GET $apiUrl -OutputType PSObject
@@ -122,14 +148,16 @@ if ($result.Count -gt 0) {
122148

123149
$return=@()
124150
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+
126154
if ($null -ne $item.userPrincipalName -and $item.accountEnabled -eq $true) {
127155
$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
133161
}
134162
}
135163
}

0 commit comments

Comments
 (0)