What an AD OU Direct Child Census Establishes
An AD OU direct child census records which objects sit immediately below a particular organizational unit. Before restructuring, use it to answer a narrow question: what does this OU contain at this level, in the directory view we actually read? Keep a separate inventory for the hierarchy beneath it. A direct-child count is useful for planning, but an apparently empty counter should never become an automatic removal decision.
This guide was reviewed on 4 October 2026 against Microsoft Learn and the collector revision identified below. It provides a reconciliation procedure for administrators reviewing an OU inventory. The PowerShell commands are documentary reader examples adapted from the documented parameter syntax; they were not executed against an AD domain for this article. No successful collection, empty production OU, or completed change is claimed.
The practical problem is a mismatch between a counter's label and its population. A report may show Users, Computers, Groups, and OUs while the reviewer assumes it describes every object beneath the OU. It may also omit objects through collection limits, exclusions, or a parent-DN comparison. Preserve those boundaries before using the report in a change request.
How It Works: Separate One-Level Counts from the Hierarchy
Microsoft's Get-ADObject reference distinguishes OneLevel, which searches immediate children, from Subtree, which includes the base and descendants. SearchBase sets the target path and Server selects the directory instance. Specify both when producing evidence.
Build two records for the same target: a direct-child inventory for the local population, and a subtree inventory for dependencies deeper in the hierarchy. Label their scopes explicitly. Do not compare the sum of direct-child class counters with an unqualified subtree total. An OU containing another OU can have few direct identities while the subordinate hierarchy contains the objects that matter to the change.
For each inventory, preserve object identifiers, original DNs, class, collection context, and completion state. Keep the raw object list beside any summary count so a discrepancy can be investigated without guessing which objects contributed. An object list also makes it possible to separate an unexpected class from an unexpected parent relationship.
Decide what the class labels mean
Treat Users, Computers, Groups, and OUs as four collector populations, not a promise that every directory class has been read. Define the independent queries before comparing them. Record other returned classes separately; do not silently fold them into Users or ignore them because the dashboard lacks a matching counter.
The reviewed LDAP user population includes a group-managed service-account branch in its default filter. Therefore, the person-user query below and the collector's Users bucket can differ even when both reads complete. Compare the actual filter definitions and returned object identifiers. Keep service-account subclasses and any other unmatched classes visible in the reconciliation record instead of forcing a numerical match.
Our default containers and Group Policy scope guide explains why default destinations and intended policy scope need a separate placement review. Its OneLevel example inventories computers in the default Computers container. It does not provide a census of the four populations in an OU, account for every class, or resolve the escaped-DN issue addressed here.
Detection: Read Objects Before Comparing Counters
1. Establish the query context
Select the approved OU and an explicit directory server. Record the operator's read context, start and completion times, search base, scope, class filter, and any errors. Use an authorized administration host with the AD PowerShell module. The OU DN below appears in example 5 of the Get-ADObject reference; the server name is a fictional FQDN placeholder. Replace both with the reviewed target.
The following examples adapt the documented Filter, LDAPFilter, SearchBase, SearchScope, Server, and Properties parameters of Get-ADObject. They request identifiers for reconciliation. Save the returned objects and operation outcome through your administration workflow before calculating a summary.
First read the direct population without a class restriction:
Get-ADObject -Filter * -SearchBase 'OU=Accounting,DC=Fabrikam,DC=com' -SearchScope OneLevel -Server 'dc01.fabrikam.com' -Properties objectGUID
Use this list to discover classes that the four counters do not describe. The inventory remains bounded by the selected view and read context. If it fails or stops early, retain that outcome; the objects already displayed are not proof of a complete census.
2. Keep class queries separate
These reader examples define separate review populations; they do not reproduce all collector filters. The first combines objectClass=user with objectCategory=person, as documented in Microsoft's Querying for Users guidance. A user-class filter alone also matches computers because the computer class derives from user:
Get-ADObject -LDAPFilter '(&(objectClass=user)(objectCategory=person))' -SearchBase 'OU=Accounting,DC=Fabrikam,DC=com' -SearchScope OneLevel -Server 'dc01.fabrikam.com' -Properties objectGUID
Get-ADObject -Filter 'ObjectClass -eq "computer"' -SearchBase 'OU=Accounting,DC=Fabrikam,DC=com' -SearchScope OneLevel -Server 'dc01.fabrikam.com' -Properties objectGUID
Get-ADObject -Filter 'ObjectClass -eq "group"' -SearchBase 'OU=Accounting,DC=Fabrikam,DC=com' -SearchScope OneLevel -Server 'dc01.fabrikam.com' -Properties objectGUID
Get-ADObject -Filter 'ObjectClass -eq "organizationalUnit"' -SearchBase 'OU=Accounting,DC=Fabrikam,DC=com' -SearchScope OneLevel -Server 'dc01.fabrikam.com' -Properties objectGUID
Keep the four outputs individually identifiable. Record whether each query completed and compare its object set with the unrestricted OneLevel list. Where another class appears, add a separately labeled review population. Where a collector bucket uses a broader filter, document the extra population before reconciling its count.
3. Read the subtree as different evidence
Use a separate operation for the hierarchy:
Get-ADObject -Filter * -SearchBase 'OU=Accounting,DC=Fabrikam,DC=com' -SearchScope Subtree -Server 'dc01.fabrikam.com' -Properties objectGUID
Label this output Subtree and distinguish the base OU from the objects beneath it. Review subordinate OUs and their contents with their owners. Do not add this total to the direct-child counters or describe it as a second measurement of the same population.
4. Reconcile identifiers and escaped DNs
Learn documents objectGUID as an identifier accepted by Get-ADObject. Use it as the comparison key in the independent inventories and retain the DN as location evidence. Where the collector payload has no GUID, resolve its recorded DN in the same context and preserve the linkage; do not invent an identifier or match solely by display name.
Microsoft's distinguished-name reference explains that RDNs are comma-separated and that a comma within an attribute value must be escaped. A raw comma is therefore insufficient to decide where a name ends and its parent begins.
For example, Learn gives this DN with an escaped comma in the OU RDN:
CN=Litware,OU=Docs\, Adatum,DC=Fabrikam,DC=COM
Prefer the directory's OneLevel result when establishing immediate parentage. If an export must be processed locally, use a DN-aware parser that handles escaping and retain the original string. Do not split every comma or remove backslashes before deciding parentage.
Remediation: Reconcile the Census Before Restructuring
1. Qualify each observation
Use the reconciliation record to decide what is established and what remains open. A discrepancy is a reason to investigate scope, filters, identifiers, or collection, rather than a reason to alter objects until counts agree.
| Observation | Evidence interpretation | Next action |
|---|---|---|
| A completed class query returns no direct objects | Zero for that stated query context | Preserve the output and examine other classes and the subtree |
| Some objects return before a read fails or is interrupted | Partial result | Resolve the collection issue and repeat the scoped read |
| A returned class has no collector counter | Not represented by that counter | Keep a separate population and identify its dependencies |
| An object is excluded from the collector input | Outside the measured collector population | Record the exclusion and compare an explicitly matched subset |
| OneLevel contains an escaped-RDN object absent from a counter | Parent-matching discrepancy requiring review | Preserve the DN and identifier and examine the reviewed predicate |
| Delegations is empty in the OU inventory payload | Delegation evidence was not populated on this path | Arrange an independent permission review |
Attach this classification to the specific OU and class. A whole-row label such as “empty” loses the distinction between a verified class zero and a missing class read. If different queries completed at different times, preserve that difference instead of presenting them as one simultaneous snapshot.
2. Explain differences before changing the hierarchy
Compare individual objects first, then totals. Record whether each difference comes from another class, a deeper descendant, an exclusion, a filter, incomplete collection, or unresolved parent matching. Keep unexplained differences open with a responsible reviewer and the evidence needed to resolve them.
Use the same server and target context for the first reconciliation. If the review includes another server, label its inventory independently. A difference between views needs investigation; this procedure provides no basis for guessing a replication result or treating a convenient count as authoritative.
Document collector exclusions and any collection limits beside the independent reads. Keep an unrestricted administrative census separate from a subset intentionally aligned with collector inputs. An excluded object still matters to restructuring even though it does not contribute to a filtered counter.
3. Retain ownership and deletion safeguards
Give the owner the object-level record, the subtree review, the unresolved differences, and the proposed scope. Obtain a decision about the exact target and its dependencies. An OU name, a small count, or an empty dashboard field does not identify who may approve a move or cleanup.
Follow the complementary independent permission review and owner decision before OU cleanup. That guide addresses deletion safeguards and bounded exceptions. Keep that review in the change process; this census does not evaluate the relevant permissions or authorize removal of protection.
Before approving restructuring, identify the intended policy and administration consequences for the affected objects. Keep default-container placement and GPO scope in the placement review, and keep deletion safeguards in the complementary permission review. The census supplies the object population on which those decisions depend.
4. Verify the approved outcome
After an authorized change, repeat the relevant OneLevel and Subtree reads using the recorded target context. Reconcile the surviving objects by identifier and record their current locations. Preserve the owner's decision and actual operation outcome beside the before-and-after evidence.
Close the inventory task when the agreed populations and remaining uncertainties are documented. Close the change task against its own outcome criteria, including the complementary safeguards review. A count that looks cleaner is insufficient to establish that dependencies, policy scope, and retained-object protection were handled.
How EtcSec Detects This
This section describes only INFO_DOMAIN_OU_INVENTORY, reviewed in etc-collector commit afca1b9f7bba6f076d6a6493371486071001452b. The relevant detector is public/internal/audit/detectors/ad/permissions/organization/ou-inventory.go:31.
Its Detect() emits one Info finding with Count=1 for each collected OU. That count identifies an inventory entry, not the number of children or a vulnerability verdict. It returns no findings when the OU input is empty. Affected-entity details are populated only when IncludeDetails is enabled; absent details do not establish absent children.
The LDAP provider's GetOUs, at public/internal/providers/ldap/client.go:946, reads organizational units through its subtree search helper. A subtree collection of OUs does not turn the emitted child counters into descendant counts or prove universal completeness.
In public/internal/audit/gpo_policy.go:365–436, OUEntity builds ChildCounts from the supplied Users, Computers, Groups, and OUs arrays. The helper counts immediate children only. Asset exclusions applied before detection affect those populations; other directory classes are not a fifth all-object counter. The count must be interpreted against what was actually collected.
The parent test checks a case-insensitive OU suffix, then rejects a leading portion containing any comma. Static inference: an escaped comma in the leading RDN can cause a genuine direct child to be omitted. This follows from the reviewed string predicate and documented DN escaping; it was not demonstrated by executing a domain test here.
Finally, OUEntity initializes Delegations to an empty placeholder array because OU DACL delegation wiring is not populated on this path. It supplies no evidence that delegated rights are absent. Use the inventory to select and reconcile OUs; establish contents, permissions, and the approved restructuring outcome through the separate evidence described above.
Explore the identity security pages that support this topic
