Setting Active Directory Object permissions using powershell and System.DirectoryServices

There are times when you have the permission to set discretionary access control (DACL) entries, however you are not  a member of the domain administrators group.    However, when you use the System.DirectoryServices namespace, you are unable to write the DACL entries back to the Active Directory object.  By default, the Active Directory attempts to write the entire security descriptor back to an object.  If the security principal doing the write is not a member of the Domain Administrators group and the process doing the action is not elevated, NTFS security restrictions prevent the write request from completing.

 

In ADSI there is a way to instruct the server side Active Directory Security control to write only the DACL entries back to the AD by using the IADsObjectOptions interface. The IADsOpjectOptions interface is provided for this purpose.  It is exposed
through the DirectoryEntry object via the Options property ( wrapped by the DirectoryEntryConfiguration object ).  There is a very good MSDN magazine article that describes just how this process works:

msdn.microsoft.com/en-us/magazine/cc188700.aspx

The question not becomes how do we translate this article in powershell.  Its very straight forward, assume you have a collection of users returned from a DirectorySearcher request, the following foreach loop illustrates how to use Powershel and the S.DS namespace to modify the permissions on an object without having to be a member of the administrators group or running the powershell host as an elevated process: 

foreach ($user in $users)
{
   #
   # Retrieve the user object
   #
   $userName=[adsi]($user.path)
   #
   # setup the everyone deny ace
   #
   $Everyone = [System.Security.Principal.SecurityIdentifier]'S-1-1-0'
   $EveryoneDeny = New-Object System.DirectoryServices.ActiveDirectoryAccessRule ($Everyone, 'Extendedright', 'Allow', [GUID]'ab721a53-1e2f-11d0-9819-00aa0040529b')
   #
   # Setup the self Deny Ace
   #
   $Self = [System.Security.Principal.SecurityIdentifier]'S-1-5-10'
   $SelfDeny = New-Object System.DirectoryServices.ActiveDirectoryAccessRule ($Self,'Extendedright', 'Deny', [GUID]'ab721a53-1e2f-11d0-9819-00aa0040529b')
   #
   # --- Apply the Settings to the User
   # before we retrieve the SD, tell the LDAP head we only want the DACL
   # information by setting the DirectoryEntryConfiguration.SecurityMasks to just the Dacl
   #
   #
 [System.DirectoryServices.DirectoryEntryConfiguration]$SecOptions = $userName.get_Options();
   $SecOptions.SecurityMasks = [System.DirectoryServices.SecurityMasks]'Dacl'
   #
   # Basically, the above code calls the IADsSetObjectOptions::SetOption method
   # with the ADS_OPTION_ENUM.ADS_OPTION_SECURITY_MASK
   # and ADS_SECURITY_INFO_ENUM.ADS_SECURITY_INFO_DACL value
   #
   # We've set the security options to 
   # IADsSetObjectOptions::SetOption documentation:
   # msdn.microsoft.com/en-us/library/windows/desktop/aa706061(v=vs.85).aspx
   # 
   # Now, retrieve the SecurityDescriptor and add the access rules
   #                       
   $userName.get_ObjectSecurity().AddAccessRule($SelfDeny)
   $userName.CommitChanges()
   $userName.get_ObjectSecurity().AddAccessRule($EveryoneDeny)
   #
   # Write just the DACL back to the AD:
   #
  $userName.CommitChanges()
}

 

Just that simple.  Its is important to note, that you will need to set the security control before you request the security information from the DirectoryEntry object.  This will properly set the security control to write only the information you have requested.