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.
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.
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.
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.
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
Comments
Anonymous
February 23, 2014
Nice !Anonymous
February 25, 2014
Goode InformationAnonymous
March 17, 2014
This is a great article. Thanks for taking the time to write it. I work in PowerShell but I am not savvy with scripting. What if I wanted a simple script to just search the AD and list users which have the Inherit permission checked off and nothing more. Can anyone help? It's very much appreciated.Anonymous
April 20, 2014
What a great passion. Thanks for the detailed article. This is what I look for.Anonymous
April 24, 2014
So useful ! Thank you for this great articleAnonymous
July 24, 2014
Thanks, Provided great help!Anonymous
November 12, 2014
Thanks for this amazing post! Understanding how this is working is one of the most important things to me since I've been starting using powershell. It's been saving weeks of serching and correcting Domains bevore Exchange migrations or other AD scenarios.Anonymous
July 07, 2015
Thanks. That's a very helpful post. Especially during migrations you need to reconfigure a lot of user objects.Anonymous
July 29, 2015
i'm looking for a way to create a script that creates a new OU , adds users and groups,sets the users into the groups (i already accomplished) and then sets the permissions of the users on that group (also FIM permissions). is there any way to do that with a script ?Anonymous
August 02, 2015
@ David G. - The short answer is YES but the details are more than I can provide here. I would suggest spending some time on the Technet Script Center (technet.microsoft.com/.../default) and look for samples of these tasks.Anonymous
November 19, 2015
Great! Thanks for sharing thisAnonymous
December 21, 2015
Just for checking out which users has the inheritance switched of, u can use this: Import-Module ActiveDirectory $user = Get-ADUSer -filter * –properties * foreach ($item in $user) { if ($item.nTSecurityDescriptor.AreAccessRulesProtected) { echo $item.SamAccountName }} Now u have a list of user-objects where inheritance ist switched off. Thanks for this blog. It was the basis for this small script. Thanks!Anonymous
January 12, 2016
Thanks to author!Anonymous
March 01, 2016
This was super helpful, thanks!Anonymous
April 05, 2016
This is fantastic. We recently granted Delegate Control for an MSP's Help Desk to reset user passwords, drop/join to domain, and move computers to approved OUs. This is across roughly 35 clients, totally close to 20,000 users. However, we were finding that not all user accounts in approved OU could have their passwords reset by the Help Desk. After a little investigation we found that not all accounts have inheritance enabled. We didn't build the environments, so we don't know the reason behind this. It's quite random, and having to check one by one was going to be taxing and a great bit of time to say the least.This will allow us to draft a report of which accounts in what OUs are the offending accounts and confirm with the clients if this was intended or not and then script the appropriate adjustments.Thanks so much for your hard work and sharing of your results.Anonymous
May 19, 2016
Excellent information, thanks. I've never messed too much with the PSDrive interface for AD... is there a simple way to specify a specific domain controller to target?Doing some reading just now, it seems like I would have to create a new PSDrive object and specify the DC as the "-Server" parameter for it, and then replace "AD:" with the name of the PSDrive I created. I've tested this and it works, but it adds some additional complexity to my script, and I'd prefer to keep it simple, but can't find much info on it... is there any similar syntax to the ADSI interface, where you could prepend a DC name to the distinguished name? I tried a few variations and it didn't work.Either way, great info and very well explained. Thanks again!Anonymous
July 07, 2016
Can I modify the script so I can use the function with a file such as txt,csv containing the ad information.If not, how can I exclude certain accounts from the function?Also, If I already cleared the admin count, should I mark those accounts again so the function would work correctly?Anonymous
July 10, 2016
Elegantly done! Thanks for taking to time to explain how the acl object first gets set and then written back into the user object. very well done and thanks again!!Anonymous
August 15, 2016
The comment has been removed- Anonymous
October 07, 2016
What version of powershell are you using, and what functional AD level do you have?- Anonymous
October 07, 2016
@Daniel The FFL would have been 2008 R2 but it would have no impact on setting security ACLs. The PowerShell version would have been 3.0.
- Anonymous
- Anonymous
Anonymous
January 11, 2017
Wonderful Article, the way you went through the steps are real help, what a beginner or intermediate PS user may be looking for how to and where to using get-command. THANK YOU!Anonymous
February 23, 2018
work like a charm, thanks u so much for your job and for sharing it.regards