Domain Admin privileges are not required to troubleshoot account lockouts, you actually on require read access to the Event Log which can we completed with Event Log Reader, giving more access violates least privilege principles and creates security risks.
The solution: use Event Log Readers group membership to query Event ID 4740 on the PDC Emulator.
Why Query the PDC Emulator
According to Microsoft documentation, account lockout is processed on the PDC emulator. When authentication failures occur, the domain controller closest to the user redirects the authentication request to the DC with the PDC emulator FSMO role, which is responsible for processing account locks.
The PDC emulator role retains specific functions including: password changes are replicated preferentially to the PDC emulator, authentication failures are forwarded to the PDC emulator before bad password failure messages are reported, and account lockout is processed on the PDC emulator.
This makes the PDC Emulator the authoritative source for Event ID 4740 (account lockout) events.
Required Permissions
Members of the Event Log Readers group can read event logs from domain controllers without administrative privileges. This built-in group provides sufficient access to query security logs for lockout events.
You will also need to add a security key to the registry of the Domain Controller - in this case the PDC, however we will cover the option to add to all Domain Controllers if required, the local setting is a registry key below:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\Security\Value Name:CustomSD
Value Type: REG_SZ
Value Data: O:BAG:SYD:(A;;CCLCSDRCWDWO;;;SY)(A;;CCLC;;;BA)(A;;CC;;;ER)(A;;CC;;;S-1-5-80-818380073-2995186456-1411405591-3990468014-3617507088)(A;;0x1;;;S-1-5-80-818380073-2995186456-1411405591-3990468014-3617507088)
If you wish to deploy this to all domain controllers via GPO you can use the "Default Domain Controllers" policy with the instructions below:
- Open Group Policy Management Console
- Edit Default Domain Controllers Policy (or create new GPO)
- Navigate to: Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options
- Configure: "Event log: Security log SDDL"
- Add the SDDL string including Event Log Readers from above!
Technical Implementation
Core Function Parameters
function Get-AccountLogonPAM {
param(
[Parameter(Mandatory=$true)]
[string]$samAccountName,
[Parameter(Mandatory=$false)]
[ValidateRange(1, 365)]
[int]$DaysBack = 7
)
PDC Detection
Automatically identify the PDC Emulator:
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$PDC = $domain.PdcRoleOwner.Name
Server-Side XML Filtering
Critical for performance with large event logs (30+ million entries):
$StartDate = (Get-Date).AddDays(-$DaysBack)
$StartDateUTC = $StartDate.ToUniversalTime()
$StartDateString = $StartDateUTC.ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
$XMLFilter = @"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4740) and TimeCreated[@SystemTime>='$StartDateString']]]
and
*[EventData[Data[@Name='TargetUserName'] and (text()='$samAccountName')]]
</Select>
</Query>
</QueryList>
"@
$events = Get-WinEvent -ComputerName $PDC -FilterXml $XMLFilter
Event Data Extraction
Parse Event 4740 structured data from the PDC:
$eventXML = [xml]$event.ToXml()
$eventData = @{}
foreach ($data in $eventXML.Event.EventData.Data) {
$eventData[$data.Name] = $data.'#text'
}
$lockoutSource = $eventData['CallerComputer'] ??
$eventData['WorkstationName'] ??
"Unknown"
Now lets move on to the script for this that can be manually run or installed as a module for all users of that server.
Script : LockoutAccountTools.ps1
function Get-AccountLogonPAM {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)]
[ValidateNotNullOrEmpty()]
[string]$samAccountName,
[Parameter(Mandatory=$false, Position=1)]
[ValidateRange(1, 365)]
[int]$DaysBack = 7
)
try {
# Calculate date range
$StartDate = (Get-Date).AddDays(-$DaysBack)
$StartDateUTC = $StartDate.ToUniversalTime()
$StartDateString = $StartDateUTC.ToString('yyyy-MM-ddTHH:mm:ss.fffZ')
# Get PDC Emulator
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$PDC = $domain.PdcRoleOwner.Name
if (-not $PDC) {
throw "Unable to identify the Primary Domain Controller"
}
Write-Host "PDC: $PDC" -ForegroundColor Green
Write-Host "Searching: $samAccountName (last $DaysBack days)" -ForegroundColor Yellow
# XML filter for server-side filtering
$XMLFilter = @"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[(EventID=4740) and TimeCreated[@SystemTime>='$StartDateString']]]
and
*[EventData[Data[@Name='TargetUserName'] and (text()='$samAccountName')]]
</Select>
</Query>
</QueryList>
"@
# Query events
try {
$events = Get-WinEvent -ComputerName $PDC -FilterXml $XMLFilter -ErrorAction Stop
}
catch {
# Fallback to hashtable filter
$FilterHashtable = @{
LogName = 'Security'
ID = 4740
StartTime = $StartDate
}
$events = Get-WinEvent -ComputerName $PDC -FilterHashtable $FilterHashtable -ErrorAction Stop |
Where-Object {
$_.Message -like "*$samAccountName*" -or
($_.Properties[0].Value -eq $samAccountName)
}
}
if ($events) {
Write-Host "Found $($events.Count) lockout event(s)" -ForegroundColor Green
foreach ($event in $events) {
# Parse event data
$eventXML = [xml]$event.ToXml()
$eventData = @{}
foreach ($data in $eventXML.Event.EventData.Data) {
$eventData[$data.Name] = $data.'#text'
}
# Extract lockout source
$lockoutSource = if ($eventData['CallerComputer']) {
$eventData['CallerComputer']
} elseif ($eventData['WorkstationName']) {
$eventData['WorkstationName']
} else {
if ($event.Message -match 'Caller Computer Name:\s+(\S+)') {
$Matches[1]
} else {
"Unknown"
}
}
# Output results
[PSCustomObject]@{
'DateTime' = $event.TimeCreated
'Username' = "$($eventData['TargetDomainName'])\$($eventData['TargetUserName'])"
'LockoutComputer' = $lockoutSource
'DC' = $event.MachineName
'RecordID' = $event.RecordId
}
}
}
else {
Write-Host "No lockout events found for $samAccountName in last $DaysBack day(s)" -ForegroundColor Yellow
}
}
catch {
if ($_.Exception.Message -like "*Access is denied*") {
Write-Error "Access Denied: Ensure membership in 'Event Log Readers' group on $PDC"
}
elseif ($_.Exception.Message -like "*RPC server is unavailable*") {
Write-Error "Cannot connect to $PDC. Check connectivity and firewall rules."
}
else {
Write-Error "Error: $_"
}
}
}
Usage Examples
# Last 24 hours (fastest)
Get-AccountLogonPAM -samAccountName "lockout.larry" -DaysBack 1
# Default 7 days
Get-AccountLogonPAM -samAccountName "lockout.larry"
# Extended search
Get-AccountLogonPAM -samAccountName "lockout.larry" -DaysBack 30
Module Installation
Save as AccountTools.psm1 in:
- System-wide:
C:\Program Files\WindowsPowerShell\Modules\AccountTools\ - Current user:
$HOME\Documents\WindowsPowerShell\Modules\AccountTools\
Key Points
- Event Log Readers group membership is sufficient - no admin rights needed
- PDC Emulator is authoritative for lockout events (Event ID 4740)
- XML filtering at server level prevents downloading entire event logs
- DaysBack parameter essential for performance with large logs
- Script handles 30+ million event log entries efficiently