Published on

How to Find Locked-Out User Accounts in Active Directory (PowerShell Script)

A locked-out account is usually the first sign something is wrong — a stale saved credential, a brute-force attempt, or just someone who fat-fingered their password one too many times. When the help desk gets the call, "who's locked out right now" is the first question, and it's faster to answer with a script than by hunting through ADUC one OU at a time.

The quick answer

Search-ADAccount is the cmdlet built for this — LockedOut is a constructed attribute, so Get-ADUser -Filter {LockedOut -eq $true} will reject it outright with "Searching on extended attribute 'LockedOut' is not supported":

Search-ADAccount -LockedOut -UsersOnly

That gets you names fast. For a report you'd actually act on, you want bad-password counts and timestamps too — which means piping the result back through Get-ADUser.

A more useful reporting script

#requires -Modules ActiveDirectory
<#
.SYNOPSIS
    Lists currently locked-out user accounts in Active Directory.

.DESCRIPTION
    Uses Search-ADAccount to find all locked-out user accounts (LockedOut is
    a constructed attribute that Get-ADUser's -Filter can't query directly),
    then looks up each one's bad-password count and last bad-password
    timestamp, optionally scoped to an OU, and optionally exporting the
    results to CSV. Uses the current logged-on user's credentials, like any
    other ActiveDirectory module cmdlet — run it from a domain-joined machine
    with RSAT installed.

.PARAMETER SearchBase
    Distinguished name of the OU to search (e.g. "OU=Sales,DC=contoso,DC=com").
    If omitted, searches the entire domain.

.PARAMETER OutputCsv
    Path to a CSV file to export the results to. If omitted, results are only
    written to the console.

.EXAMPLE
    .\Find-LockedOutADUsers.ps1

    Lists all currently locked-out users in the domain.

.EXAMPLE
    .\Find-LockedOutADUsers.ps1 -SearchBase "OU=Sales,DC=contoso,DC=com"

    Lists locked-out users in the Sales OU only.

.EXAMPLE
    .\Find-LockedOutADUsers.ps1 -OutputCsv .\locked-out-users.csv

    Lists all locked-out users and exports the results to locked-out-users.csv.
#>

[CmdletBinding()]
param(
    [string]$SearchBase,

    [string]$OutputCsv
)

Import-Module ActiveDirectory -ErrorAction Stop

$searchParams = @{ LockedOut = $true; UsersOnly = $true }
if ($SearchBase) {
    $searchParams['SearchBase'] = $SearchBase
}

$lockedOutUsers = Search-ADAccount @searchParams |
    Get-ADUser -Properties BadPwdCount, badPasswordTime |
    Select-Object Name, SamAccountName, DistinguishedName, BadPwdCount,
        @{Name = 'LastBadPasswordAttempt'; Expression = { [DateTime]::FromFileTime($_.badPasswordTime) } } |
    Sort-Object Name

if (-not $lockedOutUsers) {
    Write-Host "No locked-out user accounts found." -ForegroundColor Yellow
    return
}

Write-Host "Found $(@($lockedOutUsers).Count) locked-out user account(s)." -ForegroundColor Cyan
$lockedOutUsers | Format-Table Name, SamAccountName, BadPwdCount, LastBadPasswordAttempt -AutoSize

if ($OutputCsv) {
    $lockedOutUsers | Export-Csv -Path $OutputCsv -NoTypeInformation
    Write-Host "Exported results to $OutputCsv" -ForegroundColor Green
}

NOTE

Search-ADAccount doesn't accept -Properties — that's a common first mistake when adapting this script. Get the account list from Search-ADAccount, then pipe it through Get-ADUser -Properties ... for anything beyond the default fields.

The GUI method (no PowerShell)

In Active Directory Users and Computers, right-click your domain → FindCommon Queries → check Locked out accounts. It doesn't give you the bad-password count or last-attempt timestamp, but it's fine for a one-off check without opening a console.

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/Find-LockedOutADUsers.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.