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!