Useful scripts

Everyday Active Directory PowerShell commands (read-only)

Copy-and-paste PowerShell commands for the Active Directory questions that come up every week, from locked-out users to stale accounts and domain controller health. Nothing here changes anything.

Most Active Directory questions don’t need a fancy tool. Who’s locked out? Which accounts haven’t been used in months? Who’s actually in Domain Admins? A one-line PowerShell command answers each of these in seconds.

Every command on this page is read-only. They look things up and report back; none of them change, disable, unlock, or delete anything. That makes them safe to run while you learn what’s in your directory.

Before you start

You need the ActiveDirectory PowerShell module:

  • On a domain controller, it’s already installed.
  • On a Windows 10 or 11 admin workstation, install RSAT: Active Directory Domain Services and Lightweight Directory Services Tools from Settings > System > Optional features.

Open PowerShell as a user with rights to read the directory (a normal domain account can read most of it), then load the module:

Import-Module ActiveDirectory

Tip: to save any result to a spreadsheet, add | Export-Csv to the end of the command, like this:

Search-ADAccount -LockedOut -UsersOnly | Export-Csv -Path C:\Temp\locked-out.csv -NoTypeInformation

User accounts

Who’s locked out right now? Lists every user account that’s currently locked out, the first thing to check when someone calls saying they can’t log in.

Search-ADAccount -LockedOut -UsersOnly | Select-Object Name, SamAccountName, LastLogonDate

Which user accounts are disabled? Good for confirming offboarded employees were actually shut off. (The free onboarding and offboarding checklist covers everything else to shut off.)

Search-ADAccount -AccountDisabled -UsersOnly | Select-Object Name, SamAccountName

Which users haven’t logged in for 90 days? Your starting list for stale-account cleanup and a favorite auditor question.

Search-ADAccount -AccountInactive -TimeSpan 90.00:00:00 -UsersOnly | Select-Object Name, SamAccountName, LastLogonDate

About that “last logon” date: to reduce replication traffic, Active Directory only updates the replicated last-logon value every so often, so by design it can be 9 to 14 days behind. It’s reliable for finding accounts unused for months, but don’t use windows shorter than about 14 days, and never disable an account based on one report without checking with its owner.

Whose passwords are set to never expire? Often service accounts and forgotten test accounts. Worth reviewing, since most auditors will ask why.

Search-ADAccount -PasswordNeverExpires -UsersOnly | Select-Object Name, SamAccountName

Whose passwords have already expired?

Search-ADAccount -PasswordExpired -UsersOnly | Select-Object Name, SamAccountName

When does each user’s password expire? Handy for warning people before they’re locked out on a Monday morning.

Get-ADUser -Filter 'Enabled -eq $true' -Properties 'msDS-UserPasswordExpiryTimeComputed' |
  Select-Object Name, @{Name='PasswordExpires'; Expression={
    $v = $_.'msDS-UserPasswordExpiryTimeComputed'
    if ($v -eq 0) { 'Must change at next logon' }
    elseif ($v -ge [long]::MaxValue) { 'Never' }
    else { [datetime]::FromFileTime($v) }
  }} | Sort-Object PasswordExpires

Active Directory stores special values for two cases, so the command translates them: accounts set to never expire show Never, and accounts flagged to change their password at next logon say so, instead of showing a meaningless date.

Everything about one user Shows every attribute on one account. Replace jsmith with the user’s login name.

Get-ADUser -Identity jsmith -Properties *

Groups and admin access

Who’s in Domain Admins, including through nested groups? The single most important list in your directory. The -Recursive switch includes people who get admin rights through a group inside the group.

Get-ADGroupMember -Identity 'Domain Admins' -Recursive | Select-Object Name, SamAccountName, objectClass

Run it again with 'Enterprise Admins' and 'Administrators' for the full picture.

Which groups is a user in?

Get-ADPrincipalGroupMembership -Identity jsmith | Select-Object Name

Computers

Which computers haven’t checked in for 90 days? Old PCs and servers that were retired but never removed from the domain.

Search-ADAccount -AccountInactive -TimeSpan 90.00:00:00 -ComputersOnly | Select-Object Name, LastLogonDate

What operating systems are on the domain? A quick inventory, useful for spotting machines on unsupported versions of Windows.

Get-ADComputer -Filter * -Properties OperatingSystem, OperatingSystemVersion |
  Select-Object Name, OperatingSystem, OperatingSystemVersion | Sort-Object OperatingSystem

Domain controllers and domain health

List every domain controller

Get-ADDomainController -Filter * | Select-Object Name, IPv4Address, OperatingSystem, Site, IsGlobalCatalog

Which domain controller holds each FSMO role? You’ll need this before retiring or rebuilding a domain controller.

Get-ADDomain | Select-Object PDCEmulator, RIDMaster, InfrastructureMaster
Get-ADForest | Select-Object SchemaMaster, DomainNamingMaster

What’s the domain password policy? Minimum length, lockout threshold, and maximum password age, which auditors often ask for.

Get-ADDefaultDomainPasswordPolicy

Is replication healthy? These are classic command-line tools, not PowerShell cmdlets, but they run fine in PowerShell. Run them on a domain controller. repadmin /replsummary shows replication failures between domain controllers at a glance, and dcdiag runs a broad health check.

repadmin /replsummary
dcdiag /q

The /q switch tells dcdiag to show only errors. No output is good news.

Going further

  • Planning to replace old domain controllers? See Retiring old domain controllers before Server 2016 support ends.
  • When you’re ready to act on these reports (disabling stale accounts, unlocking users), do it deliberately and one change at a time, and keep your CSV reports as a record of what you changed and why.

Sources

All articles