Auditing Active Directory with PowerShell: Practical Queries

Ready-to-run PowerShell queries for auditing Active Directory: stale accounts, privileged group membership, password policy exceptions and delegation risks.

Auditing Active Directory with PowerShell: Practical Queries

Most Active Directory audits produce a spreadsheet nobody reads. The useful ones answer specific questions: who has rights they shouldn’t, which accounts are dormant, and what would an attacker find interesting.

These are queries you can run today. They need the ActiveDirectory module and read access. Nothing more.

Quick Facts

  • Everything here is read-only. Nothing changes anything.
  • Search-ADAccount handles most stale-account questions in one line.
  • Accounts with non-expiring passwords and Kerberos pre-auth disabled are the two findings worth acting on immediately.
  • LastLogonDate replicates slowly. Treat it as approximate, not exact.
  • Export to CSV and review with the service owner. An audit nobody acts on is wasted effort.

Stale accounts

Import-Module ActiveDirectory

# Users not logged in for 90 days, still enabled
Search-ADAccount -AccountInactive -TimeSpan 90.00:00:00 -UsersOnly |
    Where-Object Enabled -eq $true |
    Select-Object Name, SamAccountName, LastLogonDate, DistinguishedName |
    Sort-Object LastLogonDate |
    Export-Csv C:\Audit\StaleUsers.csv -NoTypeInformation

# Computers not seen for 180 days
Search-ADAccount -AccountInactive -TimeSpan 180.00:00:00 -ComputersOnly |
    Where-Object Enabled -eq $true |
    Select-Object Name, LastLogonDate, DistinguishedName

Check LastLogonDate against a maintenance window before disabling anything. Service accounts and seasonal staff both look dormant and both cause outages when disabled.

Privileged group membership

$privileged = 'Domain Admins','Enterprise Admins','Schema Admins',
              'Administrators','Account Operators','Backup Operators',
              'Server Operators','Print Operators'

foreach ($group in $privileged) {
    Get-ADGroupMember -Identity $group -Recursive -ErrorAction SilentlyContinue |
        Select-Object @{N='Group';E={$group}}, Name, SamAccountName, objectClass
}

-Recursive matters. Nested groups are how privilege quietly accumulates. Someone joins a team group, that group is a member of another, and three hops later they’re an administrator without anyone deciding it.

Account Operators and Print Operators are the ones people forget. Both can be escalated to full domain compromise, and both are frequently populated with people who were given them years ago.

Password and account settings worth flagging

# Passwords that never expire
Get-ADUser -Filter {PasswordNeverExpires -eq $true -and Enabled -eq $true} `
    -Properties PasswordNeverExpires, PasswordLastSet |
    Select-Object Name, SamAccountName, PasswordLastSet

# Kerberos pre-authentication disabled - AS-REP roasting risk
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth |
    Select-Object Name, SamAccountName

# Accounts with a Service Principal Name - Kerberoasting targets
Get-ADUser -Filter {ServicePrincipalName -like '*'} -Properties ServicePrincipalName, PasswordLastSet |
    Select-Object Name, SamAccountName, PasswordLastSet, ServicePrincipalName

Those last two are the ones a penetration tester runs first. An account with an SPN and a password set in 2019 is a realistic route to domain compromise, because its password hash can be requested by any authenticated user and cracked offline.

Delegation

# Unconstrained delegation - high risk
Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation |
    Select-Object Name, DistinguishedName

# Accounts trusted to delegate to specific services
Get-ADObject -Filter {msDS-AllowedToDelegateTo -like '*'} `
    -Properties msDS-AllowedToDelegateTo |
    Select-Object Name, msDS-AllowedToDelegateTo

Unconstrained delegation on anything other than a domain controller deserves a conversation. A compromised host with it can capture and reuse the credentials of anyone who connects.

Turning it into something repeatable

Quick Steps

  1. Save the queries as a single script that writes one CSV per finding.
  2. Schedule it monthly to a share the security and infrastructure owners can both reach.
  3. Compare against last month. A new Domain Admin is far more interesting than a list of existing ones.
  4. Record accepted exceptions in the script’s own comments, so recurring findings don’t get re-investigated.
  5. Review with the service owner, not in isolation. Most odd-looking accounts have a reason.

Glossary

TermWhat it means
Search-ADAccountPurpose-built cmdlet for finding disabled, expired, locked or inactive accounts.
SPNService Principal Name. Links a service to an account. Makes it a Kerberoasting target.
AS-REP roastingAttack against accounts with Kerberos pre-authentication disabled.
Unconstrained delegationA host allowed to impersonate any user who connects to it. High risk.
LastLogonDateA replicated approximation of last logon. Accurate to within days, not minutes.
Nested groupA group that is a member of another group, inheriting its rights.

Frequently asked questions

Do I need Domain Admin to run these?

No. Ordinary authenticated users can read most of Active Directory, which is precisely why these findings matter. An attacker with any account can run the same queries.

Why is LastLogonDate inaccurate?

It replicates between domain controllers roughly every 9-14 days by design. For exact figures you’d query lastLogon on each DC individually, but for stale-account work the approximation is fine.

What should I fix first?

Accounts with an SPN and an old password, then anything with Kerberos pre-auth disabled, then privileged group membership. Those three are the routes attackers actually use.

Is it safe to disable stale accounts automatically?

Not without review. Service accounts, shared mailboxes and seasonal staff all look dormant. Report first, disable after someone confirms.

🛠️

Gear We Recommend

Testing AD changes safely needs a lab. Here’s the kit ours runs on.

Browse our Home Lab picks on Amazon

As an Amazon Associate, TechyGeeksHome earns from qualifying purchases

Disclosure: this post may contain affiliate links. If you buy through one of them, we may earn a small commission at no extra cost to you. We only recommend products we’ve tested or genuinely rate.


Discover more from TechyGeeksHome

Subscribe to get the latest posts sent to your email.

Andrew Armstrong

Andrew Armstrong is a UK-based IT professional with 26+ years of hands-on experience in Windows, Windows Server, SCCM/ConfigMgr, Intune, Active Directory, PowerShell and enterprise infrastructure.

He founded TechyGeeksHome in 2010 and has published 770+ practical guides to real-world IT problems. He also builds free Windows utilities, including Ultimate Settings Panel, which has been downloaded over 850,000 times.

Leave a Reply

Your email address will not be published. Required fields are marked *