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

Lang: powershell
  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:

Lang: powershell
1.\Find-UserLockout.ps1

Search for Specific User

Find lockouts for a specific user:

Lang: powershell
1.\Find-UserLockout.ps1 -Username jdoe

Extended Time Range

Search the past 48 hours:

Lang: powershell
1.\Find-UserLockout.ps1 -Username jdoe -Hours 48

Increase Event Limit

Get more events per domain controller:

Lang: powershell
1.\Find-UserLockout.ps1 -Hours 72 -MaxEvents 50

What It Does

  1. Discovers Domain Controllers: Finds all DCs in your Active Directory domain
  2. Queries Security Logs: Searches each DC’s Security event log for Event ID 4740
  3. Extracts Details: Pulls out:
    • Username that was locked out
    • Source computer that caused the lockout
    • Timestamp of the lockout
    • Which DC detected the event
  4. Displays Results: Shows all findings in an easy-to-read table format
  5. 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

Lang: powershell
1# List saved credentials
2cmdkey /list
3
4# Remove specific credential
5cmdkey /delete:targetname

2. Mapped Network Drives

Persistent mapped drives with old passwords:

Lang: cmd
1# List mapped drives
2net use
3
4# Remove specific mapping
5net use Z: /delete
6
7# Remove all mappings
8net use * /delete

3. 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:

Lang: powershell
1# List scheduled tasks for user
2Get-ScheduledTask | Where-Object {$_.Principal.UserId -like "*username*"}

5. Services

Windows services running as the user:

Lang: powershell
1# Find services running as specific user
2Get-WmiObject Win32_Service | Where-Object {$_.StartName -like "*username*"} | Select-Object Name, StartName, State

6. IIS Application Pools

Application pools using the account:

Lang: powershell
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

Lang: powershell
 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

Lang: powershell
1# See who's logged into the computer
2qwinsta /server:SOURCEPC
3
4# Check for disconnected sessions
5query user /server:SOURCEPC

Unlocking the Account

After identifying the source:

Lang: powershell
1# Unlock the account
2Unlock-ADAccount -Identity username
3
4# Verify it's unlocked
5Get-ADUser -Identity username -Properties LockedOut | Select-Object Name, LockedOut

Prevention Strategies

1. Account Lockout Policy

Review your lockout policy:

Lang: powershell
1Get-ADDefaultDomainPasswordPolicy | Select-Object LockoutThreshold, LockoutDuration, LockoutObservationWindow

Recommended 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:

Lang: powershell
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, PasswordLastSet

Troubleshooting

Permission Denied

If you get “Access Denied” errors:

Lang: powershell
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 username

No Events Found

If no events are returned:

  1. Verify lockout occurred recently: Expand time range with -Hours
  2. Check event log settings: Ensure Security log isn’t full
  3. Verify Event ID 4740 auditing: Should be enabled by default
  4. Try another DC: Lockouts are logged on the PDC Emulator first

ActiveDirectory Module Not Found

Install RSAT tools:

Lang: powershell
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-PowerShell

Advanced: Continuous Monitoring

Create a scheduled task to monitor for lockouts:

Lang: powershell
 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 Highest

Check Account Status

Lang: powershell
1Get-ADUser -Identity username -Properties LockedOut,LastBadPasswordAttempt,BadPwdCount,PasswordLastSet

View Account Lockout History

Lang: powershell
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

Lang: powershell
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