Windows · Windows PowerShell 5.1
Failed sign-ins on this PC (event 4625)
Which accounts failed to sign in to this device recently, from which workstation or IP address, with what logon type, and why did each attempt fail?
Script
#Requires -RunAsAdministrator
# Failed sign-in attempts (event 4625) from this device's Security log, with the reason. Read-only.
param(
[int]$Hours = 24,
[switch]$ByAccount
)
$logonTypes = @{
'2' = 'Interactive'; '3' = 'Network'; '4' = 'Batch'; '5' = 'Service'; '7' = 'Unlock'
'8' = 'NetworkCleartext'; '9' = 'NewCredentials'; '10' = 'RemoteInteractive'; '11' = 'CachedInteractive'
}
$reasons = @{
'0xc000005e' = 'No logon servers available'
'0xc0000064' = 'User name does not exist or is misspelled'
'0xc000006a' = 'Wrong password'
'0xc000006d' = 'Bad user name or authentication information'
'0xc000006f' = 'Sign-in outside allowed hours'
'0xc0000070' = 'Sign-in from a workstation that is not allowed'
'0xc0000072' = 'Account disabled'
'0xc000015b' = 'Logon type not granted on this computer'
'0xc0000192' = 'Netlogon service not started'
'0xc0000193' = 'Account expired'
'0xc0000234' = 'Account locked out'
'0xc0000413' = 'Blocked by an authentication firewall'
}
$filter = @{ LogName = 'Security'; Id = 4625; StartTime = (Get-Date).AddHours(-$Hours) }
$events = @(Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue)
if ($events.Count -eq 0) {
Write-Output "No event 4625 in the last $Hours hours (or failure auditing for logons is off)."
}
$failures = foreach ($record in $events) {
$data = @{}
foreach ($item in ([xml]$record.ToXml()).Event.EventData.Data) { $data[$item.Name] = $item.'#text' }
$code = if ($data['SubStatus'] -and $data['SubStatus'] -ne '0x0') { $data['SubStatus'] } else { $data['Status'] }
$key = "$code".ToLowerInvariant()
[pscustomobject]@{
TimeCreated = $record.TimeCreated
Account = "$($data['TargetDomainName'])\$($data['TargetUserName'])"
Reason = if ($reasons.ContainsKey($key)) { $reasons[$key] } else { $code }
LogonType = if ($logonTypes.ContainsKey("$($data['LogonType'])")) { $logonTypes["$($data['LogonType'])"] } else { $data['LogonType'] }
Workstation = $data['WorkstationName']
IpAddress = $data['IpAddress']
Status = $data['Status']
SubStatus = $data['SubStatus']
}
}
if ($ByAccount) {
$failures | Group-Object Account | Sort-Object Count -Descending | Select-Object Count, Name
} else {
$failures | Sort-Object TimeCreated -Descending
}
Checked automatically against Microsoft's published cmdlet reference. KBY hasn't run it in your tenant; run it in a test environment first.
Where to run it
The device or server you want to check, in an elevated Windows PowerShell 5.1 session. On a domain controller it shows failed sign-ins that controller handled.
Reading the output
- Reads each event's named fields (TargetUserName, Status, SubStatus, LogonType, WorkstationName, IpAddress), as listed in Microsoft's event 4625 reference, instead of parsing the message text.
- Reason is Microsoft's description of the SubStatus code (or the Status code when SubStatus is 0x0) from the same page. Codes the page doesn't describe are shown as the raw code; look them up in the NTSTATUS values list.
- Event 4625 is only written when failure auditing for logons is on (the Audit Logon and Audit Account Lockout subcategories). An empty result can mean auditing is off, not that nothing failed.
- Pass -ByAccount for a count of failures per account, most first.
Sources
The Microsoft reference page for every cmdlet the script calls, plus the API and product pages behind each permission and caveat.
4625(F): An account failed to log on (fields, logon types and failure codes)Get-Date (Microsoft.PowerShell.Utility)Get-WinEvent (Microsoft.PowerShell.Diagnostics)Write-Output (Microsoft.PowerShell.Utility)Group-Object (Microsoft.PowerShell.Utility)Sort-Object (Microsoft.PowerShell.Utility)Select-Object (Microsoft.PowerShell.Utility)