Most Active Directory domains have more administrators than anyone expects, hidden by nested groups and old accounts. In this lab you use PowerShell to expand every privileged group, flag stale and risky admin accounts, find orphaned adminCount objects left behind by AdminSDHolder, and add your real administrators to the Protected Users group.
Key takeaways
- Always expand nested groups, because direct membership hides the real admin count
- Flag admin accounts that are stale, never expire, or have a service principal name
- adminCount stays at 1 after an account leaves a privileged group and must be cleaned up
- Protected Users blocks NTLM and delegation for the accounts that matter most
Why this lab matters
An attacker who reaches one Domain Admin account owns the domain. So the number of accounts with that level of access, and the state they are in, is the most important number in an Active Directory security review. It is nearly always higher than the team believes.
What you need
- A lab domain. Any Windows Server evaluation in a virtual machine promoted to a domain controller will do.
- A machine with the Active Directory PowerShell module (RSAT) and an account that can read the directory. Changes in Steps 4 and 5 need Domain Admin rights.
- Test data: create a few users, add some to Domain Admins directly and one through a nested group, then remove one again.
Step 1: Expand every privileged group
Import-Module ActiveDirectory
$groups = 'Domain Admins','Enterprise Admins','Schema Admins','Administrators',
'Account Operators','Backup Operators','Server Operators','Print Operators',
'DnsAdmins','Group Policy Creator Owners'
$members = foreach ($g in $groups) {
try {
Get-ADGroupMember -Identity $g -Recursive | Where-Object objectClass -eq 'user' |
ForEach-Object { [pscustomobject]@{ Group = $g; Sam = $_.SamAccountName; DN = $_.DistinguishedName } }
} catch { Write-Warning "Could not read $g" }
}
$members | Group-Object Group | Select-Object Name, Count
$admins = $members | Sort-Object Sam -Unique
Compare the counts with what your team expected. The operator groups are often forgotten, yet Account Operators and Backup Operators can each be used to take over a domain.
Step 2: Flag risky admin accounts
$cut = (Get-Date).AddDays(-90)
$detail = $admins | ForEach-Object {
Get-ADUser $_.Sam -Properties LastLogonTimestamp, PasswordLastSet, PasswordNeverExpires,
ServicePrincipalName, Enabled, DoesNotRequirePreAuth, TrustedForDelegation
} | Select-Object SamAccountName, Enabled,
@{n='LastLogon';e={[datetime]::FromFileTime($_.LastLogonTimestamp)}},
PasswordLastSet, PasswordNeverExpires,
@{n='HasSPN';e={[bool]$_.ServicePrincipalName}},
DoesNotRequirePreAuth, TrustedForDelegation
$detail | Where-Object { $_.LastLogon -lt $cut -or $_.PasswordNeverExpires -or $_.HasSPN -or -not $_.Enabled } |
Format-Table -AutoSize
$detail | Export-Csv .\privileged-accounts.csv -NoTypeInformation
| Flag | Why it matters |
|---|---|
| No logon in 90 days | Unused access that nobody would miss, or notice being abused |
| Password never expires | Old passwords are more likely to be in a breach list |
| Has an SPN | The account can be Kerberoasted. Admins should never have one |
| Disabled but still a member | Re-enabling it restores full admin rights at once |
| Does not require pre-authentication | Open to AS-REP roasting |
Step 3: Find orphaned adminCount accounts
A background process called SDProp runs every 60 minutes on the PDC emulator. It stamps adminCount = 1 on members of protected groups and replaces their permissions with the template on the AdminSDHolder object. When an account leaves the group, nothing reverses that. The account keeps blocked inheritance and often keeps delegated rights nobody intended.
$flagged = Get-ADUser -LDAPFilter '(adminCount=1)' -Properties adminCount
$orphans = $flagged | Where-Object { $_.SamAccountName -notin $admins.Sam -and $_.SamAccountName -ne 'krbtgt' }
$orphans | Select-Object SamAccountName, DistinguishedName
Step 4: Clean up
- Remove stale and unneeded accounts from the groups:
Remove-ADGroupMember -Identity 'Domain Admins' -Members 'old.admin' -Confirm:$false - For each orphan, clear the flag:
Set-ADUser 'former.admin' -Clear adminCount - Re-enable permission inheritance on the orphan. In Active Directory Users and Computers turn on Advanced Features, open the account, then Security, Advanced, Enable inheritance.
Do not clear adminCount on accounts that are still in a protected group. SDProp will set it again within the hour and you will have gained nothing.
Step 5: Add real admins to Protected Users
Members of Protected Users cannot authenticate with NTLM, cannot use DES or RC4 for Kerberos, cannot be delegated and get a four hour ticket lifetime.
Add-ADGroupMember -Identity 'Protected Users' -Members 'adm.jsmith'
Get-ADGroupMember 'Protected Users' | Select-Object SamAccountName
Add one admin first and have them work normally for a few days. Never add service accounts or computer accounts, because they will stop working.
Run Steps 1 to 3 again. You should see fewer privileged users, no flagged rows you cannot explain and no orphans.
Evidence to keep
- The before and after CSV files and group counts.
- The list of accounts removed, with the approver for each.
- Protected Users membership after the change.
Frequently asked questions
How do I list all Domain Admins including nested groups?
Run Get-ADGroupMember -Identity "Domain Admins" -Recursive in PowerShell. The Recursive switch expands nested groups so you see every user who really holds the rights.
What is adminCount in Active Directory?
adminCount is an attribute set to 1 on accounts that are, or once were, members of a protected group. The SDProp process uses it when applying the AdminSDHolder permissions template. It is not cleared automatically when the account leaves the group.
What does the Protected Users group do?
It applies non-configurable protections to its members: no NTLM authentication, no DES or RC4 Kerberos encryption, no delegation and a four hour ticket granting ticket lifetime. It is designed for human administrator accounts.
How often should privileged groups be reviewed?
Review them at least quarterly, and after any staff change in the admin team. Many teams schedule the script and alert when the membership changes.