I have an OU in AD called SQL. I have delegated full control of the OU to a user called sqladmin.

If I log in to a member server as sqladmin, I can use Active Directory Users and Computers to create two different computer objects, AG and Cluster. I can use ADUC to to set the security on AG computer object such that the Cluster computer object has full control.

However, if I try to do this with PowerShell by getting the current ACL off of AG and adding an ACE, I get an Access Denied error when I try to set the ACL on the AG Computer object.

Here is my code

$AG = Get-ADComputer AG
$cluster = Get-ADComputer cluster

$AGDistinguishedName = $AG.DistinguishedName  # input AD computer distinguishedname
$AGacl = Get-Acl "AD:\$AGDistinguishedName"

$SID = [System.Security.Principal.SecurityIdentifier] $cluster.SID

$identity = [System.Security.Principal.IdentityReference] $SID
$adRights = [System.DirectoryServices.ActiveDirectoryRights] "GenericAll"
$type = [System.Security.AccessControl.AccessControlType] "Allow"
$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "None"

$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights,$type,$inheritanceType


Set-Acl -path "AD:\$AGDistinguishedName" -AclObject $AGacl

However, If I log in as a domain admin and run the code, it works fine. The code only fails when I log in as the sqladmin user. However, I can perform the task with sqladmin using the GUI.

I can also confirm that if I use the GUI, the ACE that is created has the Generic All type for CLUSTER$ and it matches what I am trying to do in PowerShell. I need to be able to update the ACL with the user account that has been delegated access to the OU with PowerShell.

Here is the error I am seeing when I try to do this with PowerShell.

System.UnauthorizedAccessException: Access is denied ---> 
                        System.ServiceModel.FaultException: The operation failed due to insufficient access rights.
                           --- End of inner exception stack trace ---
                        extendedErrorMessage, Exception innerException)
                           at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowExceptionForErrorCode(String 
                        message, String errorCode, String extendedErrorMessage, Exception innerException)
                        faultDetail, FaultException faultException)
                           at Microsoft.ActiveDirectory.Management.AdwsConnection.ThrowException(AdwsFault 
                        adwsFault, FaultException faultException)
                           at Microsoft.ActiveDirectory.Management.AdwsConnection.Modify(ADModifyRequest request)
                           at Microsoft.ActiveDirectory.Management.ADWebServiceStoreAccess.Microsoft.ActiveDirectory.
                        Management.IADSyncOperations.Modify(ADSessionHandle handle, ADModifyRequest request)
                           at Microsoft.ActiveDirectory.Management.ADActiveObject.Update()
                           at Microsoft.ActiveDirectory.Management.Provider.ADProvider.SetSecurityDescriptor(String 
                        path, ObjectSecurity securityDescriptor)

I think this might have to do with how Get-Acl works under the hood. If I recall correctly, it retrieves both the DACL (which you want) and the SACL (which you don't want) of the object. Your sqladmin user only has permissions to modify the DACL. And when you use Set-Acl with your modified object, it tries to write the whole object including the SACL even though it hasn't changed. And since you don't have access, you get the access denied.

There's a related question here that has a workaround for dealing with permissions on filesystem objects. But the GetAccessControl() method doesn't exist on AD objects.

However, the AD object has its own set of methods that you can use as an alternative. One of them is ModifyAccessRule. Here's a modification of your code tweaked to use it.

# grab the data you need from the AD objects
$AG = Get-ADComputer AG
$AGDN = $AG.DistinguishedName  # input AD computer distinguishedname
$cluster = Get-ADComputer cluster
$SID = [System.Security.Principal.SecurityIdentifier] $cluster.SID

# create the ACE you want to add
$identity = [System.Security.Principal.IdentityReference] $SID
$adRights = [System.DirectoryServices.ActiveDirectoryRights] "GenericAll"
$type = [System.Security.AccessControl.AccessControlType] "Allow"
$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "None"
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $identity,$adRights,$type,$inheritanceType

# get an ADSI reference to the AD object we're going to tweak
$AGADSI = [adsi]"LDAP://$AGDN"

# we need an existing boolean output variable for the function
$modified = $false

# call the function and commit the changes

# you could/should check the value of $modified to make sure it's True before doing the
# commit. But hypothetically the only thing that would screw it up is if you botched the
# ACE creation.
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.