In this post we'll talk about Disable-Inactive-ADAccounts, a small yet useful Powershell script that can be used by System Administrators to perform the following tasks:
- Disable all the Active Directory user accounts inactive for more than X days
- Delete all the Active Directory user accounts prevously disabled more than Y days ago.
The two above tasks can be run independently using the provided command-line switches.
Why should we do that?
As a matter of fact, being able to automatically disable AD accounts after X days of inactivity is a good security practice. If you don't have such process up, your Active Directory could grant "permanent" access to many user accounts that should no longer be active, such as those of ex-employees or collaborators who are no longer active at your company.
Unfortunately, such feature is not (yet) supported by any version of Windows or Windows Server, at least up to Windows 10 and Windows Server 2019. That's why we ended up to develop this Powershell script.
Usage
To disable all AD users that has been inactive for 180 days or more (without deleting them):
> powershell .\Disable-Invalid-ADAccounts.ps1 -days 180
Same thing as before, plus creating a logFile.csv file containing a list of all disabled users:
> powershell .\Disable-Invalid-ADAccounts.ps1 -days 180
To disable all AD users that has been inactive for 180 days or more and also delete those that have been previously disabled more than 180 days ago.
> powershell .\Disable-Invalid-ADAccounts.ps1 -days 180 -deleteDays 180
Same thing as before, plus creating a logFile.csv file containing a list of all disabled users and a deleteLogFile.csv file containing a list of all deleted users:
powershell .\Disable-Invalid-ADAccounts.ps1 -days 180
In case you get permissions issues when disabling/deleting AD users, you can bypass them using the Bypass Execution Policy Flag in the following way:
powershell -ExecutionPolicy Bypass -File Disable-Invalid-ADAccounts.ps1
The Code
Here's the full script source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
param( [Parameter(Mandatory=$false)] [int] $days = 180, # -deleteDays : delete all previously disabled users since X days or more. # If set to -1, it won't delete anything. Default is -1. [Parameter(Mandatory=$false)] [int] $deleteDays = -1, # -logFile : if set to a valid PATH, it will output a CSV files containing the disabled users. # If set to null (default) it won't create a logFile. [Parameter(Mandatory=$false)] [string] $logFile = $null, # -deleteLogFile : if set to a valid PATH, it will output a CSV files containing the deleted users. # If set to null (default) it won't create a logFile. [Parameter(Mandatory=$false)] [string] $deleteLogFile = $null ) #Search all inactive accounts [Datetime]$ts = (Get-Date).AddDays(-$days) # (Search-ADAccount -UsersOnly -AccountInactive -TimeSpan $ts.Day | Select-Object DistinguishedName).count # Search-ADAccount -UsersOnly -AccountInactive -TimeSpan $ts.Day | Select-Object DistinguishedName,sAMAccountName # $users = {(Search-ADAccount -UsersOnly -AccountInactive | ? { $_.LastLogonDate -lt (get-date).AddDays(-$days)}) | Select-Object DistinguishedName,sAMAccountName,Enabled } $users = Search-ADAccount -UsersOnly -AccountInactive -TimeSpan $ts.Day | Select-Object DistinguishedName,sAMAccountName, Enabled Write-Host "$($users.Count)" # Adding the time of account disabled in the extentionAttribute10 for all accounts. # This will help while deleting the accounts if [deleteDays] parameter is set. [string]$t = ((Get-Date).Datetime) $disabled = 0; $alreadyDisabled = 0; $deleted = 0; # Disable the inactive accounts found if ($days -gt -1) { Foreach ($user in $users) { If ($user.Enabled) { $disabled++ Set-ADUser $user.DistinguishedName -add @{extensionAttribute10="$t"} Disable-ADAccount -Identity $user.DistinguishedName } Else { $alreadyDisabled++ } } # Export the disabled accounts If ($logFile) { $users | Export-Csv "$logFile" } } # if $deleteDays is set, delete the already-disabled accounts found If ($deleteDays -gt -1) { $tsD = (Get-Date).AddDays(-$deleteDays) $usersD = Get-ADUser -Filter {enabled -eq $false} -Properties * | Where-Object{"extensionAttribute10" -in $_.PSobject.Properties.Name} | Where-Object{$tsD -ge $_.extensionAttribute10} Foreach($user in $usersD) { $deleted++ Remove-ADUser $user } # Export the deleted accounts If ($deletedLogFile) { $usersD | Export-Csv "$deletedLogFile" } } Write-Host "" Write-Host "Operation complete:" If ($days -gt -1) { Write-Host " - $($disabled) accounts has been disabled ($($alreadyDisabled) already disabled)." If ($logFile) { Write-Host " Disabled accounts logged to $($logFile) file." } } If ($deleteDays -gt -1) { Write-Host " - $($deleted) accounts has been deleted." If ($deleteLogFile) { Write-Host " Deleted accounts logged to $($deleteLogFile) file." } } |
As you can see there are a lot of in-line comments, thus making the above code quite self-explanatory: however, if you got questions, feel free to ask anything using the comments section below this post.
Conclusion
That's about it: if you like this script, feel free to give me a virtual hug by starring the GitHub project and/or putting a like on our Facebook/Twitter pages!