Active Directory Delegation via PowerShell


In Active Directory, we have plenty of ways to perform delegation on directory objects:

  • The delegation wizard in ADU&C
  • The ACL Editor in ADU&C
  • Dsacls.exe command line

However, each of these options are either manual in nature or don’t necessarily fit well into the object-oriented nature of something like PowerShell. Sure we can call dsacls from our script, but doesn’t allow for as much flexibility when it comes to working with the objects that get created from a command like Get-ADOrganizationalUnit or Get-ADGroup. I’m often asked the best way to programmatically grant permissions within the directory so I thought I’d illustrate how this can be accomplished with everyone’s favorite language, PowerShell!

I’ll be using some of the cmdlets from the Active Directory PowerShell module, so that means you’ll need to have at least one Server 2008 R2 domain controller on your network or a down-level domain controller with the Active Directory Management Gateway Service installed. At it’s core, I’m still relying on the same .NET libraries that have existed for a while in System.DirectoryServices. This just highlights the Active Directory versions of the Get-ACL and Set-ACL cmdlets and also show some ways that makes working with this class a little more user friendly without having to dive into C# and managed code.

The pseudo code for doing this is pretty simple:

  1. Get the current DACL on the object we desire to set permissions on (can be a leaf object, OU, container, or directory partition).
  2. Append the existing DACL with a new ActiveDirectoryAccessRule.
  3. Re-apply the DACL.

The biggest hurdle is generally #2 when you have to create a ActiveDirectoryAccessRule object. Unfortunately, the constructor for this object takes schemaIDGUID and rightsGUID values to dictate what objects, attributes, and extended rights are being delegated. Below I’ll walk step-by-step through a sample script so you can see how to accomplish this task:

First I’ll get myself setup with references to some objects I’ll need later:

Import-Module ActiveDirectory
#Bring up an Active Directory command prompt so we can use this later on in the script
cd ad:
#Get a reference to the RootDSE of the current domain
$rootdse = Get-ADRootDSE
#Get a reference to the current domain
$domain = Get-ADDomain

 

Now, I want to create two hash tables to store the GUID values, but reference them by their display names. I don’t know about you, but I can never remember that 00299570-246d-11d0-a768-00aa006e0529 is the rightsGUID for allowing the forced changing of a user’s password. I can, however, remember “Reset Password” which is the displayName for this right. If you care to see the hash tables in the raw (or just need to find the display name reference"), simply type the variable names (e.g. $guidmap).

#Create a hashtable to store the GUID value of each schema class and attribute
$guidmap = @{}
Get-ADObject -SearchBase ($rootdse.SchemaNamingContext) -LDAPFilter `
"(schemaidguid=*)" -Properties lDAPDisplayName,schemaIDGUID | 
% {$guidmap[$_.lDAPDisplayName]=[System.GUID]$_.schemaIDGUID}

#Create a hashtable to store the GUID value of each extended right in the forest
$extendedrightsmap = @{}
Get-ADObject -SearchBase ($rootdse.ConfigurationNamingContext) -LDAPFilter `
"(&(objectclass=controlAccessRight)(rightsguid=*))" -Properties displayName,rightsGuid | 
% {$extendedrightsmap[$_.displayName]=[System.GUID]$_.rightsGuid}


Now that I have that in place, I can move forward with the actual delegation work. The below code allows the AD group named “Contoso Provisioning Admins” to create and delete user objects, modify their attributes, and reset their passwords. It also allows the AD group named “Contoso Service Desk” to reset user passwords in the top-level Contoso Users OU.

The tricky part comes when we have to create the ActiveDirectoryAccessRule that goes into the AddAccessRule method. This object has six different constructors and each can be used for a different use case.

#Get a reference to the OU we want to delegate
$ou = Get-ADOrganizationalUnit -Identity ("OU=Contoso Users,"+$domain.DistinguishedName)
#Get the SID values of each group we wish to delegate access to
$p = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup "Contoso Provisioning Admins").SID
$s = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup "Contoso Service Desk").SID
#Get a copy of the current DACL on the OU
$acl = Get-ACL -Path ($ou.DistinguishedName)
#Create an Access Control Entry for new permission we wish to add
#Allow the group to write all properties of descendent user objects
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
$p,"WriteProperty","Allow","Descendents",$guidmap["user"]))
#Allow the group to create and delete user objects in the OU and all sub-OUs that may get created
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
$p,"CreateChild,DeleteChild","Allow",$guidmap["user"],"All"))
#Allow the group to reset user passwords on all descendent user objects
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
$p,"ExtendedRight","Allow",$extendedrightsmap["Reset Password"],"Descendents",$guidmap["user"]))
#Allow the Service Desk group to also reset passwords on all descendent user objects
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
$s,"ExtendedRight","Allow",$extendedrightsmap["Reset Password"],"Descendents",$guidmap["user"]))
#Re-apply the modified DACL to the OU
Set-ACL -ACLObject $acl -Path ("AD:\"+($ou.DistinguishedName))

I’ll extend this example to another scenario;  the ability to link Group Policies Objects. This bit of PowerShell grants the “Contoso Group Policy Admins” AD group the ability to modify the gplink and gpoptions attributes across the entire domain (thus allowing them to link pre-existing Group Policy Objects to OUs.

#Provision the Group Policy Admins role
$acl = Get-ACL -Path ($rootdse.defaultNamingContext)
$p = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup ("Contoso Group Policy Admins")).SID
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
$p,"WriteProperty","Allow",$guidmap["gplink"],"All")) $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
$p,"WriteProperty","Allow",$guidmap["gpoptions"],"All")) Set-ACL -ACLObject $acl -Path ("AD:\"+($rootdse.defaultNamingContext))

 

As you can see, it’s not pretty but it is possible. If you get familiar with the .NET constructors used when creating the ActiveDirectoryAccessRule and the various enum values for ActiveDirectoryRights and ActiveDirectorySecurityInheritance, you could crank out your entire delegation structure with a few lines of PowerShell!

 
 
Comments (17)

  1. Joe Corey says:

    Thanks Thomas. Group Membership isn’t an extended right, it’s actually a property set which I didn’t get into. If you just wanted to delegate permission to just modify members of a group, the AccessRule would looks like: $acl.AddAccessRule((New-Object
    System.DirectoryServices.ActiveDirectoryAccessRule $s,"WriteProperty","Allow",$guidmap["member"],"Descendents",$guidmap["group"])) You can read more about that property set here: http://msdn.microsoft.com/en-us/library/ms684372.aspx. My post was misleading
    because the "Group Membership" property set is actually retrieved from the query I defined in the script for extended rights. In fact, the list of extended rights is much smaller (see here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms683985(v=vs.85).aspx).
    I’ll try to clarify that. If you did want to grant access using Property Sets, you could have done (notice the WriteProperty text instead of ExtendedRight). $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $s,"WriteProperty","Allow",$extendrightsmap["Group
    Membership"],"Descendents",$guidmap["group"]))

  2. Anonymous says:

    Hi,

    I followed your example but I’m stuck with "Access denied" error once I try to really ACL in last step. The OU I’m working on was created by me and using ADCU I’m able to change any permission, however scripted way is not working. Any idea?

  3. Nebuly says:

    Hello,

    Thanks for this great post, i also worked on AD delegation in the past and…well that's not that easy.

    It's not very well known, but Microsoft-provided cmdlets for AD delegation exists (which make this task much more easier to read and work with).

    Stuff like Get-ADPermission, Set-ADPermission…

    For some reasons the Exchange team has been monopolyzing them since years, so you'll have to install Exchange admin tools to grab Exchange Powershell module.

    Third-party stuff like Quest AD cmdlets will also do the trick.

  4. Joe Corey says:

    Full control can be granted with the "GenericAll" ActiveDirectoryRights enum. In the example you give, it would be:

    #Allow the group to write all properties of descendent user objects
    $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
    $p,"GenericAll","Allow","Descendents",$guidmap["user"]))

    See here for options:
    http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectoryrights(v=vs.110).aspx

  5. Marc says:

    Hi Joe,

    Thank you very, very much for this wonderful post. Thanks to you I can complete my fully automated forest creation!

  6. SUPRATIM says:

    Greate post.. It will be really helpful for all who manages AD.

  7. thomas says:

    Great article, has someone an idea how to delegate modifying the members of a group, which is located in an OU: Tried this, but didnt work: $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $s,"ExtendedRight","Allow",$extendedrightsmap["Group
    Membership"],"Descendents",$guidmap["group"]))

  8. thomas says:

    Awesome, thanks a lot for clarification. Works like a charm now 🙂

  9. Bob Hyatt says:

    Thanks for this, most helpful. How about full control on all descendant objects?

  10. thomas says:

    Still a great article 🙂

    Has someone an idea, how to set "full control" instead of "Write all properties"?

    #Allow the group to write all properties of descendent user objects
    $acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
    $p,"WriteProperty","Allow","Descendents",$guidmap["user"]))

  11. Sean says:

    Joe,

    I’ve been trying the steps to add an extended right (Send As) on Server 2008 R2, Server 2012 R2, and Windows 8.1 using both PowerShell 3.0 and PowerShell 4.0, and I keep getting the following error:
    New-Object : Cannot find an overload for "ActiveDirectoryAccessRule" and the argument count: "6".

    Any ideas what can be causing this?

  12. Steven says:

    Hi Sean,

    If you get an error like: New-Object : Cannot find an overload for "ActiveDirectoryAccessRule" and the argument count: "n". This probably means you have a syntax or variable error in your rule. I had the same issue and changed my syntax from:
    New-Object System.DirectoryServices.ActiveDirectoryAccessRule ("groupname","Readproperty, WriteProperty","Allow","Descendents","cg123aba-0de6-12d4-b297-00aa003050e2")

    to:

    New-Object System.DirectoryServices.ActiveDirectoryAccessRule ($grp,"Readproperty, WriteProperty","Allow","Descendents",[guid]"cg123aba-0de6-12d4-b297-00aa003050e2")

    Notice $grp & [guid] type identitier, System.DirectoryServices.ActiveDirectoryAccessRule cannot handle the strings.

    $grp is a System.Security.Principal.SecurityIdentifier

    If you are using the guid instead of the on found in the $guidmap = @{} than you’ll have to explicitely let PS know you are using a [guid].

  13. Curtiss says:

    i like your idea of hash tables, but I don’t see anything in your guidmap or extendedrightsmap hash tables for the "Write Members" property of AD groups. I’ve found the guid for that on other blog posts and TechNet articles, but it would be nice to have
    those properties available in a hash table as well.

  14. Curtiss says:

    Nevermind, I found your explanation of the differences between extended rights and properties. i guess i also needed an explanation of the difference between hash tables and arrays, because i was looking for the ‘members’ right with

    $guidmap | ? name -like "*member*"

    and of course not finding anything because it’s a hashtable.

  15. SRee says:

    can you help me delegate control join a computer to domain to a group please

  16. JasonBeckett says:

    This worked perfectly for me until the line:

    #Re-apply the modified DACL to the OU
    Set-ACL -ACLObject $acl -Path ("AD:"+($ou.DistinguishedName))

    I get an error of:
    Set-ACL : This security ID may not be assigned as the owner of this object
    At line:1 char:1
    + Set-ACL -ACLObject $acl -Path ("AD:"+($ou.DistinguishedName))

Skip to main content