The following script needs to be run on a DC with Domain Admin priveleges . This will send a detailed report of repeated account lockouts
<#
.SYNOPSIS
Generates a report of currently locked user accounts in Active Directory, along with the computers that caused the lockouts.
.DESCRIPTION
This script queries locked out users, parses relevant security events, and outputs clean HTML/CSV reports.
.PARAMETER TempPath
Path for local temp storage.
.PARAMETER SharedPath
Path for shared report storage.
.PARAMETER LookbackMilliseconds
Time window (in ms) for event lookup.
#>
param (
[string]$TempPath = "C:\temp",
[string]$SharedPath = "\\Sharedpath\AccountLockout",
[int]$LookbackMilliseconds = 4233600000 # 7 weeks
)
# --- Function Definitions ---
function Remove-ExistingFile {
param ([string[]]$Files)
foreach ($f in $Files) {
if (Test-Path $f) {
Remove-Item $f -ErrorAction SilentlyContinue
}
}
}
function Write-ProgressInfo {
param($msg, $color="Yellow")
Write-Host $msg -ForegroundColor $color
}
function Send-ReportEmail {
param($from, $to, $subject, $body, $attachments, $smtp)
Try {
Send-MailMessage -From $from -To $to -Subject $subject -Body $body -Attachments $attachments -SmtpServer $smtp
Write-ProgressInfo "Email sent to $to" Green
} Catch {
Write-ProgressInfo "Failed to send email: $_" Red
}
}
# --- Variables and File Paths ---
$StartDate = Get-Date
$filename1 = "$TempPath\List_of_locked_users.txt"
$filename2 = "$TempPath\Computers_Causing_locked_users.csv"
$filename3 = "$SharedPath\Computers_Causing_locked_users.csv"
$filename4 = "$TempPath\sorted.csv"
$filename5 = "$TempPath\Computers_Causing_Lockouts.html"
$filename6 = "$SharedPath\Computers_Causing_Lockouts.html"
$filename7 = "$SharedPath\List_of_locked_users.txt"
Remove-ExistingFile -Files @($filename1, $filename2, $filename4)
# --- Discover PDC and Locked Users ---
$PDC = Get-ADDomainController -Discover -Service PrimaryDC
$LockedUsers = Search-ADAccount -LockedOut | Select-Object -ExpandProperty Name
$LockedUsers | Out-File $filename1
$UserCount = $LockedUsers.Count
Write-ProgressInfo "Current List of locked out users:" Red
$LockedUsers
Write-ProgressInfo "There are $UserCount accounts locked out." Red
Write-ProgressInfo "...Now checking which computers caused the lockouts....." Red
# --- Query Event Log per User & Export ---
$pass = 1
foreach ($User in $LockedUsers) {
Write-ProgressInfo "Checking account: $User ($pass of $UserCount)" Blue
Try {
Get-WinEvent -ComputerName $PDC.Name -Logname Security `
-FilterXPath "*[System[EventID=4740 and TimeCreated[timediff(@SystemTime) <= $LookbackMilliseconds]] and EventData[Data[@Name='TargetUserName']='$User']]" `
-ErrorAction Stop |
Select-Object TimeCreated,@{Name='User Name';Expression={ $_.Properties[0].Value }},@{Name='Source Host';Expression={ $_.Properties[1].Value }} |
Export-Csv -Path $filename2 -Append -NoTypeInformation -Force
} Catch {
Write-ProgressInfo "Error processing user $User: $_" Red
}
$pass++
}
$EndDate = Get-Date
$duration = New-TimeSpan -Start $StartDate -End $EndDate
$duration2 = [math]::Round($duration.TotalMinutes,2)
# --- Clean HTML Output (CSS) ---
$htmlformat = @'
<title>Computers Causing Lockouts</title>
<style>
body { background: #f3f4f6; color: #22223b; font-family: "Segoe UI", Arial, sans-serif; font-size: 16px; margin: 20px;}
h1 { color: #2a394f; border-bottom: 2px solid #c9d6e3; padding-bottom: 8px; }
table { border-collapse: collapse; width: 100%; background: #fff; margin-top: 20px;}
th, td { border: 1px solid #e1e5ee; padding: 10px; text-align: left;}
th { background: #eaf0fa;}
tr:nth-child(even) td { background: #f8f8fc;}
</style>
'@
$bodyformat = '<h1>Computers Causing Lockouts: Sorted by User Name</h1>'
# --- Sort and Export Reports ---
Try {
if (Test-Path $filename2) {
Import-Csv -Path $filename2 | Sort-Object "User Name" | Export-Csv -Path $filename4 -NoTypeInformation
Import-Csv -Path $filename4 | ConvertTo-Html -Head $htmlformat -Body $bodyformat | Out-File $filename5 -Force
} else {
Write-ProgressInfo "No event data exported, $filename2 missing." Yellow
}
} Catch {
Write-ProgressInfo "Error generating HTML report: $_" Red
}
# --- Clean-up Old Reports on File Share ---
Remove-ExistingFile -Files @($filename3, $filename6, $filename7)
# --- Copy Results to Shared Path ---
Copy-Item $filename2 $filename3 -Force
Copy-Item $filename5 $filename6 -Force
Copy-Item $filename1 $filename7 -Force
# --- Compose and Send Email Summary ---
$From = "shankar.karanth@karanth.ovh"
$To = "DomainAdmins@karanth.ovh"
$Sub = "User lockout report"
$Body = @"
There are $UserCount accounts locked out at this time.
This report was generated via a scheduled task on $env:COMPUTERNAME
It started at $StartDate and took $duration2 minutes to run.
Please refer to the following attachments:
1. A list of locked out accounts
2. CSV and HTML reports showing which computers were recorded as causing account lockouts.
Lockout Reports can be accessed from:
$filename3
$filename6
$filename7
NOTE:
The Computers_Causing_locked_users report only shows users who were locked out if they were noted in the Event Viewer during the configured time frame --AND-- only lists computers if they are noted in the Event Viewer of the PDC.
The CSV and HTML reports are therefore, not necessarily all inclusive.
"@
$SMTP = "mail@karanth.ovh"
Send-ReportEmail -from $From -to $To -subject $Sub -body $Body -attachments @($filename1, $filename2, $filename5) -smtp $SMTP
Write-ProgressInfo "Report generation complete. Duration: $duration2 minutes." Green

