From be6c35585604a973e63b5e9e79d04152fccf62c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0t=C4=9Bp=C3=A1n?= Date: Tue, 21 Jul 2026 14:50:49 +0200 Subject: [PATCH] Add Get-InactiveUsers script to list Active Directory users inactive for over 90 days --- Get-InactiveUsers.ps1 | 143 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 Get-InactiveUsers.ps1 diff --git a/Get-InactiveUsers.ps1 b/Get-InactiveUsers.ps1 new file mode 100644 index 0000000..8510f62 --- /dev/null +++ b/Get-InactiveUsers.ps1 @@ -0,0 +1,143 @@ +<# +.SYNOPSIS + Lists all Active Directory users who have not logged on for more than 90 days + (default) and are not disabled. + +.DESCRIPTION + Uses Active Directory cmdlets to find all users and checks their last logon + date. Users who have had no active logon for longer than the configured + number of days (default 90) are returned. + +.PARAMETER Days + Number of days after which a user is considered inactive. Defaults to 90. + +.PARAMETER IncludeDisabled + If set, the script also includes DISABLED users in the results. + +.PARAMETER SearchBase + If set, the script searches for users only within the specified OU(s). This + is an array, so multiple OUs can be supplied; each one is searched + recursively and the results are combined. + +.EXAMPLE + .\Get-InactiveUsers.ps1 + Displays users who have not been active for more than 90 days. + +.EXAMPLE + .\Get-InactiveUsers.ps1 | Sort-Object LastLogonDate | Format-Table -AutoSize + Displays users who have not been active for more than 90 days, sorted by + last logon date. + +.EXAMPLE + .\Get-InactiveUsers.ps1 -Days 180 + Displays users who have not been active for more than 180 days. + +.EXAMPLE + .\Get-InactiveUsers.ps1 -Days 365 -IncludeDisabled + Displays users who have not been active for more than 365 days, including + DISABLED users. + +.EXAMPLE + .\Get-InactiveUsers.ps1 -SearchBase "OU=Users,DC=example,DC=com" + Displays users who have not been active for more than 90 days, searching + only within the specified OU. + +.EXAMPLE + .\Get-InactiveUsers.ps1 | Export-Csv -Path .\Export_InactiveUsers.csv -NoTypeInformation -Encoding UTF8 + Exports all inactive users in Active Directory to a CSV file. + +.EXAMPLE + .\Get-InactiveUsers.ps1 | Set-ADUser -Enabled $false + Disables all inactive users in Active Directory. + +.NOTES + Author: Petr Štěpán + Created: 2024-10-20 + Version: 1.1.0 + Changelog: + 1.1.0 - Fixed -SearchBase to correctly support multiple OUs (Get-ADUser's + own -SearchBase only accepts one); added standard header, + Set-StrictMode/$ErrorActionPreference, error handling, exit codes. + 1.0.0 - Initial version +#> + +#Requires -Modules ActiveDirectory +[CmdletBinding()] +param ( + [Parameter(Mandatory = $false, HelpMessage = 'Number of days after which a user is considered inactive.')] + [ValidateRange(15, [int]::MaxValue)] + [int] + $Days = 90, + + [Parameter(Mandatory = $false, HelpMessage = 'Also include DISABLED users?')] + [switch] + $IncludeDisabled, + + [Parameter(Mandatory = $false, HelpMessage = 'Search for users only within the specified OU(s).')] + [ValidateNotNullOrEmpty()] + [string[]] + $SearchBase +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Read-only script (queries Active Directory only) - no ShouldProcess needed. +# The caller decides what to do with the results (export, disable, ...) via the pipeline. + +$propertiesToRetrieve = 'Name', 'SamAccountName', 'LastLogonDate', 'Enabled', 'PasswordNeverExpires', 'CanonicalName', 'DistinguishedName' + +# Date beyond which a user is considered inactive +$inactiveDate = (Get-Date).AddDays(-$Days) + +if ($IncludeDisabled) { + $parameters = @{ + Filter = { + LastLogonDate -le $inactiveDate + } + } +} +else { + $parameters = @{ + Filter = { + LastLogonDate -le $inactiveDate -and Enabled -eq $true + } + } +} + +if ($SearchBase) { + # Get-ADUser's own -SearchBase only accepts a single OU, so each requested + # OU is queried separately and the results are combined. + $inactiveUsers = [System.Collections.Generic.List[object]]::new() + $failedOuCount = 0 + foreach ($ou in $SearchBase) { + Write-Verbose "Searching for inactive users in OU '$ou'..." + try { + $ouUsers = Get-ADUser @parameters -SearchBase $ou -Properties $propertiesToRetrieve -ErrorAction Stop + foreach ($u in $ouUsers) { $inactiveUsers.Add($u) } + } + catch { + Write-Warning "Skipping OU '$ou': failed to query users - $_" + $failedOuCount++ + } + } + + if ($failedOuCount -eq $SearchBase.Count) { + Write-Error "Failed to retrieve users from any of the specified OUs." + exit 1 + } +} +else { + Write-Verbose "Searching for inactive users across the entire domain..." + try { + $inactiveUsers = @(Get-ADUser @parameters -Properties $propertiesToRetrieve -ErrorAction Stop) + } + catch { + Write-Error "Failed to query inactive users from Active Directory: $_" + exit 1 + } +} + +$inactiveUsers | Select-Object -Property $propertiesToRetrieve + +exit 0