How to Modify Security Inheritance on Active Directory Objects using PowerShell

A couple of weeks ago I was working with a customer analyzing a number of user accounts affected by AdminSDHolder protection.  User accounts that are members of privileged groups such as Domain Admins end up being modified so they are protected by AdminSDHolder.  There is a property named AdminCount that usually has no value that is set to “1” once an account is added to a privileged group.  In addition the inheritance flag is unchecked so the account no longer inherits permissions from a parent object (I.E. an OU).  The AdminCount value has no effect on the account and is used as a flag to indicate the account is, or was, protected.  More information can be found in the Reference section below.

When the account is removed from a privileged group these two changes are not automatically reversed and must be changed manually.  The inability to inherit permissions will prevent your delegation model from working as expected so we decided to create script to set the AdminCount back to it’s default state and enable inheritance on the user objects.

Finding accounts that were or are members of privileged groups is easy enough.  You can use the Get-ADUser cmdlet to find them as shown below:

Get-ADUser -Filter {AdminCount -eq 1} -properties AdminCount

Changing the AdminCount property on an object back to the default state is easy enough as well:

Set-ADUser <user identity> -Clear AdminCount

Modifying the inheritance flag was a bit more challenging.  First lets describe the behavior of the inheritance flag and where it is located in the GUI.  The Inheritance Flag can be set using Active Directory Users & Computers (ADUCs).  You can see checkbox by viewing the properties for a user, selecting the “Security” tab and then clicking the “Advanced” button and you will see the screen shown below.

image

Figure 1 – Inheritance flag check box

If you uncheck the box you will receive the following prompt asking you if you want to copy the existing permissions to the object.

image

Figure 2 – Copy permissions dialog,

Clicking “Add” will add the inherited permissions form the parent object.  Clicking “Remove” will leave the object with the permissions applied explicitly to the object.

We spent a good deal of time trying to find a way to “check” the inheritance box programmatically in PowerShell.  After a few hours we realized we had other work to do and settled on a tried and true method that uses DSACLS command to enable inheritance.  The command is shown below.

DSACLS <User DN> /P:N

We were now able to script these changes on the desired accounts but I still wanted to find out how to do this using native PowerShell commands…

The Quest…

Before you read any further you need to understand I am NOT a programmer.  I’m a support engineer who likes to automate tasks with scripting languages.  The great thing about PowerShell is it exposes the underlying .NET code and makes it easy for mortals like me to write scripts quickly and easily.  For more advanced scripts I am forced to “look under the hood” understand what is happening at the programming level.  This is where the challenge, and the learning happens for me.  The remainder of the article will explain how I found the solution to my problem.

Most of my research for enabling inheritance on an object with PowerShell showed examples of modifying files and folders instead of users.  The interesting thing is the same PowerShell cmdlets works for files, folders and Active Directory objects.

The good news is Get-Acl and Set-Acl cmdlets can be used to read and set security on Active Directory objects.  The bad news is the process to enable/disable inheritance is not very intuitive at all. 

To get started I created a test account named “ACL Test” and unchecked the inheritance flag.  I then started looking at all properties on the object by running the following command:

Get-ADUSer acltest –properties * |fl *

I noticed there was something called nTSecurityDescriptor that displayed  “System.DirectoryServices.ActiveDirectorySecurity” instead of a normal property value which indicated I was dealing with an object.  I used the following commands to expose more information:

$user = Get-ADUSer acltest –properties * |fl *

$user.nTSecurityDescriptor |Get-Member

This command showed promise.

image

Figure 3 – Get-Member output for nTSecurityDescriptor 

At some point during several hours of “Bing-ing” I became aware that the inheritance flag was also referred to an Access Rule.  The first thing I noticed was this:

AreAccessRulesProtected Property bool AreAccessRulesProtected {get;}

This property shows the state of the inheritance check flag on the object.  So the command below made it easy to check the status of the flag:

$user.nTSecurityDescriptor.AreAccessRulesProtected 

If the box was checked the command would return FALSE.  If the box was unchecked it would return TRUE.  I was able to verify this by checking and unchecking the box manually and running the code above.

As I started researching the process and testing code it became apparent to me I needed an easier way to determine if my code was making the change successfully.  Frankly refreshing the object in ADUCs, viewing the properties selecting the “Security” tab and then “Advanced” button to view the checkbox for each test cycle was getting old.  To solve this problem I used a PowerShell loop running in a separate window to monitor the change on the test account.  The PowerShell code shown below checks the status of the flag every five seconds and repeats the check !000 times which meant I could work for over 80 minutes before restarting my monitor loop. 

FOR($i=1;$i -le 1000; $i++){(get-aduser acltest -properties nTSecurityDescriptor).nTSecurityDescriptor.AreAccessRulesProtected ;Write-host "Count = $i";start-sleep 5}

The output is shown below.  I displayed the “count” so I could tell the display was updating.  You can see where the state change occurred during my testing.

image

Figure 4 – Monitoring my progress

Keep in mind when you make a change in Active Directory it does not appear immediately but can take several seconds before the change can be detected.  During my testing a change was usually detected within one or two 5 second cycles.

During my research I kept seeing examples of using Get-Acl and Set-Acl to set inheritance on OUs, files and folders but no examples of performing this task on users.  I’ll spare you the details but I was unable to change the state of the inheritance flags using the methods associated with $user.nTSecurityDescriptor object initially because I did not understand how to "write" the updated security information back to Active Directory.  At this point in my testing and research I switched my focus to using Get-Acl and Set-Acl to solve my problem.

The first thing I did was run Get-Acl on the “ACL Test” user object and store that in a variable.

$ACL = Get-ACL -path 'AD:\CN=ACL Test,OU=Managed Users,DC=Contoso,DC=com'

The $ACL variable is an object that represents the security of the “CN=ACL Test,OU=Managed Users,DC=Contoso,DC=com” user object.  I then ran Get-Member on $ACL to expose the same properties and methods I had seen on the nTSecurityDescriptor object in earlier testing.

$ACL | Get-Member

NOTE- See the ACL_GET_MEMBER.TXT file attached below to see all the properties and methods exposed.

Several properties and methods were exposed but I was focused on two.  The first was the “AreAccessRulesProtected” property which tells me the status of the inheritance checkbox.  The second one is the “SetAccessRuleProtection” method shown below:

SetAccessRuleProtection Method void SetAccessRuleProtection(bool IsProtected, bool preserveInheritance)

Lets look at this a little closer.  We see that “SetAccessRuleProtection” is a method which means we can take some type of action on the object.  Then we see the word “bool” which is short for Boolean which means the two parameters we are passing; IsProtected and PreserveInheritance will be TRUE or FALSE when passed to the method.  If we call the “SetAccessRuleProtection” method and set the value of the IsProtected parameter to TRUE, the inheritance checkbox will be cleared.  If we set it to FALSE, the checkbox will become checked.  The “preserveInheritance” parameter only has an effect when we uncheck the inheritance checkbox  (IsProtected = TRUE).  If we set preserveInheritance to TRUE then the permissions from the parent object are copied to the object.  It has the same effect as clicking “Add” in figure 2 above.  If “preserverInheritance” is set to FALSE, it has the same effect as clicking “Remove” in Figure 2 above.  Let’s take a look at the code in action.

First we create an object that represents the current security configuration of our user (ACL Test) using Get-Acl and store it in the variable $ACL.  Notice the “AD:\” in front of the distinguished name for the user object.  This let’s Get-Acl command know the object is located on the Active Directory PSDrive (PowerShell Drive).

$ACL = Get-ACL -path 'AD:\CN=ACL Test,OU=Managed Users,DC=Contoso,DC=com'

Next we call the SetAccessRuleProtection method of the $ACL security object and pass $False for the “IsProtected” parameter which will “check” the inheritance checkbox for the user account. We also pass $True for the “preserveInheritance” parameter but it has no effect since we are enabling inheritance.  At this point in time we have modified the security on the $ACL object but we have not modified the object in Active Directory.

$ACL.SetAccessRuleProtection($False,$True)

Finally we use Set-Acl to overwrite the security of the “ACL Test” user account in Active Directory with the value stored in $ACL variable.

Set-ACL -AclObject $ACL -path 'AD:\CN=ACL Test,OU=Managed Users,DC=Contoso,DC=com'

Now that I understood how to write the security changes back to Active Directory using Set-Acl I realized I would be able to perform the same task using the $user.nTSecurityDescriptor path I had started down originally.  Here is the solution using that method:

 $user = get-aduser acltest -properties ntsecuritydescriptor
 $user.ntsecuritydescriptor.SetAccessRuleProtection($false, $true)
 Set-Acl -AclObject $user.ntsecurityDescriptor  -Path "AD:\CN=ACL Test,OU=PS_USERS,DC=AD,DC=CONTOSO,DC=COM"
 

Putting it All Together

Now that we know how to use Get-Acl and Set-Acl to modify the inheritance flag let’s put it in a PowerShell function to make it easier to use in a script:

Function Set-Inheritance {
param($ObjectPath)
$ACL = Get-ACL -path "AD:\$ObjectPath"
If ($acl.AreAccessRulesProtected){
$ACL.SetAccessRuleProtection($False, $True)
Set-ACL -AclObject $ACL -path "AD:\$ObjectPath"
Write-Host "MODIFIED "$ObjectPath
} #End IF
} #End Function Set-Inheritance

To use the function we call the function and pass the distinguished name of the object we want to enable inheritance on.  Here is a sample script that uses the function:

Import-Module ActiveDirectory

Function Set-Inheritance {
param($ObjectPath)
$ACL = Get-ACL -path "AD:\$ObjectPath"
If ($acl.AreAccessRulesProtected){
$ACL.SetAccessRuleProtection($False, $True)
Set-ACL -AclObject $ACL -path "AD:\$ObjectPath"
Write-Host "MODIFIED "$ObjectPath
} #End IF
} #End Function Set-Inheritance

#Find user with AdminCount set to 1
$users = get-aduser -SearchBase "OU=Managed Users,DC=Contoso,DC=com" -Filter {AdminCount -eq 1}
#Enable inheritance flag for each user
$users | foreach {Set-Inheritance $_.distinguishedname}

We place the PowerShell function Set-Inheritance at the top of the script so it is loaded into memory before we call the function later in the script.  We search for users with their AdminCount property set to “1” and store them in the $users variable.  Then we loop through each user in the variable $users and pass the distinguished name of each user to the Set-Inheritance function to set the inheritance flag so the object will inherit permissions.

So there you have it.  A long discussion on how I arrived at what amounts to three lines of PowerShell code to perform the same task as “DSACLS <User DN> /P:N”.  As always if you have feedback or questions use the comments section below.

Reference

Regarding AdminSDHolder
https://blogs.technet.com/b/asiasupp/archive/2006/11/16/adminsdholder1.aspx

AdminSDHolder, Protected Groups and SDPROP
https://technet.microsoft.com/en-us/magazine/2009.09.sdadminholder.aspx

Delegated permissions are not available and inheritance is automatically disabled
https://support.microsoft.com/kb/817433

AdminSDHolder Object Affects Delegation of Control for Past Administrator Accounts
https://support.microsoft.com/kb/306398

ACL_GET_MEMBER.txt