User Lockout Finder
Find the source of Active Directory account lockouts
Table of Contents
Quickly identify which computer is causing Active Directory account lockouts by searching all domain controllers for lockout events.
Overview
This script searches all domain controllers in your domain for Event ID 4740 (Account Lockout) events, showing you exactly which computer triggered the lockout, when it occurred, and for which user. This is essential for troubleshooting persistent lockout issues caused by saved credentials, mapped drives, or mobile devices.
Use Case: When users report repeated account lockouts and you need to find the source computer/device causing the lockouts.
Platform: Windows Server 2012 R2+, Windows 10/11 (with RSAT) Requirements: Domain Admin or equivalent read access to Security logs on all DCs Execution Time: 30-60 seconds (depends on number of DCs)
The Script
1<#
2.SYNOPSIS
3 Find the source of Active Directory account lockouts
4
5.DESCRIPTION
6 Searches all domain controllers for Event ID 4740 (Account Lockout) events
7 and displays which computer/device caused the lockout. This helps identify
8 the source of persistent lockout issues.
9
10.PARAMETER Username
11 Specific username to search for. If not provided, shows all recent lockouts.
12
13.PARAMETER Hours
14 Number of hours back to search. Default is 24 hours.
15
16.PARAMETER MaxEvents
17 Maximum number of events to retrieve per DC. Default is 10.
18
19.EXAMPLE
20 .\Find-UserLockout.ps1
21 Shows all account lockouts from the past 24 hours
22
23.EXAMPLE
24 .\Find-UserLockout.ps1 -Username jdoe
25 Shows lockout events for specific user "jdoe"
26
27.EXAMPLE
28 .\Find-UserLockout.ps1 -Username jdoe -Hours 48
29 Shows lockout events for jdoe from the past 48 hours
30
31.EXAMPLE
32 .\Find-UserLockout.ps1 -Hours 72 -MaxEvents 50
33 Shows up to 50 lockout events per DC from the past 72 hours
34
35.NOTES
36 Author: glyph.sh
37 Requires: ActiveDirectory module, read access to DC Security logs
38 Reference: https://glyph.sh/posts/active-directory-troubleshooting/
39#>
40
41[CmdletBinding()]
42param(
43 [Parameter(Mandatory=$false)]
44 [string]$Username,
45
46 [Parameter(Mandatory=$false)]
47 [int]$Hours = 24,
48
49 [Parameter(Mandatory=$false)]
50 [int]$MaxEvents = 10
51)
52
53# Import ActiveDirectory module
54try {
55 Import-Module ActiveDirectory -ErrorAction Stop
56}
57catch {
58 Write-Host "ERROR: ActiveDirectory module not found!" -ForegroundColor Red
59 Write-Host "Install RSAT tools: Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0" -ForegroundColor Yellow
60 exit 1
61}
62
63Write-Host "========================================" -ForegroundColor Cyan
64Write-Host " Active Directory Lockout Finder" -ForegroundColor Cyan
65Write-Host "========================================" -ForegroundColor Cyan
66Write-Host ""
67
68# Calculate start time
69$StartTime = (Get-Date).AddHours(-$Hours)
70Write-Host "Search Parameters:" -ForegroundColor Yellow
71Write-Host " Time Range: Past $Hours hours (since $StartTime)" -ForegroundColor White
72if ($Username) {
73 Write-Host " Username: $Username" -ForegroundColor White
74} else {
75 Write-Host " Username: All users" -ForegroundColor White
76}
77Write-Host " Max Events per DC: $MaxEvents" -ForegroundColor White
78Write-Host ""
79
80# Get all domain controllers
81Write-Host "[1/2] Discovering domain controllers..." -ForegroundColor Yellow
82try {
83 $DomainControllers = Get-ADDomainController -Filter * | Sort-Object Name
84 Write-Host " Found $($DomainControllers.Count) domain controller(s)" -ForegroundColor Green
85 foreach ($DC in $DomainControllers) {
86 Write-Host " - $($DC.HostName)" -ForegroundColor Gray
87 }
88}
89catch {
90 Write-Host " ERROR: Failed to retrieve domain controllers" -ForegroundColor Red
91 Write-Host " $($_.Exception.Message)" -ForegroundColor Red
92 exit 1
93}
94
95Write-Host ""
96
97# Search for lockout events
98Write-Host "[2/2] Searching for account lockout events..." -ForegroundColor Yellow
99Write-Host ""
100
101$AllLockouts = @()
102$TotalEvents = 0
103
104foreach ($DC in $DomainControllers) {
105 Write-Host " Checking $($DC.HostName)..." -NoNewline
106
107 try {
108 # Build filter hashtable
109 $FilterHash = @{
110 LogName = 'Security'
111 ID = 4740
112 StartTime = $StartTime
113 }
114
115 # Query for lockout events
116 $Events = Get-WinEvent -ComputerName $DC.HostName -FilterHashtable $FilterHash -MaxEvents $MaxEvents -ErrorAction Stop
117
118 if ($Events) {
119 foreach ($Event in $Events) {
120 # Extract event details
121 $EventXML = [xml]$Event.ToXml()
122 $LockedUser = $EventXML.Event.EventData.Data[0].'#text'
123 $CallerComputer = $EventXML.Event.EventData.Data[1].'#text'
124
125 # Filter by username if specified
126 if ($Username -and $LockedUser -ne $Username) {
127 continue
128 }
129
130 # Create lockout object
131 $LockoutInfo = [PSCustomObject]@{
132 TimeCreated = $Event.TimeCreated
133 User = $LockedUser
134 SourceComputer = $CallerComputer
135 DomainController = $DC.HostName
136 EventID = $Event.Id
137 }
138
139 $AllLockouts += $LockoutInfo
140 $TotalEvents++
141 }
142
143 Write-Host " Found $($Events.Count) event(s)" -ForegroundColor Green
144 }
145 else {
146 Write-Host " No events" -ForegroundColor Gray
147 }
148 }
149 catch [System.Exception] {
150 if ($_.Exception.Message -like "*No events were found*") {
151 Write-Host " No events" -ForegroundColor Gray
152 }
153 else {
154 Write-Host " ERROR" -ForegroundColor Red
155 Write-Host " $($_.Exception.Message)" -ForegroundColor Red
156 }
157 }
158}
159
160Write-Host ""
161Write-Host "========================================" -ForegroundColor Cyan
162Write-Host " Search Results" -ForegroundColor Cyan
163Write-Host "========================================" -ForegroundColor Cyan
164Write-Host ""
165
166if ($AllLockouts.Count -gt 0) {
167 Write-Host "Found $TotalEvents lockout event(s):" -ForegroundColor Green
168 Write-Host ""
169
170 # Display results sorted by time (most recent first)
171 $AllLockouts | Sort-Object TimeCreated -Descending | Format-Table -AutoSize @{
172 Label = 'Time'
173 Expression = {$_.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')}
174 },
175 @{
176 Label = 'User'
177 Expression = {$_.User}
178 },
179 @{
180 Label = 'Source Computer'
181 Expression = {$_.SourceComputer}
182 },
183 @{
184 Label = 'Detected On DC'
185 Expression = {$_.DomainController}
186 }
187
188 Write-Host ""
189 Write-Host "Next Steps:" -ForegroundColor Yellow
190 Write-Host "1. Investigate the Source Computer(s) listed above" -ForegroundColor White
191 Write-Host "2. Check for saved credentials: Control Panel > Credential Manager" -ForegroundColor White
192 Write-Host "3. Look for mapped network drives with old credentials" -ForegroundColor White
193 Write-Host "4. Check mobile devices/apps with saved passwords" -ForegroundColor White
194 Write-Host "5. Review scheduled tasks running as the locked user" -ForegroundColor White
195 Write-Host "6. Check services running with the user's credentials" -ForegroundColor White
196 Write-Host ""
197
198 # Show unique source computers
199 $UniqueComputers = $AllLockouts | Select-Object -ExpandProperty SourceComputer -Unique
200 if ($UniqueComputers.Count -gt 0) {
201 Write-Host "Unique Source Computers ($($UniqueComputers.Count)):" -ForegroundColor Yellow
202 foreach ($Computer in $UniqueComputers) {
203 Write-Host " - $Computer" -ForegroundColor White
204 }
205 Write-Host ""
206 }
207}
208else {
209 Write-Host "No lockout events found matching your criteria." -ForegroundColor Yellow
210 Write-Host ""
211 Write-Host "Suggestions:" -ForegroundColor Yellow
212 Write-Host " - Increase the time range with -Hours parameter" -ForegroundColor White
213 Write-Host " - Check if username is spelled correctly" -ForegroundColor White
214 Write-Host " - Verify you have permissions to read Security logs on DCs" -ForegroundColor White
215 Write-Host ""
216}
217
218Write-Host "========================================" -ForegroundColor Cyan
219Write-Host " Lockout Investigation Complete" -ForegroundColor Cyan
220Write-Host "========================================" -ForegroundColor Cyan
221Write-Host ""Usage
Basic Usage
Find all account lockouts in the past 24 hours:
1.\Find-UserLockout.ps1Search for Specific User
Find lockouts for a specific user:
1.\Find-UserLockout.ps1 -Username jdoeExtended Time Range
Search the past 48 hours:
1.\Find-UserLockout.ps1 -Username jdoe -Hours 48Increase Event Limit
Get more events per domain controller:
1.\Find-UserLockout.ps1 -Hours 72 -MaxEvents 50What It Does
- Discovers Domain Controllers: Finds all DCs in your Active Directory domain
- Queries Security Logs: Searches each DC’s Security event log for Event ID 4740
- Extracts Details: Pulls out:
- Username that was locked out
- Source computer that caused the lockout
- Timestamp of the lockout
- Which DC detected the event
- Displays Results: Shows all findings in an easy-to-read table format
- Lists Unique Sources: Summarizes which computers are causing lockouts
Understanding Event ID 4740
Event ID 4740 is logged on the domain controller when a user account is locked out. The event contains:
- User Account Name: The account that was locked
- Caller Computer Name: The computer/device that triggered the lockout
- Timestamp: When the lockout occurred
Common Lockout Causes
1. Saved Credentials
Location: Control Panel → Credential Manager
1# List saved credentials
2cmdkey /list
3
4# Remove specific credential
5cmdkey /delete:targetname2. Mapped Network Drives
Persistent mapped drives with old passwords:
1# List mapped drives
2net use
3
4# Remove specific mapping
5net use Z: /delete
6
7# Remove all mappings
8net use * /delete3. Mobile Devices
- Email apps with old Exchange passwords
- VPN clients with cached credentials
- Mobile device management (MDM) apps
4. Scheduled Tasks
Tasks running with old credentials:
1# List scheduled tasks for user
2Get-ScheduledTask | Where-Object {$_.Principal.UserId -like "*username*"}5. Services
Windows services running as the user:
1# Find services running as specific user
2Get-WmiObject Win32_Service | Where-Object {$_.StartName -like "*username*"} | Select-Object Name, StartName, State6. IIS Application Pools
Application pools using the account:
1Import-Module WebAdministration
2Get-ChildItem IIS:\AppPools | Where-Object {$_.processModel.userName -like "*username*"}Investigating Source Computers
Once you identify the source computer, investigate these areas:
Remote PowerShell Investigation
1# Check saved credentials remotely
2Invoke-Command -ComputerName SOURCEPC -ScriptBlock {
3 # List saved credentials
4 cmdkey /list
5
6 # Check mapped drives
7 net use
8
9 # Find services running as user
10 Get-WmiObject Win32_Service | Where-Object {$_.StartName -like "*domain\username*"}
11
12 # Check scheduled tasks
13 Get-ScheduledTask | Where-Object {$_.Principal.UserId -like "*username*"}
14}Check for Old Sessions
1# See who's logged into the computer
2qwinsta /server:SOURCEPC
3
4# Check for disconnected sessions
5query user /server:SOURCEPCUnlocking the Account
After identifying the source:
1# Unlock the account
2Unlock-ADAccount -Identity username
3
4# Verify it's unlocked
5Get-ADUser -Identity username -Properties LockedOut | Select-Object Name, LockedOutPrevention Strategies
1. Account Lockout Policy
Review your lockout policy:
1Get-ADDefaultDomainPasswordPolicy | Select-Object LockoutThreshold, LockoutDuration, LockoutObservationWindowRecommended Settings:
- Lockout Threshold: 5-10 attempts
- Lockout Duration: 15-30 minutes
- Observation Window: 15-30 minutes
2. User Education
Educate users about:
- Changing passwords on all devices
- Updating Credential Manager after password changes
- Removing old mapped drives
- Updating mobile device passwords
3. Password Expiration Notifications
Notify users before passwords expire:
1# Find users with passwords expiring soon
2Get-ADUser -Filter {Enabled -eq $true -and PasswordNeverExpires -eq $false} -Properties PasswordLastSet, PasswordNeverExpires |
3 Where-Object {$_.PasswordLastSet -lt (Get-Date).AddDays(-75)} |
4 Select-Object Name, PasswordLastSetTroubleshooting
Permission Denied
If you get “Access Denied” errors:
1# Verify you're in Domain Admins or have Event Log Readers permissions
2Get-ADGroupMember -Identity "Event Log Readers"
3
4# Add user to Event Log Readers group
5Add-ADGroupMember -Identity "Event Log Readers" -Members usernameNo Events Found
If no events are returned:
- Verify lockout occurred recently: Expand time range with
-Hours - Check event log settings: Ensure Security log isn’t full
- Verify Event ID 4740 auditing: Should be enabled by default
- Try another DC: Lockouts are logged on the PDC Emulator first
ActiveDirectory Module Not Found
Install RSAT tools:
1# Windows 10/11
2Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
3
4# Windows Server
5Install-WindowsFeature -Name RSAT-AD-PowerShellAdvanced: Continuous Monitoring
Create a scheduled task to monitor for lockouts:
1# Create monitoring script
2$MonitorScript = @'
3$Events = Get-WinEvent -FilterHashtable @{LogName='Security';ID=4740;StartTime=(Get-Date).AddMinutes(-5)} -ErrorAction SilentlyContinue
4if ($Events) {
5 $Events | ForEach-Object {
6 $XML = [xml]$_.ToXml()
7 Send-MailMessage -To "admin@domain.com" `
8 -From "lockout-alert@domain.com" `
9 -Subject "Account Lockout Alert" `
10 -Body "User: $($XML.Event.EventData.Data[0].'#text')`nSource: $($XML.Event.EventData.Data[1].'#text')" `
11 -SmtpServer "smtp.domain.com"
12 }
13}
14'@
15
16# Save script
17$MonitorScript | Out-File C:\Scripts\Monitor-Lockouts.ps1
18
19# Create scheduled task (run every 5 minutes)
20$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\Monitor-Lockouts.ps1"
21$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 5)
22Register-ScheduledTask -TaskName "Monitor-AccountLockouts" -Action $Action -Trigger $Trigger -RunLevel HighestRelated Account Information
Check Account Status
1Get-ADUser -Identity username -Properties LockedOut,LastBadPasswordAttempt,BadPwdCount,PasswordLastSetView Account Lockout History
1# Get all lockout events for a user in the past week
2Get-WinEvent -FilterHashtable @{
3 LogName='Security'
4 ID=4740
5 StartTime=(Get-Date).AddDays(-7)
6} | Where-Object {$_.Properties[0].Value -eq "username"}See Also
Download
1# Download the script
2Invoke-WebRequest -Uri "https://glyph.sh/scripts/Find-UserLockout.ps1" -OutFile "Find-UserLockout.ps1"
3
4# Run it for specific user
5.\Find-UserLockout.ps1 -Username jdoe