•
•
•
•
•
•

Updated with additional requirements and scenarios, 2017-10-26.

I recently worked with a customer that needed assistance in configuring the additional permissions required for AAD Connect delegation.  After chasing down an incredible number of prerequisite information, I decided it would be more helpful to my customer to put together a tool that would help them configure the various permissions delegations.

AAD Connect supports several different write-back scenarios to the on-premises environment:

In addition, the tool supports configuration of the special permissions required to enable Password Hash Sync.

While many organizations have previously configured AAD Connect to use an account that is a member of Domain Admins or Enterprise Admins, it’s not a best security practice.  However, for enabling password hash sync to the cloud or write data back to the on-premises environment, the AAD Connect service account needs to have the ability to extract the necessary information from the on-premises directory and update on-premises attributes.

Enter the tool.

With this tool, you can:

• Grant the AAD Connect service account delegate permissions over objects in specific OUs for writeback
• Initialize the environment for device writeback
• Grant the AAD Connect service account  ‘Replicating Directory Changes’ and ‘Replicating Directory Changes All’ permissions for password hash sync

Some organizations also require additional write permissions for the adminSDHolder container (for example, if they have objects currently or previously protected by adminSDHolder that are being synchronized to Office 365) and the ms-DS-ConsistencyGuid property, so those can also be updated/modified using this script.

# About the tool

After you’ve downloaded the tool, launch an elevated PowerShell prompt on your AAD Connect server.  For most organizations, you’ll run this from your AAD Connect server, which should have the AAD Connect modules installed (shocker, I know) and the MSOnline module.

## Requirements

• Device WriteBack     If you are using this tool to configure DeviceWriteback, the MSOnline module is required.
• Password WriteBack     If you are using this tool to configure Password Reset (password writeback), the ADSync module (installed with AAD Connect) is recommended, as it is used to determine the Azure AD Connect connector and update it.
• Exchange Hybrid WriteBack, Password WriteBack, Groups WriteBack, msDS-ConsistencyGuid updating, adminSDholder modification, Password Hash Sync     For any of these features, you must have the Active Directory Remote Server Administration Tools (RSAT) installed–the script primarily relies on DSACLS.  You can install it by running Install-WindowsFeature RSAT-ADDS from an elevated PowerShell prompt.

## Multiple Domain Support

In the past few weeks, I’ve talked with several people attempting to use this tool in a mutli-domain (parent/child) environment.

You can run it one of two ways:

• All from the AAD Connect server and using the parameters –ExchangeHybridWriteBackOUs, –PasswordWriteBackOUs, and –GroupWriteBackOU (optionally, using -Domain and -User to specify the service accounts you’ll use for each AD connector, if you’re not using the default AAD Connect service account).
• From a DC in the child OU that you’re attempting to configure permissions on

If you are running the -PasswordWriteBack in a child domain on a server that DOES NOT have AAD Connect, you will get a warning that you need to update the AAD Connector manually.

# Parameters

Any good tool has parameters, and this one is no different. 😉

## DeviceWriteBack

Use this parameter to configure Device Write Back. Using this parameter will require use of the AAD Connect modules, so AAD Connect must already be installed.  DeviceWriteBack also requires a particular version of the MSOnline cmdlets.  If you don’t have them installed, it will attempt to go download them.

In order to make this work correctly across multiple platforms, I decided to use PowerShellGet to support the installation.  The latest Windows Azure AD v1 cmdlets are available as a download that way, so it seemed to make sense to try to use it.  Also, I tried to find the URL from the download.connect.microsoft.com page, but we apparently now generate a token of some sort, rendering any download link invalid after a short period of time.  So, there’s that, too.

This option will download and install the Microsoft Online Services Sign-In Assistant first (since it’s a requirement), then proceed to install PowerShellGet and NuGet, and then finally, use PowerShellGet to download and install the MSOnline Module.  I go through a tremendous amount of effort to install stuff you should already have.

If (!(Get-Module -ListAvailable MSOnline -ea silentlycontinue))
{
Write-Host -ForegroundColor Yellow "This requires the Microsoft Online Services Module. Attempting to download and install."
wget https://download.microsoft.com/download/5/0/1/5017D39B-8E29-48C8-91A8-8D0E4968E6D4/en/msoidcli_64.msi -OutFile $env:TEMP\msoidcli_64.msi If (!(Get-Command Install-Module)) { wget https://download.microsoft.com/download/C/4/1/C41378D4-7F41-4BBE-9D0D-0E4F98585C61/PackageManagement_x64.msi -OutFile$env:TEMP\PackageManagement_x64.msi
}
msiexec /i $env:TEMP\msoidcli_64.msi /quiet /passive msiexec /i$env:TEMP\PackageManagement_x64.msi /qn
Install-PackageProvider -Name Nuget -MinimumVersion 2.8.5.201 -Force -Confirm:$false Install-Module MSOnline -Confirm:$false -Force
If (!(Get-Module -ListAvailable MSOnline))
{
Break
}
}

Whew! That was painful.

## Domain

Used to specify the NetBIOS domain name for the AAD Connect service account.  If this parameter is omitted, the current NetBIOS domain name is used.

I found I needed this parameter when I encountered a testing scenario where I had deployed AAD Connect on a DC–since I could validate the user using Get-ADUser, I had no need to look elsewhere.  Except for those other 99% of the use cases where AAD Connect is deployed on a member server.  If you don’t specify the user account, this parameter is ignored.

## ExchangeHybridWriteBack

Use this switch parameter to set the permissions for Exchange Hybrid WriteBack.  Using my good friend DSACLS, I trudge through whatever OUs have been specified in ExchangeHybridWriteBackOUs.  Depending on your Exchange Schema version, you may have different attributes updated.  Exchange 2016 introduced the usage of msDS-ExternalDirectoryObjectID, so write permissions are given to that property as well.

The Exchange schema version is determined through this wizardry:

$Schema = (Get-ADRootDSE).SchemaNamingContext$Value = "CN=ms-Exch-Schema-Version-Pt," + $Schema$ExchangeSchemaVersion = (Get-ADObject $Value -pr rangeUpper).rangeUpper There are three potential ranges that we need to be concerned with, where$ExchangeSchemaVersion is:

• less than 14734     This indicates that the environment’s schema is less than Exchange 2010 SP3.  If an Exchange Schema less than 14734 is detected, the script will exit.
• greater than 15316    This schema version indicates Exchange 2016 or later.
• anything else     Any version of Exchange 2013 or any version of Exchange 2010 later than SP3

## ExchangeHybridWriteBackOUs

Use this parameter to specify target OUs to enable the service account writeback permissions. If this parameter is omitted, access is granted at the domain root.  The parameter is typed as an array, so you can enter one or more OUs in the standard array formats — @(‘obj1′,’obj2’) or ‘obj1′,’obj2’.

OUs must be specified in the “OU=Child,OU=Parent,DC=domain,DC=com.”  The format is validated using the following regular expression:

'^(?i)(ou=)[a-zA-Z\d\=\, \-]*(,dc\=\S*,dc=\S*)'

I was previously using ‘^(ou=)[a-zA-Z\d\=\, ]*(,dc\=\w*,dc=\w*)’, but a colleague pointed out that it was failing on domains that had hyphens, so I modified the filter with the 2017-10-19 update.

That’s the best that I could come up with–if you have a better one, I’d love to try it out.

OUs are also validated for whether or not the domain is valid.  The method that I use to do it is:

    Foreach ($OUPath in$OUs)
{
[array]$OUSplit =$OUPath.Split(",")
foreach ($obj in$OUSplit)
{
If ($obj -like "DC=*") {$OUVer += $obj + "," } }$OUVer = $OUVer.TrimEnd(",").ToString() If (!(Test-Path "AD:\$OUVer" -ErrorAction SilentlyContinue))
{
$BadPaths +=$OUVer
}
$OUVer =$null
}

That way, I can verify if the DC=domain,DC=tld actually exists in your environment and then tell you otherwise.

I haven’t put anything in to check if they’re actually a valid OU structure yet.  It’s not that I can’t–just that I didn’t put it in yet.  Gotta save something for the updates, right?

## Forests

If you have more than one forest in your AAD Connect topology, you can use the Forests parameter to specify them for device writeback.  You must be logged on with an account that has enterprise admin privileges in the target forests.  Nuff said.

## GroupWriteBack

Use this switch parameter to configure Office 365 Group writeback permissions.  It uses GroupWriteBackOU if the parameter is specified; otherwise, it defaults to the value in AD connector.  If no container is specified and Office 365 group writeback has not been configured in AAD Connect, the script will exit.

## GroupWriteBackOU

Use this parameter to specify the Office 365 Groups writeback container.  If the container exists, the account specified will be granted access.  If the GroupWriteBackOU is specified and does not exist, it will be created (as long as the DN is formatted correctly and references a valid domain).

While the checking part was pretty easy, the process of creating a nested OU was not.  At least, not until I stumbled upon the [array]::Reverse method.  Using that, I was able to iteratively march through the array elements.

[array]$OuPath =$GroupWriteBackOU.Split(",")
[array]::Reverse($OuPath)$OuDepthCount = 1
foreach ($obj in$OuPath)
{
If ($OuDepthCount -eq 1) {$Ou = $obj # Do nothing else, since Test-Path will return a referral error when querying the very top level } Else { Write-Host Current item is$obj
$Ou =$obj + "," + $Ou If (!(Test-Path AD:\$Ou))
{
Write-Host -ForegroundColor Green "     Creating OU ($($Ou)) in path."
New-Item "AD:\$Ou" -ItemType OrganizationalUnit } }$OuDepthCount++
}

What’s happening here:

1. The value that’s passed to $GroupWriteBackOU is in the form “OU=abc,OU=def,DC=domain,dc=com.” Using the Split method, I separate each of those elements into a new array element inside the array, so it ends up looking like this: OU=abc OU=def DC=domain DC=com 2. Then, the magic of [array]::Reverse comes into play, and switches the order of the elements around, so that it now looks like this: DC=com DC=domain OU=def OU=abc No, I didn’t lie. It’s exactly reversed. 3. In the next section, I start creating an DN path that I can validate using Test-Path AD:\$OU.  The sticky wicket is that if you try to do Test-Path “AD:\DC=com”, you get this lovely error:
test-path : A referral was returned from the server
At line:1 char:1
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (DC=com:String) [Test-Path], ADReferralException
+ FullyQualifiedErrorId : ADProvider:ItemExists::ADError,Microsoft.PowerShell.Commands.TestPathCommand

Helpful, right? So, I put a counter in there for each iteration of the array, and if it’s the first pass, we skip the Test-Path command.

4. Finally, if the path tests as $False, then I create a new OU and continue the iteration until the end of the array. I discovered some *really* interesting things about checking for the presence of certain paths. For example, Get-ADObject and Get-ADOrganizationalUnit return hard errors when the target object isn’t found. You can’t hide them inside a variable ($obj=Get-ADOrganizationalUnit $OU). It ignores both -ErrorAction SilentlyContinue AND piping to Out-Null, which makes for an ugly script. Test-Path AD:\$Ou was the answer to this problem.  However, if you just walk through the tree attempting to query for DC=com, you get the referral error I mentioned previously.  So you have to skip that one.  Can’t ever be easy.

I’ll move the OU checking code into the ExchangeHybridWriteBackOUs validation and return an error if the OU(s) specified don’t exist.

Eventually.

## msDSConsistencyGuid

Use this parameter to grant the service account user write permissions for msDS-ConsistencyGuid.  Yes, it’s spelled differently depending on which interface you use.  Don’t shoot the messenger.

Use this parameter to set ‘Replicating Directory Changes’ and ‘Replicating Directory Changes All’ permissions.  This is actually the easiest bit of code to implement, since it’s only two DSACLS commands for configuring the permissions:

If ($PasswordHashSync) {$RootDSE = Get-ADRootDSE
$DefaultNamingContext =$RootDSE.defaultNamingContext
$ConfigurationNamingContext =$RootDSE.configurationNamingContext

$cmd = "dsacls '$DefaultNamingContext' /G '"$User":CA;"Replicating Directory Changes";'n"$cmd += "dsacls '$DefaultNamingContext' /G '"$User":CA;"Replicating Directory Changes All";'n"
Invoke-Expression $cmd | Out-Null } ## PasswordWriteBack Use this parameter to enable password writeback. It can read values from various places–the ExchangeHybridWriteBackOUs or PasswordWriteBackOUs parameters if specified. The PasswordWriteBackOUs parameter takes precedence (so the ExchangeHybridWriteBackOUs parameter will be ignored). Otherwise, it sets permissions at domain root. ## PasswordWriteBackOUs Use this parameter to specify target OUs to enable the service account writeback permissions. If this parameter is specified in conjunction with the parameter ExchangeHybridWriteBackOUs, this parameter will take effect. If this parameter is omitted but the ExchangeHybridWriteBackOUs parameter is specified, PasswordWriteBack will use the ExchangeHybridWriteBackOUs values. If neither parameter is supplied, then permissions will be delegated at the domain root. ## TenantCredential Use this parameter to specify the tenant credential used when returning domains from Office 365 for Device WriteBack. If DeviceWriteBack is not selected, this parameter is not used. ## TenantID Use this parameter to specify the tenant GUID used when configuring Device WriteBack. If the DeviceWriteBack parameter is not specified, this parameter is not used. ## UpdateAdminSDHolder If you have objects protected by adminSDHolder, you can use this switch to allow write-back delegation for those objects. If an object is a member of a group protected by adminSDHolder and the AAD Connect service account is not a member of Domain Admins or Enterprise Admins, Exchange Hybrid WriteBack and Password WriteBack may not work. When you use this switch, the container calculated by “CN=AdminSDHolder,CN=System,” + ((Get-ADDomain).DistinguishedName) is added to the WriteBackOUs array and then processed with the DSACLS command. ## User Specify the AAD account that will be granted permissions. If no account is specified, attempt to locate the account through the connector properties. If for some reason it can’t figure out the account, it will exit (since you can’t grant permissions to an account that doesn’t exist). Duh. Extracting the data from AAD Connect was actually a little more involved than I would have thought. The user account information for the connector is not actually stored in the connector properties. Why? Ask the product group. Instead, you can find the information located inside a backup of the AAD Connect configuration. But where do you get that? I’m so glad you asked. You can use Get-ADSyncServerConfiguration to dump the configuration. Then, you have to grab the connector ID for the on-premises AD connector, find the XML file related to that connector, and then look in following node: [xml]$ConnectorXMLData = gc <xmlfile.xml>
\$ConnectorXMLData.'ma-data'.'private-configuration'.'adma-configuration'.'forest-login-user'

If you don’t specify the Domain parameter, the script will also examine the XML to find the NetBIOS domain of the user (stored in ma-data.private-configuration.adma-configuration.forest-login-domain).  For the record, the PG has said they don’t support the Get-ADSyncServerConfiguration cmdlet.  So, try not to expect too much out of it.

## VerifiedDomain

Specify a verfied domain for your AAD tenant instead of discovering a domain automatically.  This is only valid for configuring device writeback.  If you don’t specify this parameter for configuring device writeback, the script will connect to AAD and return the first valid verified domain.  If the DeviceWriteBack parameter isn’t specified, this parameter will be ignored.

I think that’s all the bells and whistles for now.

If you didn’t already see the link, it’s on the TechNet Gallery at https://gallery.technet.microsoft.com/AD-Advanced-Permissions-49723f74.

Helping companies conquer inferior technology since 1997. I spend my time developing and implementing technology solutions so people can spend less time with technology. Specialties: Active Directory and Exchange consulting and deployment, Virtualization, Disaster Recovery, Office 365, datacenter migration/consolidation, cheese.

1. Joshua Bines says:

Hey Aaron,

Thanks for sharing the script!! I just wanted to check the Exchange hybrid switch also includes all the values for public folders?

1. Aaron Guilmette says:

There aren’t any write-back permissions. By default, the Authenticated Users group has READ permissions to the Microsoft Exchange System Objects container, so no additional changes should be necessary.

1. Joshua Bines says:

Cheers mate, makes sense sorry I should have checked that! Perhaps I should READ more closely next time 🙂

2. mombu says:

Can the accounts you want to delegate permissions to be a gmsa account?

1. Aaron Guilmette says:

I’ve not tried it specifically, but since the gMSA is a still a user account, it should work. If your gMSA has a dollar sign in it (as part of the username), you’ll need to escape it with a backtick (` character) when you specify it.

3. Jeremy says:

From what I have been able to gather and test, the correct command for the AdminSDHolder permission should be:

This will allow it to be written to any object type and the permissions will not propagate.

1. Aaron Guilmette says:

I’m not sure I understand why you think it should change. Did you experience an issue? It’s a container-level permission, meaning, objects that are related to the container will inherit the permissions. The default ACL / propagation method is inherit on the objects, as you can see when you run:

There aren’t any permissions listed for “this object only.” All permissions listed in a default forest are “inherited to all subobjects.” A brief sample:

Permissions inherited to subobjects are:
Inherited to all subobjects
Allow forestc\Organization Management
SPECIAL ACCESS
LIST CONTENTS
LIST OBJECT
Allow forestc\Exchange Trusted Subsystem
SPECIAL ACCESS
LIST CONTENTS
LIST OBJECT
Allow forestc\Organization Management
FULL CONTROL for msExchDynamicDistributionList
Allow forestc\Exchange Trusted Subsystem
FULL CONTROL for msExchDynamicDistributionList
Allow forestc\Delegated Setup SPECIAL ACCESS for Account Restrictions
Allow forestc\Exchange Servers SPECIAL ACCESS for Exchange Personal Information
Allow forestc\Exchange Servers SPECIAL ACCESS for canonicalName
Allow forestc\Exchange Servers SPECIAL ACCESS for userAccountControl
Allow forestc\Exchange Servers SPECIAL ACCESS for Exchange Information
Allow forestc\Exchange Servers SPECIAL ACCESS for memberOf
Allow forestc\Exchange Servers SPECIAL ACCESS for garbageCollPeriod
Allow forestc\Organization Management
SPECIAL ACCESS for proxyAddresses
WRITE PROPERTY
Allow forestc\Exchange Trusted Subsystem
SPECIAL ACCESS for proxyAddresses
WRITE PROPERTY
Allow forestc\Organization Management
SPECIAL ACCESS for showInAddressBook
WRITE PROPERTY
Allow forestc\Exchange Trusted Subsystem
SPECIAL ACCESS for showInAddressBook
WRITE PROPERTY

The permissions applied to adminSDHolder are used as a template to be applied to protected objects, so, they need to be “inherited.” Further reference:

4. Jeremy says:

Are you sure the AdminSDHolder permissions are correct? As added they apply to “Descendent User Objects” on the User object in question. I thought they should apply to This Object.

Also when using the latest default option for AADConnect it is trying to write back the msDS-ConsistencyGUID even without password writeback.

1. Aaron Guilmette says:

Yes, I believe in the updated versions of AAD Connect that msDS-ConsistencyGuid is now used by default.

This site uses Akismet to reduce spam. Learn how your comment data is processed.