[Scripts updated 5th October 2017 to support updates for Exchange Hybrid Writeback. If you ran earlier versions of these scripts you will need to run them again]
AdminSDHolder is something I come across a lot, but find a lot of admins are unaware of it. In brief it is any user that is a member of a protected group (i.e. Domain Admins) will find that their AD permission inheritance and access control lists on their AD object will be reset every hour. Michael B. Smith did a nice write-up on this subject here.
AdminSDHolder is an AD object that determines what the permissions for all protected group members need to be. Why this matters with AADConnect and your sync to Azure Active Directory (i.e. the directory used by Office 365) is that any object that the AADConnect service cannot read cannot be synced, and any object that the AADConnect service cannot write to can be targeted by writeback permissions. This blog post was last updated 18th June 2017 in advance of the release of AADConnect version 1.1.553.0.
For the read permissions this is less of an issue, as the default read permissions by every object is part of a standard Active Directory deployment and so you will find that AdminSDHolder contains this permission and therefore protected objects can be read by AADConnect. This happens in reality becase Authenticated Users have read permissions to lots of attributes on the AdminSDHolder object under the hidden System containing in the domain. Unless your AD permissions are very locked down or AdminSDHolder permissions have been changed to remove Authenticated Users you should have no issue in syncing admin accounts, who of course might have dependencies on mailboxes and SharePoint sites etc. and so need to be synced to the cloud.
Writeback though is a different ball game. Unless you have done AADConnect with Express settings you will find that protected accounts fail during the last stage of AADConnect sync process. You often see errors in the Export profile for your Active Directory that list your admin accounts. Ofter the easiest way to fix this is to enable the Inheritance permission check box on the user account and sync again. The changes are now successfully written but within the hour this inheritance checkbox will be removed and the default permissions as set on AdminSDHolder reapplied to these user accounts. Later changes that need written back from the cloud will result in a failure to writeback again, and again permission issues will be to blame.
To fix this we just need to ensure that the AdminSDHolder object has the correct permissions needed. This is nothing more than doing what the AADConnect Express wizard will do for you anyway, but if you don’t do the Express wizard I don’t think I have seen what you should do documented anywhere – so this is the first (maybe).
Often if you don’t run Express settings you are interested in the principal of least privilege and so the rest of this blog post will outline what you will see in your Active Directory and what to do to ensure protected accounts will always sync and writeback in the Azure Active Directory sync engine. I covered the permissions to enable various types of writeback permissions in a different blog post, but the scripts in this post never added the correct write permissions to AdminSDHolder, so this post will cover what to do for your protected accounts.
First, take a look at any protected account (i.e. one that is a member of Domain Admins):
You will see in the Advanced permissions dialog that their is an “Enable Inheritance” button (or a check box is unchecked in older versions of Active Directory. You will also notice that all the permissions under the “Inherited From” column read “None” – that is there are no permissions inherited. You will also see, as shown in the above dialog, that if Express settings have been run for your AADConnect sync service that a access control entry for the AADConnect service account will be listed – here this is MSOL_924f68d9ff1f (yours will be different if it exists) and has read/write for everything. This is not least privilege! If you have run the sync engine previously on different servers and later removed them (as the sync engine can only run on one server to one AAD tenant, excluding staging servers) then you might see more than one MSOL account. The description field of the account will show what server it was created on for your information.
If you compare your above admin account to a non-protected account you will see inheritance can be disabled and that the Inherited From column lists the source of the permission inheritance.
Compare the access control entries (ACE) to the list of ACE’s on the AdminSDHolder object. AdminSDHolder can be found at CN=AdminSDHolder,CN=System,DC=domain,DC=local. You should find that the protected accounts match those of the AdminSDHolder, or at least will within the hour as someone could have just changed something.
Add a permission ACE to AdminSDHolder and it will appear on each protected account within an hour, remove an ACE and it will go within the hour as well. So you could for example remove the MSOL_ account(s) from older ADSync deployments and tidy up your permissions as well.
This is what my Advanced permissions for AdminSDHolder looks like on my domain
If I add the relevant ACE’s here for the writeback permissions then within the hour, and then for syncs that happen after that time, the errors for writeback in the sync management console will go away. Note though that AdminSDHolder is per domain, so if you are syncing more than one domain you need to set these permissions on each domain.
To script these permissions, run the following in PowerShell to update AD permissions regarding to the different hybrid writebacks scenarios that you are interested in implementing.
Finding All Your AdminSDHolder Affected Users
The following PowerShell will let you know all the users in your domain who have an AdminCount set to 1 (>0 in reality), which means they are impacted by AdminSDHolder restrictions. The changes below directly on the AdminSDHolder will impact these users as their permissions will get updated to allow writeback from Azure AD.
get-aduser -Filter {admincount -gt 0} -Properties adminCount -ResultSetSize $null | FT DistinguishedName,Enabled,SamAccountName
SourceAnchor Writeback
This setting is needed for all installations since version 1.1.553.0.
$accountName = "domain\aad_account" #[this is the account that will be used by Azure AD Connect Sync to manage objects in the directory, this is an account usually in the form of AAD_number or MSOL_number]. $AdminSDHolder = "CN=AdminSDHolder,CN=System,DC=contoso,DC=com" $cmd = "dsacls.exe '$AdminSDHolder' /G '`"$accountName`":WP;ms-ds-consistencyGuid'" Invoke-Expression $cmd | Out-Null
Password Writeback
The following PowerShell will modify the permissions on the AdminSDHolder object so that protected accounts can have Self Service Password Reset (SSPR) function against the accounts. Note you need to change the DC values in the script for it to function against your domain(s).
Note that if you implement this, I recommend that you use version 1.1.553 or later, as that version restricts rogue Azure AD admins from resetting other Active Directory admins passwords and then taking ownership of the Active Directory account. Often Azure AD admins have admin rights in AD, and so this was always possible independent of AADConnect, but versions of AADConnect prior to 1.1.553 would allow an Azure AD admin to reset a restricted AD account that they did not own.
To determine the account name that permissions must be granted to, open the Synchronization Service Manager on the sync server, click Connectors and double click the connector to the domain you are updating. Under the Connect to Active Directory Forest item you will see the Forest Name and User Name. The User Name is the name of the account you need in the script. An example is shown below:
$accountName = "domain\aad_account" #[this is the account that will be used by Azure AD Connect Sync to manage objects in the directory, this is an account usually in the form of AAD_number or MSOL_number]. $AdminSDHolder = "CN=AdminSDHolder,CN=System,DC=contoso,DC=com" $cmd = "dsacls.exe '$AdminSDHolder' /G '`"$accountName`":CA;`"Reset Password`"'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls.exe '$AdminSDHolder' /G '`"$accountName`":CA;`"Change Password`"'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls.exe '$AdminSDHolder' /G '`"$accountName`":WP;lockoutTime'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls.exe '$AdminSDHolder' /G '`"$accountName`":WP;pwdLastSet'" Invoke-Expression $cmd | Out-Null
Note: Take care with this permission. Though Express Mode does this, there is a possible scenario where writeback of an AD admin password change could be misused. Versions of AADConnect released on 2018 specifically block this functionality where the user is changing a different admin user. These permissions allow the current user to make this change to their own account.
Exchange Hybrid Mode Writeback
The below script will set the permissions required for the service account that AADSync uses. Note that if Express mode has been used, then an account called MSOL_AD_Sync_RichCoexistence will exist that has these permissions rather than being assigned directly to the sync account. Therefore you could change the below permissions to utilise MSOL_AD_Sync_RichCoexistence rather than AAD_ or MSOL_ and achieve the same results, but knowing that future changes to the MSOL_ or AAD_ account will be saved as it was done via a group.
The final permission in the set is for msDS-ExternalDirectoryObjectID and this is part of the Exchange Server 2016 (and maybe Exchange Server 2013 later CU’s) schema updates. Newer documentation on AAD Connect synchronized attributes already has this attribute listed, for example in Azure AD Connect sync: Attributes synchronized to Azure Active Directory
$accountName = "domain\aad_account" $AdminSDHolder = "CN=AdminSDHolder,CN=System,DC=contoso,DC=com" $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;proxyAddresses'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchUCVoiceMailSettings'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchUserHoldPolicies'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchArchiveStatus'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchSafeSendersHash'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchBlockedSendersHash'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchSafeRecipientsHash'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msDS-ExternalDirectoryObjectID'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;publicDelegates'" Invoke-Expression $cmd | Out-Null $cmd = "dsacls '$AdminSDHolder' /G '`"$accountName`":WP;msExchDelegateLinkList'" Invoke-Expression $cmd | Out-Null
Once these two scripts are run against AdminSDHolder object and you wait an hour, the permissions will be applied to your protected accounts, then within 30 minutes (based on the default sync time) any admin account that is failing to get cloud settings written back to Active Directory due to permission-issue errors will automatically get resolved.
Leave a Reply