dtembe

Disable-InactiveADAccounts

Dec 28th, 2017
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ####################################################
  2. #
  3. # AD account cleanup script
  4. # Date Created : 9/22/17
  5. # Author : PowerMonkey500
  6. #
  7. ####################################################
  8. #
  9. # WARNING: THIS SCRIPT WILL OVERWRITE EXTENSIONATTRIBUTE3 FOR INACTIVE USERS, MAKE SURE YOU ARE NOT USING IT FOR ANYTHING ELSE
  10. # This script is SLOW because it gets the most accurate last logon possible by comparing results from all DCs. By default the lastlogontimestamp is only replicated every 14 days minus a random percentage of 5.
  11. #
  12. ####################################################
  13.  
  14. #FUNCTION DECLARATIONS
  15. #===================================================================================
  16.  
  17. #Logging function - starts transcript and cleans logs older than specified retention date.
  18. Function Start-Logging{
  19.     param (
  20.         [Parameter(Mandatory=$true)][String]$LogDirectory,
  21.         [Parameter(Mandatory=$true)][String]$LogName,
  22.         [Parameter(Mandatory=$true)][Int]$LogRetentionDays
  23.         )
  24.  
  25.     #Sets screen buffer from 120 width to 500 width. This stops truncation in the log.
  26.     $ErrorActionPreference = 'SilentlyContinue'
  27.     $pshost = get-host
  28.     $pswindow = $pshost.ui.rawui
  29.  
  30.     $newsize = $pswindow.buffersize
  31.     $newsize.height = 3000
  32.     $newsize.width = 500
  33.     $pswindow.buffersize = $newsize
  34.  
  35.     $newsize = $pswindow.windowsize
  36.     $newsize.height = 50
  37.     $newsize.width = 500
  38.     $pswindow.windowsize = $newsize
  39.     $ErrorActionPreference = 'Continue'
  40.  
  41.     #Create log directory if it does not exist already
  42.     If (!(Test-Path $LogDirectory)){mkdir $LogDirectory}
  43.  
  44.     #Starts logging.
  45.     New-Item -ItemType directory -Path $LogDirectory -Force | Out-Null
  46.     $Today = Get-Date -Format M-d-y
  47.     Start-Transcript -Append -Path ($LogDirectory + "\" + $LogName + "." + $Today + ".log") | Out-Null
  48.  
  49.     #Shows proper date in log.
  50.     Write-Output ("Start time: " + (Get-Date))
  51.  
  52.     #Purges log files older than X days
  53.     $RetentionDate = (Get-Date).AddDays(-$LogRetentionDays)
  54.     Get-ChildItem -Path $LogDirectory -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $RetentionDate } | Remove-Item -Force
  55. }
  56.  
  57. ###NOT USED IN SCRIPT, JUST FOR UTILITY WHEN TWEAKING###
  58. Function Get-ADUserLastLogon ([string]$UserName){
  59.     $dcs = Get-ADDomainController -Filter {Name -like "*"}
  60.       $time = 0
  61.       $dt = 0
  62.         foreach($dc in $dcs) {
  63.         Try
  64.         {
  65.             $user = Get-ADUser -Server $dc -identity $userName -properties LastLogon,LastLogonTimestamp
  66.             if($user.LastLogonTimeStamp -gt $time)
  67.             {
  68.                 $time = $user.LastLogonTimeStamp
  69.             }
  70.             if ($user.LastLogon -gt $time)
  71.             {
  72.                 $time = $user.LastLogon
  73.             }  
  74.         }
  75.         Catch {}
  76.     }
  77.     $dt = [DateTime]::FromFileTime($time)
  78.  
  79.     $OutputObj = New-Object -TypeName PSObject
  80.     $OutputObj | Add-Member -MemberType NoteProperty -Name SamAccountName -Value $user.SamAccountName
  81.     $OutputObj | Add-Member -MemberType NoteProperty -Name Enabled -Value $user.Enabled
  82.     $OutputObj | Add-Member -MemberType NoteProperty -Name LastLogon -Value $dt
  83.     $OutputObj | Add-Member -MemberType NoteProperty -Name GivenName -Value $user.GivenName
  84.     $OutputObj | Add-Member -MemberType NoteProperty -Name Surname -Value $user.SurName
  85.     $OutputObj | Add-Member -MemberType NoteProperty -Name Name -Value $user.Name
  86.     $OutputObj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $user.DistinguishedNameName
  87.  
  88.     Return $OutputObj
  89. }
  90.  
  91. #-----------------------------------
  92. #Main script
  93. #-----------------------------------
  94. #Declarations
  95. $From = "noreply@email.com"
  96. $SMTPServer = "server.domain.local"
  97. $To = @('email1@email.com','email2@email.com') #Array. You can add more than one entry.
  98. $Subject = "Account Cleanup Report"
  99. $TryCount = 0 #Leave this at 0.
  100. $LogDirectory = "C:\Disable-InactiveADAccountsLog" #No trailing slash
  101. $LogName = "Disable-InactiveADAccountsLog"
  102. $MaxTryCount = 20 #Amount of times to try for identical DC results before giving up. 30 second retry delay after each failure.
  103. $UTCSkew = -5 #Accounting for the time zone difference, since some results are given in UTC. Eastern time is UTC-5.
  104. $ExclusionsGroup = "ServiceAccts" #AD group containing accounts to exclude.
  105. $DaysThreshold = 30 #Threshold of days of inactivity before disabling the user.
  106.  
  107. #Start logging
  108. Start-Logging -logdirectory $LogDirectory -logname $LogName -LogRetentionDays 30
  109.  
  110. #Get all DCs, add array names to vars array
  111. $DCnames = @()
  112. If (!$DCs){$DCs = Get-ADGroupMember 'Domain Controllers'}
  113. $DCs | % {$DCnames += $_.Name}
  114.  
  115. #Check that results match from each DC
  116. While (($ComparisonResults -contains $False -or !$ComparisonResults) -and $TryCount -lt $MaxTryCount){
  117. #Fetch AD users from each DC, add to named array
  118.     $DCnames | % {
  119.         #Filters / Exclusions.
  120.         Write-Output ("Fetching last logon times from " + $_ + "...")
  121.         New-Variable -Name $_ -Value (Get-ADUser -Filter {Enabled -eq $True} -Server $_ -Properties DistinguishedName,LastLogon,LastLogonTimestamp,whenCreated,Description | Sort SamAccountName) -Force
  122.     }
  123.  
  124.     $ComparisonResults = @()
  125.     ForEach ($i in 0..(($DCnames.Count)-1)){
  126.         If ($i -le (($DCnames.Count)-2)){
  127.             Write-Output ("Comparing results from " + $DCnames[$i] + " and " + $DCnames[$i+1] + "...")
  128.             $NotEqual = Compare-Object (Get-Variable -Name $DCnames[$i]).Value (Get-Variable -Name $DCnames[$i+1]).Value -Property SamAccountName
  129.  
  130.             If (!$NotEqual) {$ComparisonResults += $True}
  131.             Else {$ComparisonResults += $False}
  132.         }
  133.     }
  134.     If ($ComparisonResults -contains $False){
  135.         Write-Warning "One or more DCs returned differing results. This is likely just replication delay. Retrying..."
  136.         $TryCount++
  137.         Start-Sleep 30
  138.     }
  139. }
  140. If ($TryCount -lt $MaxTryCount){Write-Output "All DC results are identical!"}
  141. Else {Throw "Try limit exceeded. Aborting."}
  142.  
  143. #Get current time for comparison later.
  144. If (!$StartTime){$StartTime = Get-Date}
  145.  
  146. #User count so we know how many times to loop.
  147. $UserCount = (Get-Variable -Name $DCnames[0]).Value.Count
  148.  
  149. #Create results array of the same size
  150. $FullResults = @($null) * $UserCount
  151.  
  152. #Loop through array indexes
  153. ForEach ($i in 0..($UserCount -1)){
  154.     #Grab user object from each resultant array, make array of each user object
  155.     $UserEntries = @(0) *$DCnames.Count
  156.     ForEach ($o in 0..($DCnames.Count -1)) {
  157.         $UserEntries[$o] = (Get-Variable -Name $DCnames[$o]).Value[$i]
  158.     }
  159.     If (($UserEntries.SamAccountName | Select -Unique).Count -gt 1){Throw "A user mismatch at index $i has occurred. Aborting."}
  160.  
  161.     #Find most recent LastLogon, whenCreated, and LastLogonTimestamp.
  162.     If ($UserEntries.LastLogon){
  163.         [datetime]$LastLogon = [datetime]::FromFileTimeUtc(($UserEntries | Measure-Object -Property LastLogon -Maximum).Maximum)
  164.         [datetime]$TrueLastLogon = $LastLogon
  165.     }
  166.     Else {[datetime]$LastLogon = 0; $TrueLastLogon = 0}
  167.  
  168.     If ($UserEntries.whenCreated){
  169.         [datetime]$whenCreated = $UserEntries[0].whenCreated
  170.     }
  171.     Else {[datetime]$whenCreated = 0}
  172.  
  173.     If ($UserEntries.LastLogonTimestamp){
  174.         [datetime]$LastLogonTimestamp = [datetime]::FromFileTimeUtc(($UserEntries | Measure-Object -Property LastLogonTimestamp -Maximum).Maximum)
  175.     }
  176.     Else {[datetime]$LastLogonTimestamp = 0}
  177.  
  178.     #If LastLogonTimestamp is newer, use that instead of LastLogon.
  179.     If ($LastLogonTimestamp -gt $LastLogon){$TrueLastLogon = $LastLogonTimestamp}
  180.  
  181.     #UTC to EST
  182.     If ($TrueLastLogon -ne 0){$TrueLastLogon = $TrueLastLogon.AddHours($UTCSkew)}
  183.  
  184.     #If TrueLastLogon is older than 20 years (essentially null/zero), set to true zero
  185.     If ((New-TimeSpan -Start $TrueLastLogon -End $StartTime).Days -gt 7300){[string]$TrueLastLogon = $null}
  186.  
  187.     #Calculate days of inactivity.
  188.     If ($TrueLastLogon -ne $null -and $TrueLastLogon -ne ""){$DaysInactive = (New-TimeSpan -Start $TrueLastLogon -End $StartTime).Days}
  189.     Else {$DaysInactive = (New-TimeSpan -Start $whenCreated -End $StartTime).Days}
  190.  
  191.     #Create object for output array
  192.     $OutputObj = New-Object -TypeName PSObject
  193.     $OutputObj | Add-Member -MemberType NoteProperty -Name SamAccountName -Value $UserEntries[0].SamAccountName
  194.     $OutputObj | Add-Member -MemberType NoteProperty -Name Enabled -Value $UserEntries[0].Enabled
  195.     $OutputObj | Add-Member -MemberType NoteProperty -Name LastLogon -Value $TrueLastLogon
  196.     $OutputObj | Add-Member -MemberType NoteProperty -Name whenCreated -Value $whenCreated
  197.     $OutputObj | Add-Member -MemberType NoteProperty -Name DaysInactive -Value $DaysInactive
  198.     $OutputObj | Add-Member -MemberType NoteProperty -Name GivenName -Value $UserEntries[0].GivenName
  199.     $OutputObj | Add-Member -MemberType NoteProperty -Name Surname -Value $UserEntries[0].SurName
  200.     $OutputObj | Add-Member -MemberType NoteProperty -Name Name -Value $UserEntries[0].Name
  201.     $OutputObj | Add-Member -MemberType NoteProperty -Name DistinguishedName -Value $UserEntries[0].DistinguishedName
  202.     $OutputObj | Add-Member -MemberType NoteProperty -Name Description -Value $UserEntries[0].Description
  203.  
  204.     #Append object to output array and output preogress to console.
  205.     $FullResults[$i] = $OutputObj
  206.     $PercentComplete = [math]::Round((($i/$UserCount) * 100),2)
  207.     Write-Output ("User: " + $OutputObj.SamAccountName + " - Last logon: $TrueLastLogon ($DaysInactive day(s) inactivity) - $PercentComplete% complete.")
  208. }
  209.  
  210. Write-Output "Getting exclusion group members..."
  211. $UserExclusions = (Get-ADGroupMember -Identity $ExclusionsGroup).SamAccountName
  212.  
  213. #Splits "other" and "real" users into two different arrays.
  214. Write-Output "Filtering users..."
  215. $RealUsersResults = @()
  216. $RealUsersResults = $FullResults | Where {$UserExclusions -notcontains $_.SamAccountName}
  217.  
  218. $OtherUsersResults = @()
  219. $FullResults = $FullResults | Where {$_ -ne $null}
  220.  
  221. #For some reason compare-object is not working properly without specifying all properties. Don't know why.
  222. $OtherUsersResults = Compare-Object $RealUsersResults $FullResults `
  223. -Property SamAccountName,enabled,lastlogon,whencreated,DaysInactive,givenname,surname,name,distinguishedname,Description | `
  224. select SamAccountName,enabled,lastlogon,whencreated,DaysInactive,givenname,surname,name,distinguishedname,Description
  225.  
  226. #Disable accounts with inactivity past days threshold, add to UsersDisabled array for CSV report
  227. $UsersDisabled = @()
  228. $RealUsersResults | % {
  229.     If ($_.DaysInactive -ge $DaysThreshold){
  230.         Write-Output ("Disabling " + $_.SamAccountName + "...")
  231.         Disable-ADAccount -Identity $_.SamAccountName
  232.         $Date = "INACTIVE SINCE " + $_.LastLogon
  233.         Set-ADUser -Identity $_.SamAccountName -Replace @{ExtensionAttribute3=$Date}
  234.         $UsersDisabled += $_
  235.     }
  236. }
  237.  
  238. #Filtered users - add to UsersNotDisabled array for CSV report
  239. $OtherInactiveUsers = @()
  240. $OtherUsersResults | % {
  241.     If ($_.DaysInactive -ge $DaysThreshold){
  242.         $OtherInactiveUsers += $_
  243.     }
  244. }
  245.  
  246. #Reports "Auto-Disable Exclusions-ENT" members
  247. $ExcludedUsersReport = $FullResults | Where {$UserExclusions -contains $_.SamAccountName} | Select * -ExcludeProperty Enabled,LastLogon,whenCreated,DaysInactive
  248.  
  249. #Export CSVs to logging directory
  250. $UsersDisabledCSV = $LogDirectory + "\InactiveUsers-Disabled.csv"
  251. $UsersNotDisabledCSV = $LogDirectory + "\InactiveUsers-Excluded.csv"
  252. $ExcludedUsersReportCSV = $LogDirectory + "\Auto-Disable Exclusions.csv"
  253. $UsersDisabled | Export-CSV $UsersDisabledCSV -NoTypeInformation -Force
  254. $OtherInactiveUsers | Export-CSV $UsersNotDisabledCSV -NoTypeInformation -Force
  255. $ExcludedUsersReport | Export-CSV $ExcludedUsersReportCSV -NoTypeInformation -Force
  256.  
  257. <#
  258. Write-Output "Starting Move-Disabled task..."
  259. Start-ScheduledTask -TaskName "\Move-Disabled"
  260. #>
  261.  
  262. #Send email with CSVs as attachments
  263. Write-Output "Sending email..."
  264. Send-MailMessage -Attachments @($UsersDisabledCSV,$UsersNotDisabledCSV,$ExcludedUsersReportCSV)`
  265.  -From $From -SmtpServer $SMTPServer -To $To -Subject $Subject
  266.  
  267. Stop-Transcript
Add Comment
Please, Sign In to add comment