次の方法で共有


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

Comments

  • Anonymous
    February 23, 2014
    Nice !

  • Anonymous
    February 25, 2014
    Goode Information

  • Anonymous
    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 article

  • Anonymous
    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 this

  • Anonymous
    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
    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