🏢Active DirectoryAccountsPermissionsPrivileged Access

Cleanup Stale AdminCount Active Directory: A Safe Workflow

Verify protected membership, clear confirmed stale adminCount flags, restore inheritance, and validate each account after SDProp.

Younes AZABARBy Younes AZABAR12 min read
Cleanup Stale AdminCount Active Directory: A Safe Workflow

Cleanup stale admincount active directory: what you are fixing

The operational goal behind cleanup stale admincount active directory is to remove a leftover administrative marker and restore the intended permission inheritance on accounts that have genuinely left protected groups. This is routine maintenance after role changes and offboarding. Our AdminSDHolder and SDProp backdoor guide covers a different scenario: an unauthorized ACE on the template can propagate to protected objects. Here, the decision starts with current membership and the account's ACL, without assuming an attacker is involved.

Checking nesting, clearing the flag and enabling inheritance are only part of this workflow. A safe maintenance record should identify protected groups by SID, resolve the primary group separately, review the parent OU, approve each account individually, and verify the result after a subsequent SDProp cycle. A smaller adminCount=1 inventory is useful, but it does not establish that effective permissions are correct.

How SDProp works and why the flag survives

AdminSDHolder supplies template permissions for protected objects. SDProp compares their permissions with that template on each domain's PDC Emulator (PDCE), every 60 minutes by default, and resets differences. Protected objects have inheritance disabled; moving them into another OU does not remove protection. Microsoft documents this in Appendix C: Protected Accounts and Groups. The AdminSDHolder protocol specification explains the periodic protection of security descriptors.

Microsoft's Admin-Count schema reference describes the attribute as recording an ACL strengthened after direct or transitive administrative membership. It is not a complete inventory of current privilege. The historical Microsoft article on AdminSDHolder and SDProp explains that, after protected membership is removed, adminCount=1 and disabled inheritance remain. The former member stops receiving the template ACL but still lacks parent inheritance.

Use the current Appendix C list rather than the historical article's older list:

Protected groupsDirectly protected accounts
Account Operators, Administrators, Backup Operators, Domain Admins, Domain Controllers, Enterprise Admins, Enterprise Key Admins, Key Admins, Print Operators, Read-only Domain Controllers, Replicator, Schema Admins, Server OperatorsAdministrator, Krbtgt

Administrator and Krbtgt are accounts, not membership groups. Record any applicable domain configuration or forest exceptions; do not change dSHeuristics to simplify cleanup. The Protected Users group is a separate authentication control; it is not itself protected by AdminSDHolder.

Trap 1: a direct removal leaves a nested path

The historical Microsoft article describes transitive protection, including paths through distribution groups. A user removed from Domain Admins can still reach a protected group through another group. If that path remains, clearing an attribute does not remove protection, and enabling inheritance can be undone by SDProp. Do not promise that clearing the flag alone necessarily causes it to be stamped again at a precisely timed next cycle; the supported conclusion is that the remaining membership still makes the object protected.

Trap 2: the marker disappears but the ACL remains wrong

For a confirmed former member, clearing adminCount leaves inheritance unchanged. Delegation on the parent OU may therefore still fail to reach the account. This does not prove that the account's existing ACL is more permissive: it may instead be missing intended management rights. Conversely, enabling inheritance under an excessively delegated OU can add unwanted permissions. Explicit ACEs also require their own review; enabling inheritance does not itself remove them.

Detection: build a candidate inventory

Start with a read-only inventory. Get-ADObject documents -LDAPFilter, -Properties and -Server; this adaptation requests the marker and the state needed for investigation:

Get-ADObject -LDAPFilter "(adminCount=1)" -Properties adminCount,memberOf,objectSid,primaryGroupID,nTSecurityDescriptor -Server "dc-pdce.corp.example"

The results can include groups and computers. Classify those separately rather than applying the user-account workflow to every returned object. For the user population discussed by the two EtcSec checks, use the documented Get-ADUser parameters:

Get-ADUser -LDAPFilter "(adminCount=1)" -Properties adminCount,memberOf,Enabled,SID,primaryGroupID -Server "dc-pdce.corp.example"

An empty or apparently ordinary memberOf value is a lead for investigation, not proof of an orphan. The MemberOf schema page describes group DNs; this property alone does not provide the full protection analysis required below.

SignalWhat it establishesRequired follow-up
adminCount=1A candidate with an administrative ACL markerResolve current protection and examine the ACL
No direct protected group in memberOfNo obvious direct explanation in that viewCheck transitive paths and the primary group
Disabled inheritanceParent permissions are disconnectedDetermine whether protection is legitimate or residual
Account disabledA separate account stateApply the same membership and ACL checks
Protection returns after maintenanceThe intended final state did not holdInvestigate remaining paths, new changes and collection gaps

This workflow detects a directory-state condition through queries and ACL review. It does not require an invented event ID or treat a historical group removal as evidence that the account is currently safe to change.

Remediation: verify, then change one account

Step 1 — Preserve the account and parent state

For each candidate, record its DN, SID, enabled state, direct groups, primary-group RID, business owner, full ACL and inheritance state. Record the DC and capture time. Keep an approved ACL restoration method available; saving text output is useful evidence, but it is not a claim of a restorable AD backup.

The Microsoft Dsacls reference documents displaying access entries without modifying them. The default output omits ownership and auditing information; /A includes those details when collecting the full security record. Run each read separately:

dsacls "\\dc-pdce.corp.example\CN=FormerAdmin,OU=Staff,DC=corp,DC=example"
dsacls "\\dc-pdce.corp.example\OU=Staff,DC=corp,DC=example"

Review the parent before permitting inheritance. Identify which inherited rights should appear, and which explicit account ACEs need to remain or be reviewed separately. A suspicious ACL moves the account into investigation rather than automatic cleanup.

Step 2 — Resolve protected groups by SID

Use Microsoft's SID reference to distinguish the domain identifier from the RID. Resolve every protected group in the relevant domain; forest groups require the appropriate forest-root-domain coverage. Renamed or localized group names are not a sufficient identity check.

Get-ADGroup accepts a SID for -Identity. The domain-specific SID below is an illustrative placeholder, not a discovered group. The built-in Administrators SID is also shown, with an explicit server to establish domain context:

Get-ADGroup -Identity "S-1-5-21-111111111-222222222-333333333-512" -Properties memberOf,groupType -Server "dc-pdce.corp.example"
Get-ADGroup -Identity "S-1-5-32-544" -Properties memberOf,groupType -Server "dc-pdce.corp.example"

Document the resolved SID and DN for each entry in the protected list. A lookup failure leaves a coverage gap. It is not equivalent to confirming that the group contains no relevant member.

Step 3 — Check recursive membership and the primary group

The Get-ADGroupMember documentation includes a recursive membership example. Adapt it to each verified protected-group SID and compare returned user SIDs with the candidate:

Get-ADGroupMember -Identity "S-1-5-21-111111111-222222222-333333333-512" -Recursive -Server "dc-pdce.corp.example"

Repeat across the full protected list and relevant domains. Include distribution-group paths. -Recursive returns members in the hierarchy that have no child objects; it is not a complete list of intermediate group edges. Microsoft also documents an other-forest limitation when that forest lacks Active Directory Web Service. Failed or incomplete enumeration must leave the verdict indeterminate.

Check the primary group separately. Microsoft's Primary-Group-ID reference defines the value as the user's primary-group RID. The primaryGroupID protocol reference confirms that the user's memberOf and the primary group's member attribute omit this membership. The reviewed collector constructs the primary-group SID from the domain SID and that RID. Resolve that full SID using Get-ADGroup; do not infer the answer from a friendly group name.

Then determine whether the primary group is directly protected or reaches a protected group through nesting. This documented Get-ADGroup adaptation reads the next set of parents:

Get-ADGroup -Identity "CN=PrimaryGroup,OU=Groups,DC=corp,DC=example" -Properties memberOf -Server "dc-pdce.corp.example"

Continue reviewing each parent with cycle handling and coverage of the domains concerned. The example is one read, not a complete validated traversal program. An absent candidate in recursive member output does not waive the primary-group check. Unresolved foreign members, unqueried domains or unresolved cycles prevent approval for a write.

Step 4 — Record the decision before writing

ClassificationEvidenceAction
ProtectedDirectly protected account, or direct, transitive or primary-group path to protectionKeep protection; review any unjustified membership with its owner
Confirmed orphanadminCount=1, all relevant protection paths ruled out, parent and account ACL understoodApprove individual flag and inheritance maintenance
IndeterminateMissing resolution, incomplete domain coverage, unresolved cycle or suspicious ACLDefer writes and complete the investigation

The built-in Administrator and Krbtgt accounts remain protected regardless of the absence of an explanatory direct membership. The reviewed collector identifies them by RID 500 and 502, including after renaming. A disabled account that remains protected belongs in the first row, not the orphan row. For a confirmed disabled orphan, decide between maintenance and retirement under the retention policy; this workflow does not automatically re-enable it.

Step 5 — Clear only the confirmed residual attribute

Set-ADObject documents -Clear with the LDAP display name, plus -WhatIf and -Confirm. First preview the individual change:

Set-ADObject -Identity "CN=FormerAdmin,OU=Staff,DC=corp,DC=example" -Clear adminCount -Server "dc-pdce.corp.example" -WhatIf

Only after the decision and review, issue the separate confirmed write:

Set-ADObject -Identity "CN=FormerAdmin,OU=Staff,DC=corp,DC=example" -Clear adminCount -Server "dc-pdce.corp.example" -Confirm

-Clear removes the value; it does not write the number zero. The command does not remove memberships, modify explicit ACEs or enable inheritance. A successful preview also says nothing about whether the candidate is genuinely orphaned.

Step 6 — Enable inheritance and review the resulting ACL

The Dsacls reference defines /P:N as permitting the object to inherit parent permissions. Apply it only to the approved object, after the parent review:

dsacls "\\dc-pdce.corp.example\CN=FormerAdmin,OU=Staff,DC=corp,DC=example" /P:N

Check the exit result and immediately repeat the ACL read from step 1. Compare inherited rights with the approved parent baseline and review the explicit entries independently. /P:N changes the object's inheritance protection; it does not change an ACE's inheritability. Do not substitute /S or /S /T: /S restores the object's schema-default security, and /T extends that restoration to the tree only when used with /S.

Validation after the next SDProp cycle

Reread the account on the recorded server, using the Get-ADUser parameters documented above:

Get-ADUser -Identity "CN=FormerAdmin,OU=Staff,DC=corp,DC=example" -Properties adminCount,memberOf,primaryGroupID,Enabled -Server "dc-pdce.corp.example"

Read its ACL separately:

dsacls "\\dc-pdce.corp.example\CN=FormerAdmin,OU=Staff,DC=corp,DC=example"

Repeat membership resolution as well: membership can change between inventory and maintenance. Wait for a subsequent SDProp pass under the actual configured interval, and account for replication before comparing DCs. Record the check time and servers, the absence of the residual flag, expected inheritance, explicit-ACE review and a real test of the intended delegation. Claim “validated after the cycle” only when that observation exists.

If protection returns, investigate a remaining group path, a recent addition or incomplete collection. Repeatedly clearing the attribute is not a resolution. Microsoft's Appendix C provides an optional laboratory method using Ldp.exe on the PDCE with rootDSE RunProtectAdminGroupsTask=1. Prefer the normal cycle for this production maintenance workflow and leave its frequency unchanged. The historical article's FixUpInheritance operation should not be substituted automatically for that task.

Our privileged access drift guide explains why group and ACL exceptions need owners and revalidation against effective rights. Apply that principle here: successful marker cleanup is one maintenance result, while proving that the account's permissions match its intended role requires the broader rights review.

How EtcSec Detects This

The two reviewed etc-collector checks share the same direct-or-primary-membership orphan predicate, applied to different account populations:

CheckPopulationSeveritySelection
ADMIN_COUNT_ORPHANEDActive usersMediumAdministrative marker present, no recognized direct protected membership or directly protected primary group, excluding RID 500/502
ADMIN_COUNT_ORPHANED_ON_DISABLED_ACCOUNTDisabled usersLowThe same predicate, with the Disabled condition inverted

The normalized selection blocks differ only in if u.Disabled versus if !u.Disabled; titles, severity and recommendations also differ. These are two account-state branches of the same check, not independent mechanisms to add together as evidence of greater risk.

Nested memberships are not resolved. Neither check expands a group nested into a protected group, including a primary group that is itself nested. Such accounts can still be protected and produce a false orphan candidate. The helper resolves protected-group DNs from SID suffixes in the collected index, then compares direct DNs; missing group or SID data can also leave an unexplained candidate.

Neither Detect() reads the account's ACL or inheritance state. A finding therefore does not establish dangerous residual rights, prove the absence of SDProp protection or authorize clearing the attribute. Confirm it with the membership and ACL workflow before making changes. After maintenance and directory validation, rerun the audit to compare candidate findings with the recorded result.

Explore the identity security pages that support this topic