Public/Get-Lockouts.ps1
Function Get-Lockouts { <# .SYNOPSIS Pipe in Search Term or User Object Queries AD for all Domain Controllers Queries the list of DCs to find lockout sources with bad password counts greater then 5 Runs Get-WinEvent with a custom XML formatted around the provided usernames Outputs all lockout events with relevant source information .NOTES Name: Get-Lockouts Author: Luke Hagar Version: 1.0 DateCreated: January 20th, 2021 .EXAMPLE Single Search Get-Lockouts "Luke.Hagar" Get-Lockouts "Luke.Hagar" -Verbose .EXAMPLE Array Search $Users = {"SamAccountName","EmployeeID","mail"} $Users | Get-Lockouts | Format-Table -AutoSize -GroupBy EventDataTargetUserName -Unique .EXAMPLE Search All Locked out AD User Objects $Users = Search-AdAccount -LockedOut $Users | Get-Lockouts | Format-Table -AutoSize -GroupBy EventDataTargetUserName -Unique .LINK #> [CmdletBinding()] param ( [Parameter( ValueFromPipelineByPropertyName )] [Alias("SamAccountName", "DistinguishedName", "GUID", "SID", "EmployeeID", "mail", "UserPrincipalName")] $Identity = $ENV:USERNAME, [string[]]$Properties = @("Name", "EmployeeID", "physicalDeliveryOfficeName", "Title", "mail", "msRTCSIP-PrimaryUserAddress", "CanonicalName", "DistinguishedName", "samaccountname", "UserPrincipalname", "AccountExpirationDate", "Enabled", "Manager", "badPwdCount", "LastBadPasswordAttempt", "LockedOut", "LockOutTime", "lastLogonDate", "PasswordExpired", "PasswordLastSet", "whenCreated", "whenChanged"), [string[]]$Select = @("Name", "EmployeeID", "physicalDeliveryOfficeName", "Title", "mail", "msRTCSIP-PrimaryUserAddress", "CanonicalName", "DistinguishedName", "samaccountname", "UserPrincipalname", "AccountExpirationDate", "Enabled", "Manager", "badPwdCount", "LastBadPasswordAttempt", "LockedOut", "LockOutTime", "lastLogonDate", "PasswordExpired", "PasswordLastSet", "whenCreated", "whenChanged") ) BEGIN { #get list of Domain Controllers $DomainControllers = Get-ADDomainController -Filter * #XML part 1 to be joined later around username variable $LastHourXMLPart1 = "<QueryList> <Query Id='0' Path='Security'> <Select Path='Security'>*[System[Provider[@Name='Microsoft-Windows-Security-Auditing'] and (Level=4 or Level=0) and (band(Keywords, 13510798882111488)) and (EventID=4740) and TimeCreated[timediff(@SystemTime) < = 604800000]]] and *[EventData[Data[@Name='TargetUserName'] = '" #XML part 2 to be joined later around username variable $LastHourXMLPart2 = "']] </Select> </Query> </QueryList>" } PROCESS { #Find user account to validate input data Try { $Users = Get-ADUser -Filter "employeeid -eq '$Identity' -or SamAccountName -eq '$Identity' -or DistinguishedName -eq '$Identity' -or GUID -eq '$Identity' -or SID -eq '$Identity' -or EmployeeID -eq '$Identity' -or mail -eq '$Identity' -or UserPrincipalName -eq '$Identity'" -Properties $Properties | Select-Object $Select } Catch { Write-Warning $_.Exception.Message } #Create results array $Results = @() Foreach ($User in $Users) { Write-Verbose "$($User.Name) found" If ($User.lockedout -eq $true) { $LockoutSources = @() #output found account name for visibility Write-Verbose "$($User.Name)($($User.SamAccountName)) is LockedOut, Searching for source DCs" #Merge Queries together around User Search variable [xml]$FinalQuery = "$LastHourXMLPart1" + $User.SamAccountName + "$LastHourXMLPart2" #Test each DCs to find sources Foreach ($DC in $DomainControllers) { Write-Verbose "Checking $($DC.Hostname.ToUpper())" $DCQuery = Get-ADUser -Identity $User.SamAccountName -Server $DC.Hostname -Properties BadPwdCount If ($DCQuery.BadPwdCount -gt 5) { Write-Verbose "$($DCQuery.BadPwdCount) Bad Passwords found on $($DC.Hostname.ToUpper())" $LockoutSources += $DC.Hostname } } if ($LockoutSources.Count -gt 0) { Foreach ($DC in $LockoutSources) { Write-Verbose "Checking $($DC.ToUpper()) for lockout events" $Temp = Get-WinEvent -ComputerName $DC -FilterXml $FinalQuery -ErrorAction SilentlyContinue | Get-WinEventData | Select-Object TimeCreated, EventDataTargetUserName, EventDataTargetDomainName, EventDataSubjectUserName $Results += [PSCustomObject]@{ SamAccountName = $User.SamAccountName DC = $DC.Hostname.ToUpper() TimeCreated = $Temp.TimeCreated EventDataTargetUserName = $Temp.EventDataTargetUserName EventDataTargetDomainName = $Temp.EventDataTargetDomainName EventDataSubjectUserName = $Temp.EventDataSubjectUserName Result = "Success" } } } else { Write-Verbose "No Lockout Sources found" } } else { Write-Verbose "User not lockedout" } } Return $Results } END { } } |