A PowerShell V1 script to find all orphaned objects in Active Directory. These are security principals that were once members of a group protected by the Security Descriptor Propagator process (SDProp). When these objects are removed from protected groups they become orphaned.

Certain security principals in Active Directory are protected. A process called SDProp (Security Descriptor Propagator) runs once an hour (by default) on the domain controller with the PDC Emulator role. SDProp compares the permissions of all protected objects to those assigned to the AdminSDHolder object. If they are different, SDProp overwrites the permissions so they match those of the AdminSDHolder object. By default, the AdminSDHolder object has inheritance disabled, so SDProp also enforces this on the protected objects. Finally, SDProp assigns the value 1 to the adminCount attribute. The AdminSDHolder object is in the cn=System container of the domain.

When a security principal is removed from all protected groups, it no longer is protected by SDProp. However, the value of the adminCount attribute is not changed. In addition, inheritance remains disabled. This is a source of confusion and a possible security concern. These objects are called orphaned. Without inheritance, permissions applied to the parent organizational unit are not applied to such objects. It is important to review these objects. Some people recommend that they be deleted or disabled. At the very least, inheritance should be enabled. Clearing the adminCount attribute would avoid confusion. Many people use this attribute as a quick way to find protected objects.

A default set of groups and users is protected automatically. The list of default protected objects is determined by the operating system of the PDC Emulator. In addition, any members of the default protected groups will also be protected. This includes nested groups and their members.

This script first determines the default protected objects in each domain in the forest. The list depends on the operating system and service pack of the domain controller with the PDC Emulator FSMO role. Then the script adds all nested objects of the default groups. The default protected objects are identified by their RID, so they can be identified even if they have been renamed.

The script also considers the dSHeuristic attribute of the "cn=Directory Service,cn=Windows NT,cn=Services" object in the Configuration container. The value of this attribute can be modified to exclude some of the builtin Operator groups from protection.

A recursive function is used to check nested group membership. The nested groups can include distribution groups. The script also considers cases where the primary group of the object is a protected group.

For each domain in the forest the script does the following:

  1. Collects the RID values of all default protected objects, taking into account the operating system of the PDC Emulator and the value of the dSHeuristic attribute.
  2. Retrieves all default protected groups and their nested group members. The script outputs their distinguished names, their sAMAccountNames, whether they are default or nested groups, and whether inheritance is disabled. At the same time, the script constructs two filters for later use. The first is a filter of all security principals that are members of any protected group. The second is a filter of all security principals that are not members of any protected group.
  3. The script uses the first filter constructed in the previous step to retrieve all security principals that are members of a protected group. This includes nested protected groups. It also includes objects where the primary group is a protected group. However, the script skips default protected users and groups, as they were documented in the previous step. For each object found the script outputs the distinguished name, object class, sAMAccountName, the value of adminCount, whether inheritance is enabled, and the protected group membership that makes this object protected. The object could be a member of more than one protected group, but the script only documents the first one found.
  4. The script uses the second filter constructed above to retrieve all security principals that are not members of a protected group. We want to only consider such objects where inheritance is disabled, but we cannot filter on that. The script outputs the objects found if either inheritance is disabled or the adminCount attribute is 1. These are potential orphaned objects. For these objects the script outputs distinguished name, object class, sAMAccountName, adminCount, and whether inheritance is enabled.
  5. Finally, the script outputs totals for all objects.

The output from the script can be redirected to a text file. Any orphaned objects should be investigated. Some should be disabled or deleted. Others should have inheritance enabled so permissions applied to parent organizational units will be inherited. All should have the adminCount attribute cleared to avoid confusion.

FindOrphanedAdminSDHolders.txt <<-- Click here to view or download the program