Published on

How to Find Disabled User Accounts in Active Directory (PowerShell Script)

Disabled accounts pile up in every Active Directory environment — former employees, contractors whose engagements ended, service accounts nobody decommissioned properly. Knowing exactly which accounts are disabled (and which ones should be, but aren't) is one of the most common tasks in an AD security review or a routine access audit.

Here's the fastest way to get that list.

The quick answer

If you have the RSAT Active Directory PowerShell module installed, this one-liner lists every disabled user account in your domain:

Get-ADUser -Filter {Enabled -eq $false} | Select-Object Name, SamAccountName, DistinguishedName

That's enough for a quick check. For anything you'll actually act on — an audit, a cleanup project, evidence for a compliance review — you'll want more context per account: when it was disabled, when it last logged on, and where it lives in the OU structure.

A more useful reporting script

Import-Module ActiveDirectory

Get-ADUser -Filter { Enabled -eq $false } -Properties LastLogonDate, whenCreated, DistinguishedName |
    Select-Object Name,
                  SamAccountName,
                  @{Name = 'OU'; Expression = { ($_.DistinguishedName -split ',', 2)[1] } },
                  LastLogonDate,
                  whenCreated |
    Sort-Object LastLogonDate |
    Export-Csv -Path 'C:\Reports\DisabledUsers.csv' -NoTypeInformation

This pulls every disabled user, works out which OU it lives in, sorts by last logon (so the longest-dormant accounts float to the top), and drops the result in a CSV you can hand off or drop into a spreadsheet.

WARNING

LastLogonDate is replicated between domain controllers and can lag by up to ~14 days by default (msDS-LogonTimeSyncInterval). Don't treat it as precise to the day — treat it as "roughly this long ago," and if you need exact timing, query LastLogon (not replicated) across every DC and take the max.

Checking if any disabled accounts are still in privileged groups

Disabled doesn't always mean harmless — a disabled account that's still a member of Domain Admins is worth flagging separately, since group membership and account history both matter during a security review:

$privilegedGroups = 'Domain Admins', 'Enterprise Admins', 'Schema Admins'

foreach ($group in $privilegedGroups) {
    Get-ADGroupMember -Identity $group -Recursive |
        Get-ADUser -Properties Enabled |
        Where-Object { $_.Enabled -eq $false } |
        Select-Object Name, SamAccountName, @{Name = 'Group'; Expression = { $group } }
}

The GUI method (no PowerShell)

If RSAT's PowerShell module isn't available but the Active Directory Users and Computers (ADUC) snap-in is, you can get the same list without a script:

  1. Right-click your domain (or an OU) → NewQuery.
  2. Click Find and switch to Common Queries.
  3. Check Disabled accounts and click OK.

That gives you a filtered view you can save for reuse, though it lacks the CSV export, OU breakdown, and sorting the script above gives you for free.

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.

  • Disabled account reports Every disabled account, with the OU it lives in, when it was last used, and how long it has been sitting there.
  • Stale and inactive account detection Enabled accounts that nobody has logged into for 30, 60, or 90+ days — the ones that are still a live attack surface.
  • Privileged group auditing Who is in Domain Admins, Enterprise Admins, and Schema Admins — including nested membership and accounts that should not be there.
  • Scheduled reports by email Daily, weekly, or monthly runs delivered to the right inbox automatically. No Task Scheduler job for you to babysit.

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-DisabledADUsers.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.