- Published on
How to Find What Caused an Active Directory Account Lockout (PowerShell Script)
Finding a locked-out account is the easy part. The question that actually matters — "what triggered this, and is it going to happen again in fifteen minutes" — needs Event ID 4740 from the Security event log, and in a multi-DC environment you don't know in advance which DC saw it. That means checking all of them.
The quick answer
On a single DC:
Get-WinEvent -ComputerName <DC-name> -FilterHashtable @{LogName='Security'; Id=4740} |
Select-Object TimeCreated, @{N='User';E={$_.Properties[0].Value}}, @{N='Source';E={$_.Properties[1].Value}}
Properties[0] is the account that got locked out; Properties[1] is the computer the bad password attempts actually came from — that's the field you're really after.
A script that checks every DC and filters by user
#requires -Modules ActiveDirectory
<#
.SYNOPSIS
Finds the source (computer) that caused an Active Directory account lockout.
.DESCRIPTION
Searches the Security event log on every domain controller for Event ID
4740 (account lockout events), which records the computer the bad
password attempts actually came from — the PDC emulator itself is where
lockouts are ultimately processed, but 4740 on it names the origin host.
Uses the current logged-on user's credentials; you need permission to
read the Security event log on each DC (typically Domain Admins, or
delegated log-reading rights).
.PARAMETER SamAccountName
Only show lockout events for this user. If omitted, shows lockout events
for all users.
.PARAMETER Hours
How far back to search, in hours. Defaults to 24.
.PARAMETER OutputCsv
Path to a CSV file to export the results to. If omitted, results are only
written to the console.
.EXAMPLE
.\Get-ADAccountLockoutSource.ps1 -SamAccountName jdoe
Shows lockout events for jdoe in the last 24 hours, including which
computer triggered each one.
.EXAMPLE
.\Get-ADAccountLockoutSource.ps1 -Hours 72 -OutputCsv .\lockouts.csv
Shows lockout events for all users in the last 3 days and exports to CSV.
#>
[CmdletBinding()]
param(
[string]$SamAccountName,
[int]$Hours = 24,
[string]$OutputCsv
)
Import-Module ActiveDirectory -ErrorAction Stop
$domainControllers = Get-ADDomainController -Filter *
$startTime = (Get-Date).AddHours(-$Hours)
$results = New-Object System.Collections.Generic.List[object]
foreach ($dc in $domainControllers) {
Write-Host "Checking $($dc.HostName) for lockout events since $startTime ..." -ForegroundColor Cyan
$filterHashtable = @{
LogName = 'Security'
Id = 4740
StartTime = $startTime
}
try {
$events = Get-WinEvent -ComputerName $dc.HostName -FilterHashtable $filterHashtable -ErrorAction Stop
} catch [Exception] {
if ($_.Exception -is [System.Diagnostics.Eventing.Reader.EventLogNotFoundException]) {
Write-Verbose "No lockout events found on $($dc.HostName) in the given window."
continue
}
Write-Warning "Could not query $($dc.HostName): $($_.Exception.Message)"
continue
}
foreach ($event in $events) {
$targetUser = $event.Properties[0].Value
if ($SamAccountName -and $targetUser -ne $SamAccountName) {
continue
}
$results.Add([PSCustomObject]@{
TimeCreated = $event.TimeCreated
LockedOutUser = $targetUser
SourceComputer = $event.Properties[1].Value
RecordedOnDC = $dc.HostName
})
}
}
if ($results.Count -eq 0) {
Write-Host "No lockout events found in the last $Hours hour(s)." -ForegroundColor Yellow
return
}
$sorted = $results | Sort-Object TimeCreated -Descending
Write-Host "Found $(@($sorted).Count) lockout event(s)." -ForegroundColor Cyan
$sorted | Format-Table TimeCreated, LockedOutUser, SourceComputer, RecordedOnDC -AutoSize
if ($OutputCsv) {
$sorted | Export-Csv -Path $OutputCsv -NoTypeInformation
Write-Host "Exported results to $OutputCsv" -ForegroundColor Green
}
Why an account keeps getting locked out repeatedly
If the same account locks out every few hours, the usual culprit is a saved credential somewhere still holding the old password — a mapped drive, a scheduled task, a service account, a phone's cached Wi-Fi or Exchange login — retrying on a loop. Once this script tells you the source computer, check Credential Manager, scheduled tasks, and any service running as that user on that machine.
IMPORTANT
Reading the Security log remotely needs "Manage auditing and security log" rights on the DC — by default that's Domain Admins. If you don't want to hand out full Domain Admin membership just for lockout troubleshooting, delegate that specific right via Group Policy instead.
Where the manual approach runs out of road
A one-off script is fine for a single check. It starts to hurt once you actually need to run this regularly:
- No scheduling. Cron/Task Scheduler can run the script, but now you own the scheduling, the credentials it runs as, and what happens when it silently fails.
- No history. A CSV export is a snapshot. Was this the same five accounts as last month, or a growing list? A single export can't tell you.
- No distribution. Getting the report to the right people (security, IT ops, compliance) on a schedule means building that plumbing yourself.
- Multi-domain/multi-forest pain. Run it once per domain, reconcile the results yourself, and hope naming/OU conventions are consistent across all of them.
- No alerting. If something changes unexpectedly between runs — an account re-enabled, a privileged group gaining a member — nothing tells you until you happen to run the script again.
None of that is a PowerShell problem. It's what turns a script into a product.
What SysFlint AD does instead
Our SysFlint AD runs the same kind of discovery shown above automatically, on a schedule, entirely inside your own network. No agents on domain controllers, no data leaving your environment. It's free, forever.
- Scheduled reports by email — Daily, weekly, or monthly runs delivered to the right inbox automatically. No Task Scheduler job for you to babysit.
- Multi-domain and forest coverage — Enumerate every domain in the forest in one pass and get one consolidated report instead of one per domain.
If you're currently doing this with a script on a scheduled task, here's the honest comparison between the two, or go straight to the download page.
Get this script, and the rest of them
The script above is part of awesome-it-scripts, SysFlint's free, open-source library of PowerShell scripts for Active Directory and other IT-admin tasks — no signup, no catch, MIT licensed. Grab this one directly from active-directory/Get-ADAccountLockoutSource.ps1, or browse the whole thing.
Find every disabled/locked-out/stale/privileged-account script we've published (plus the FAQ that goes with each one) in the repo's active-directory folder. Star it if it's useful — new scripts land there regularly.