AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

  •  
  •  
  •  
  •  
  •  
  •  

In part 1 of our adventure, we built an Azure AD lab to support configuring AAD Connect to work as a GalSync engine. In this post, we’ll finish up the configuration.  As a reminder, this is the what the overall solution will look like:

AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

And, as I mentioned in part 1:

Please don’t call Premier asking for support on this. They will hunt me down and give me a stern talking to.  As any custom solution is, this is also unsupported.  While it does work and only utilizes built-in connectors, AAD Connect (like Office 365) is an evergreen product, so there is potential that the steps outlined here may someday cease to work or functionality may be deprecated. This works with the build version of AAD Connect current as of this writing: 1.1.882.0 (September 8, 2018)

Our story thus far

We definitely got a lot done in the last post:

  • Starting 2 Office 365 Enterprise Trials, each representing a separate organization
  • Setting up 3 virtual networks and network security groups
  • Deploying 3 virtual machines into the network security groups
  • Configuring each of those virtual machines as a new forest
    • Two of the forests will be account forests, representing our two separate organizations
    • One of the forests will be a resource forest, which will act as a staging location for shared global address list
  • Provisioning 5,000 unique users accounts in each of the account forests
  • Running the AAD Connect Network Testing Tool to verify that our two account forests can communicate with Office 365
  • Running the AAD Connect installation in Express mode to configure our account forests to sync to their respective Office 365 tenants

Now that we’ve got the foundation laid, we’re going to start configuring our environments to talk to each other and hopefully end up with 5,000 new contacts in each tenant organization.

Create Dns Conditional Forwarding Zones

As I stated in the original solution description, we’re going to leverage the default Active Directory connectors.  The AD connector requires AD DNS SRV record lookups to be successful, so in order to make that happen, we’re going to create some conditional forwarding zones.  We need to be able to resolve the shared or resource forest from both of the account forests.  To achieve this, we will use PowerShell.  As a reminder, our network configuration:

GalSyncTenantA, IP Range 10.0.0.0/24, DC IP: 10.0.0.4, NAT IP: 137.117.58.26

C:\>Get-ADForest
ApplicationPartitions : {DC=ForestDnsZones,DC=gstenanta,DC=local, DC=DomainDnsZones,DC=gstenanta,DC=local}
CrossForestReferences : {}
DomainNamingMaster : GSTA-DC.gstenanta.local
Domains : {gstenanta.local}
ForestMode : Windows2012R2Forest
GlobalCatalogs : {GSTA-DC.gstenanta.local}
Name : gstenanta.local
PartitionsContainer : CN=Partitions,CN=Configuration,DC=gstenanta,DC=local
RootDomain : gstenanta.local
SchemaMaster : GSTA-DC.gstenanta.local
Sites : {Default-First-Site-Name}
SPNSuffixes : {}
UPNSuffixes : {galsynctenanta.onmicrosoft.com

GalSyncTenantB, IP Range 10.0.1.0/24, DC IP: 10.0.1.4, NAT IP: 168.62.181.187

C:\>Get-ADForest
ApplicationPartitions : {DC=ForestDnsZones,DC=gstenantb,DC=local, DC=DomainDnsZones,DC=gstenantb,DC=local}
CrossForestReferences : {}
DomainNamingMaster : GSTB-DC.gstenanta.local
Domains : {gstenantb.local}
ForestMode : Windows2012R2Forest
GlobalCatalogs : {GSTB-DC.gstenanta.local}
Name : gstenantb.local
PartitionsContainer : CN=Partitions,CN=Configuration,DC=gstenantb,DC=local
RootDomain : gstenantb.local
SchemaMaster : GSTB-DC.gstenanta.local
Sites : {Default-First-Site-Name}
SPNSuffixes : {}
UPNSuffixes : {galsynctenantb.onmicrosoft.com}

GalSyncShared, IP Range 10.0.2.0/24, DC IP: 10.0.2.4, NAT IP: 23.96.103.200

C:\>Get-ADForest
ApplicationPartitions : {DC=ForestDnsZones,DC=gsshared,DC=local, DC=DomainDnsZones,DC=gsshared,DC=local}
CrossForestReferences : {}
DomainNamingMaster : GSS-DC.gsshared.local
Domains : {gsshared.local}
ForestMode : Windows2012R2Forest
GlobalCatalogs : {GSS-DC.gsshared.local}
Name : gsshared.local
PartitionsContainer : CN=Partitions,CN=Configuration,DC=gsshared,DC=local
RootDomain : gsshared.local
SchemaMaster : GSS-DC.gsshared.local
Sites : {Default-First-Site-Name}
SPNSuffixes : {}
UPNSuffixes : {}

Since the diagram shows exporting to and importing from the GalSyncShared forest, we’ll need to be able to locate that forest from each of the account forests.  So, we can run this in each of the account forests:

$DnsServers = @('<IP addresses of DC in resource forest')
Add-DnsServerConditionalForwarderZone -MasterServers $DnsServers -Name <resource forest FQDN>

In my environment, it looks like this:

$DnsServers = @('10.0.2.4')
Add-DnsServerConditionalForwarderZone -MasterServers $DnsServers -Name gsshared.local

In the previous post, we configured some network security groups. Now, it’s time to test them out!  As the solution requires, we need to verify that we have network connectivity to our resource forest from our account forests. Grab the AAD Network Tool and run it from each of the account forest DCs (GTSA-DC and GTSB-DC, in my lab) with the following parameters:

.\AADConnect-CommunicationsTest.ps1 -DCs <FQDN of one or more DCs in remote forest> -ActiveDirectory -ForestFQDN <resource forest FQDN> -Dns -Network

So, in my lab, it looks like this:

.\AADConnect-CommunicationsTest.ps1 -DCs gss-dc.gshshared.local -ActiveDirectory -ForestFQDN gsshared.local -Dns -Network

AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

This test verifies that all of the networking and name resolution prerequisites are met in order to be able to add another AD connector to AAD Connect.  Run this in each account forest and attempt to communicate with the resource forest.

Prepare the Resource Forest

In this step, we’re going to prepare the resource forest and delegated service accounts.  Similar to a standard mutli-forest configuration, we’re going to need to specify an account to use to connect with in the remote resource forest.  We’re also going to specify which organizational unit structure we want to scope our connector to (well, we need to create it first, technically).

  1. Log into the resource forest domain controller.  In my lab, this is gss-dc.gsshared.local.
  2. Launch Active Directory Users and Computers.
  3. Create an Organizational Unit called something easy to identify, such as Shared GAL.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  4. Then, underneath it, create an OU for each organization that will be utilizing the shared resource forest.
    andAAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  5. In the users container (or any other container not in the Shared GAL path), create two new users–one for each tenant.  I’m going to name my accounts pretty obvious names: admin-tenanta and admin-tenantb.
  6. Select View | Advanced Features.
  7. Right-click on OU=Tenant A,OU=Shared GAL, select Properties, and then select the Security tab.  Click Add, add admin-tenanta, and then click the Full Control check box under the Allow column.
  8. Click Advanced, and then click the entry for admin-tenanta. Click Edit. Ensure This object and all descendant objects is selected in addition to Full Control.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  9. Click OK. Repeat the procedure for OU=Tenant B,OU=Shared GAL and admin-tenantb.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

Create Connector for Resource Forest

Now that we have name resolution and network connectivity established as well as an OU structure in the resource forest, we’re going to start the AAD Connect configuration.  A brief overview:

  • Stop AAD Connect Sync Cycle Schedule
  • Establish a new connector
  • Create Run Profiles
  • Create metaverse attribute

These steps will establish the connectivity between AAD Connect and the resource forest and configure the run steps that will allow connector to execute later.  These steps will be performed on each of the account forest AAD Connect servers.

Disable AAD Connect Schedule

  1. Launch an elevated PowerShell window.
  2. Run the following command to disable the synchronization scheduler:
    Set-ADSyncScheduler -SyncCycleEnabled $false

Create Connector

  1. Click Start and select the Synchronization Service.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  2. Click the Operations tab, and then select Create from the Actions Pane (or right-click | Create in the empty area).
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  3. Select the type of connector as Active Directory Domain Services.  Enter a name and a description and click Next.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  4. Enter the resource forest name, the admin account created previously for this account forest, password, and the  DNS domain name. Click Next.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  5. Select the domain partition shown, and then click the Containers button.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  6. Deselect all containers except the Shared GAL container created previously. Click OK when finished.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  7. Click Next.
  8. On the Configure Provisioning Hierarchy page, click Next without making any changes.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  9. On the Select Object Types page, click contact to add it to the list of selected object types.  Click Next.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  10. On the Select Attributes page, click the Show All checkbox, and then select the following attributes:
    c
    cn
    co
    company
    department
    description
    displayName
    division
    extensionAttribute1
    extensionAttribute10
    extensionAttribute11
    extensionAttribute12
    extensionAttribute13
    extensionAttribute14
    extensionAttribute15
    extensionAttribute2
    extensionAttribute3
    extensionAttribute4
    extensionAttribute5
    extensionAttribute6
    extensionAttribute7
    extensionAttribute8
    extensionAttribute9
    facsimileTelephoneNumber
    givenName
    homePhone
    info
    initials
    l
    mail
    mailNickname
    middleName
    mobile
    msExchRecipientDisplayType
    msExchRecipientTypeDetails
    objectGUID
    otherHomePhone
    otherTelephone
    pager
    physicalDeliveryOfficeName
    postalAddress
    postalCode
    postOfficeBox
    proxyAddresses
    sn
    st
    street
    streetAddress
    targetAddress
    telephoneAssistant
    telephoneNumber
    title
  11. Click OK to complete the creation of the connector.

Create Run Profiles

Run profiles are action definitions for the connector.  For example, if AAD Connect calls a profile with the Full Import action, it will import all objects in scope in the connected directory.

  1. On the Connections tab, right-click on the Shared GAL connector and click Configure Run Profiles.
  2. Click New Profile.
  3. Enter Full Import in the name field and click Next.
  4. Select the Full Import step type and click Next.
  5. Click Finish.
  6. Click New Profile.
  7. Enter Full Synchronization in the name field and click Next.
  8. Select the Full Synchronization step type and click Next.
  9. Click Finish.
  10. Click New Profile.
  11. Enter Delta Import in the name field and click Next.
  12. Select the Delta Import (Stage Only) step type and click Next.
  13. Click Finish.
  14. Click New Profile.
  15. Enter Delta Synchronization in the name field and click Next.
  16. Select the Delta Synchronization step type and click Next.
  17. Click Finish.
  18. Click New Profile.
  19. Enter Export in the name field and click Next.
  20. Select the Export step type and click Next.
  21. Click Finish.  You should now have 5 run profiles configured.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  22. Click OK.

Create metaverse attribute

For this custom configuration, we’re going to create a custom metaverse attribute to hold a unique value that we can assign to objects in the remote forest. In the event that we have two objects with otherwise identical properties (for example, two users name John Smith), we can use this stored value which is unique to this installation to ensure uniqueness of objects going to the resource forest.

  1. From inside the Synchronization Service Manager, click Metaverse Designer.
  2. Click the person object type.
  3. Click Add Attribute.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  4. Click New Attribute.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  5. Enter a new attribute name.  In this example, I’m going to use customMailNicknameBe exactly sure of what you enter.  This is case-sensitive, and bad things will happen if you capitalize it differently throughout the configuration process.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  6. Click OK to close the Add Attribute to Object Type dialog box.
  7. Click the group object type.
  8. Click Add Attribute.
  9. Select customMailNickname from the list and click OK.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

Create Synchronization Rules

The synchronization rules is where all of the magic happens.  You can download this script, which is all of the rules assembled here.  If you have used a different custom attribute in the Metaverse, you’ll need to specify it with -CustomMetaverseAttribute. To run the script:

  1. Download it to each of the AAD connect servers participating in the synchronization.
  2. Launch an elevated PowerShell window and change to the directory where you’ve saved the script.
  3. The script requires a TargetOU parameter, so, you’ll need to specify the OU that you created above for the forest that you’re syncing from.  For example, if we’re configuring this in GalSync Tenant A (GSTA), I’d use “OU=Tenant A,OU=Shared GAL,DC=gsshared,DC=local” as my OU path.
    .\CustomGAL -TargetOU "OU=Tenant A,OU=Shared GAL,DC=gsshared,DC=local"

    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

  4. Select the AD connector that represents your current Active Directory Account ForestIn this case, I’m going to choose 1.
  5. Select the AD connector that represents the Active Directory Resource Forest.  In this case, I’m going to choose 2.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)
  6. Confirm your choice.  The script will create the necessary connectors.
    AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

Or, if you’re a glutton for punishment, you can go through the process outlined here to create the sync rules manually.

In from AD – Prevent Contact Target Address

The purpose of this rule is to prevent the flowing of an AD user’s targetAddress into their corresponding contact’s targetAddress when the object gets synchronized out to the GAL.

  1. Launch the Synchronization Rules Editor.
  2. Select Inbound under direction, and then click Add New Rule.
  3. On the Description page, enter the following values:
NameIn from AD – Prevent Contact Target Address
Connected SystemOrganization Active Directory connector
Connected System Object Typeuser
Metaverse Object Typeperson
Link Typejoin
Precedence90 (or other unused value about 10 below default rules)
  1. Click Next.
  2. On the Scoping Filter page, click Next.
  3. On the Join Rules page, click Next.
  4. On the Transformations page, click Add.
  5. Enter the following values:
Flow TypeTarget AttributeSourceApply OnceMerge Type
ExpressiontargetAddressAuthoritativeNullUpdate
  1. Click Add.

In from AD – Flow CustomMailNickname – Group

The purpose of this rule is to populate the CustomMailNickname attribute on the objects that will be going to the Shared GAL.  It will be used to help construct unique names in the event that multiple source objects have the same alias value.

  1. Select Inbound under direction, and then click Add New Rule.
  2. On the Description page, enter the following values:
NameIn from AD – Flow CustomMailNickname – Group
Connected SystemOrganization Active Directory connector
Connected System Object Typegroup
Metaverse Object Typegroup
Link Typejoin
Precedence98 (or other unused value higher than Prevent Contact Target Adress)
  1. Click Next.
  2. On the Scoping Filter page, click Add Group.
  3. Click Add Clause.
    Enter the following values:
AttributeOperatorValue
mailNicknameISNOTNULL
  1. Click Next.
  2. On the Join Rules page, click
  3. On the Transformations page, enter the following values:
Flow TypeTarget AttributeSourceApply OnceMerge Type
ExpressioncustomMailNickname%Forest.Netbios% & “.” & [mailNickname]Update
  1. Click Add.

In from AD – Flow CustomMailNickname – User

The purpose of this rule is to populate the CustomMailNickname attribute on the objects that will be going to the Shared GAL.  It will be used to help construct unique names in the event that multiple source objects have the same alias value.

  1. Select Inbound under direction, and then click Add New Rule.
  2. On the Description page, enter the following values:
NameIn from AD – Flow CustomMailNickname – User
Connected SystemOrganization Active Directory connector
Connected System Object Typeuser
Metaverse Object Typeperson
Link Typejoin
Precedence99 (or other unused value higher than Flow CustomMailNickname – Group)
  1. Click Next.
  2. On the Scoping Filter page, enter the following values:
AttributeOperatorValue
mailNicknameISNOTNULL
  1. Click Next.
  2. On the Join Rules page, click
  3. On the Transformations page, enter the following values:
Flow TypeTarget AttributeSourceApply OnceMerge Type
ExpressioncustomMailNickname%Forest.Netbios% & “.” & [mailNickname]Update
  1. Click Add.

In from AD – Shared GAL Contact

The purpose of this rule is to import objects from the Shared GAL to the AAD Connect metaverse.

  1. Select Inbound under direction, and then click Add New Rule.
  2. On the Description page, enter the following values:
NameIn from AD – Shared GAL Contact
Connected SystemShared GAL (resource forest)
Connected System Object Typecontact
Metaverse Object Typeperson
Link Typeprovision
Precedence201 (or other unused value higher than all default values)
  1. Click Next.
  2. On the Scoping Filter page, enter the following values:
AttributeOperatorValue
dnNOTCONTAINS.group.
mailNOTCONTAINS@[organization SMTP]
  1. Click Next.
  2. On the Join Rules page, click Add group.
  3. Click Add clause.
  4. Enter the following values, clicking Add clause to add a line for each join rule:
Source AttributeTarget AttributeCase Sensitive
mailNicknamecustomMailNickname
mailmail
  1. Click Next.
  2. On the Transformations page, enter the following values:
Flow TypeTarget AttributeSourceApply OnceMerge Type
ExpressioncTrim([c])Update
DirectcncnUpdate
ExpressioncoTrim([co])Update
ExpressioncompanyTrim([company])Update
DirectcountryCodecountryCodeUpdate
ExpressiondepartmentTrim([department])Update
ExpressiondescriptionIIF(IsNullOrEmpty([description]),NULL,Left(Trim(Item([description],1)),448))Update
ExpressiondisplayNameIIF(IsNullOrEmpty([displayName]),[cn],[displayName])Update
ExpressionextensionAttribute1Trim([extensionAttribute1])Update
ExpressionextensionAttribute2Trim([extensionAttribute2])Update
ExpressionextensionAttribute3Trim([extensionAttribute3])Update
ExpressionextensionAttribute4Trim([extensionAttribute4])Update
ExpressionextensionAttribute5Trim([extensionAttribute5])Update
ExpressionextensionAttribute6Trim([extensionAttribute6])Update
ExpressionextensionAttribute7Trim([extensionAttribute7])Update
ExpressionextensionAttribute8Trim([extensionAttribute8])Update
ExpressionextensionAttribute9Trim([extensionAttribute9])Update
ExpressionextensionAttribute10Trim([extensionAttribute10])Update
ExpressionextensionAttribute11Trim([extensionAttribute11])Update
ExpressionextensionAttribute12Trim([extensionAttribute12])Update
ExpressionextensionAttribute13Trim([extensionAttribute13])Update
ExpressionextensionAttribute14Trim([extensionAttribute14])Update
ExpressionextensionAttribute15Trim([extensionAttribute15])Update
ExpressionfacsimileTelephoneNumberTrim([facsimileTelephoneNumber])Update
ExpressiongivenNameTrim([givenName])Update
ExpressionhomePhoneTrim([homePhone])Update
ExpressioninfoLeft(Trim([info]),448)Update
ExpressioninitialsTrim([initials])Update
ExpressionipPhoneTrim([ipPhone])Update
ExpressionlTrim([l])Update
ExpressionmailTrim([mail])Update
ExpressionmailNicknameIIF(IsPresent([mailNickname]), [mailNickname], [cn])Update
ExpressionmiddleNameTrim([middleName])Update
ExpressionmobileTrim([mobile])Update
DirectmsExchRecipientDisplayTypemsExchRecipientDisplayTypeUpdate
DirectmsExchRecipientTypeDetailsmsExchRecipientTypeDetailsUpdate
ExpressionotherFacsimileTelephoneNumberTrim([otherFacsimileTelephoneNumber])Update
ExpressionotherHomePhoneTrim([otherHomePhone])Update
ExpressionotherIpPhoneTrim([otherIpPhone])Update
ExpressionotherMobileTrim([otherMobile])Update
ExpressionotherPagerTrim([otherPager])Update
ExpressionotherTelephoneTrim([otherTelephone])Update
ExpressionpagerTrim([pager])Update
ExpressionphysicalDeliveryOfficeNameTrim([physicalDeliveryOfficeName])Update
ExpressionpostalCodeTrim([postalCode])Update
ExpressionpostOfficeBoxIIF(IsNullOrEmpty([postOfficeBox]),NULL,Left(Trim(Item([postOfficeBox],1)),448))Update
ExpressionproxyAddressesRemoveDuplicates(Trim(ImportedValue(“proxyAddresses”)))Update
ExpressionsnTrim([sn])Update
ExpressionsourceAnchorConvertToBase64([objectGUID])Update
DirectsourceAnchorBinaryobjectGUIDUpdate
ConstantsourceObjectTypeContactUpdate
ExpressionstTrim([st])Update
ExpressionstreetAddressTrim([streetAddress])Update
DirecttargetAddresstargetAddressUpdate
ExpressiontelephoneAssistantTrim([telephoneAssistant])Update
ExpressiontelephoeNumberTrim([telephoneNumber])Update
ExpressiontitleTrim([title])Update
ExpressioncloudFilteredIIF(IsPresent([isCriticalSystemObject]) || ( (InStr([displayName], “(MSOL)”) > 0) && (CBool([msExchHideFromAddressLists]))) || (Left([mailNickname], 4) = “CAS_” && (InStr([mailNickname], “}”) > 0)) || CBool(InStr(DNComponent(CRef([dn]),1),”\\0ACNF:”)>0), True, NULL)Update
ExpressionmailEnabledIIF(( (IsPresent([proxyAddresses]) = True) && (Contains([proxyAddresses], “SMTP:”) > 0) && (InStr(Item([proxyAddresses], Contains([proxyAddresses], “SMTP:”)), “@”) > 0)) ||  (IsPresent([mail]) = True && (InStr([mail], “@”) > 0)), True, False)Update
  1. Click Add.

Out to AD – Shared GAL User Contact

The purpose of this rule is to provision a new user contact object in the organization’s Office 365 GAL OU (the resource forest).

  1. Select Outbound under direction, and then click Add New Rule.
  2. On the Description page, enter the following values:
NameOut to AD – Shared GAL User Contact
Connected SystemShared GAL [resource forest]
Connected System Object Typecontact
Metaverse Object Typeperson
Link Typeprovision
Precedence300 (or other unused value higher than all other rules)
  1. Click Next.
  2. On the Scoping Filter page, enter the following values for each domain for which the agency hosts mail:
AttributeOperatorValue
customMailNicknameISNOTNULL
mailISNOTNULL
  1. Click Next.
  2. On the Join Rules page, enter the following values:
Source AttributeTarget AttributeCase Sensitive
mailmail
  1. Click Next.
  2. On the Transformations page, enter the following values:
Flow TypeTarget AttributeSourceApply OnceMerge Type
ExpressioncTrim([c])Update
ExpressioncoTrim([co])Update
ExpressioncompanyTrim([company])Update
DirectcountryCodecountryCodeUpdate
ExpressiondepartmentTrim([department])Update
ExpressiondescriptionIIF(IsNullOrEmpty([description]),NULL,Left(Trim(Item([description],1)),448))Update
ExpressiondisplayNameIIF(IsNullOrEmpty([displayName]),[cn],[displayName])Update
Expressiondn“CN=” & [customMailNickname] & “,OU=<dept>, OU=Shared GAL,DC=[resource forest],DC=[tld]”Update
ExpressionextensionAttribute1Trim([extensionAttribute1])Update
ExpressionextensionAttribute2Trim([extensionAttribute2])Update
ExpressionextensionAttribute3Trim([extensionAttribute3])Update
ExpressionextensionAttribute4Trim([extensionAttribute4])Update
ExpressionextensionAttribute5Trim([extensionAttribute5])Update
ExpressionextensionAttribute6Trim([extensionAttribute6])Update
ExpressionextensionAttribute7Trim([extensionAttribute7])Update
ExpressionextensionAttribute8Trim([extensionAttribute8])Update
ExpressionextensionAttribute9Trim([extensionAttribute9])Update
ExpressionextensionAttribute10Trim([extensionAttribute10])Update
ExpressionextensionAttribute11Trim([extensionAttribute11])Update
ExpressionextensionAttribute12Trim([extensionAttribute12])Update
ExpressionextensionAttribute13Trim([extensionAttribute13])Update
ExpressionextensionAttribute14Trim([extensionAttribute14])Update
ExpressionextensionAttribute15Trim([extensionAttribute15])Update
ExpressionfacsimileTelephoneNumberTrim([facsimileTelephoneNumber])Update
ExpressiongivenNameTrim([givenName])Update
ExpressionhomePhoneTrim([homePhone])Update
ExpressioninfoLeft(Trim([info]),448)Update
ExpressioninitialsTrim([initials])Update
ExpressionipPhoneTrim([ipPhone])Update
ExpressionlTrim([l])Update
ExpressionmailTrim([mail])Update
ExpressionmailNicknameIIF(IsPresent([mailNickname]), [mailNickname], [cn])Update
ExpressionmiddleNameTrim([middleName])Update
ExpressionmobileTrim([mobile])Update
ConstantmsExchRecipientDisplayType6Update
ConstantmsExchRecipientTypeDetails128Update
ExpressionotherFacsimileTelephoneNumberTrim([otherFacsimileTelephoneNumber])Update
ExpressionotherHomePhoneTrim([otherHomePhone])Update
ExpressionotherIpPhoneTrim([otherIpPhone])Update
ExpressionotherMobileTrim([otherMobile])Update
ExpressionotherPagerTrim([otherPager])Update
ExpressionotherTelephoneTrim([otherTelephone])Update
ExpressionpagerTrim([pager])Update
ExpressionphysicalDeliveryOfficeNameTrim([physicalDeliveryOfficeName])Update
ExpressionpostalCodeTrim([postalCode])Update
ExpressionpostOfficeBoxIIF(IsNullOrEmpty([postOfficeBox]),NULL,Left(Trim(Item([postOfficeBox],1)),448))Update
ExpressionproxyAddressesRemoveDuplicates(Trim(ImportedValue(“proxyAddresses”)))Update
ExpressionsnTrim([sn])Update
ExpressionsourceAnchorConvertToBase64([objectGUID])Update
DirectsourceAnchorBinaryobjectGUIDUpdate
ConstantsourceObjectTypeContactUpdate
ExpressionstTrim([st])Update
ExpressionstreetAddressTrim([streetAddress])Update
ExpressiontargetAddress“SMTP:” & [mail]Update
ExpressiontelephoneAssistantTrim([telephoneAssistant])Update
ExpressiontelephoeNumberTrim([telephoneNumber])Update
ExpressiontitleTrim([title])Update
  1. Click Add.

Out to AD – Shared GAL Group Contact

The purpose of this rule is to provision a new group contact object in the organization’s Office 365 GAL (resource forest).

  1. Select Outbound under direction, and then click Add New Rule.
  2. On the Description page, enter the following values:
NameOut to AD – Shared GAL Group Contact
Connected SystemShared GAL (resource forest)
Connected System Object Typecontact
Metaverse Object Typegroup
Link Typeprovision
Precedence301 (or other unused value higher than Out to AD – Shared GAL User Contact rule)
  1. Click Next.
  2. On the Scoping Filter page, enter the following values for each domain for which the agency hosts mail:
AttributeOperatorValue
customMailNicknameISNOTNULL
mailISNOTNULL

 

  1. Click Next.
  2. On the Join Rules page, enter the following values:
Source AttributeTarget AttributeCase Sensitive
mailmail
  1. Click Next.
  2. On the Transformations page, enter the following values:
Flow TypeTarget AttributeSourceApply OnceMerge Type
ExpressiondescriptionIIF(IsNullOrEmpty([description]),NULL,Left(Trim(Item([description],1)),448))Update
ExpressiondisplayNameIIF(IsNullOrEmpty([displayName]),[cn],[displayName])Update
Expressiondn“CN=.group.” & [customMailNickname] & “,OU=<dept>, OU=Shared GAL,DC=[resourceforest],DC=[tld]”Update
ExpressionextensionAttribute1Trim([extensionAttribute1])Update
ExpressionextensionAttribute2Trim([extensionAttribute2])Update
ExpressionextensionAttribute3Trim([extensionAttribute3])Update
ExpressionextensionAttribute4Trim([extensionAttribute4])Update
ExpressionextensionAttribute5Trim([extensionAttribute5])Update
ExpressionextensionAttribute6Trim([extensionAttribute6])Update
ExpressionextensionAttribute7Trim([extensionAttribute7])Update
ExpressionextensionAttribute8Trim([extensionAttribute8])Update
ExpressionextensionAttribute9Trim([extensionAttribute9])Update
ExpressionextensionAttribute10Trim([extensionAttribute10])Update
ExpressionextensionAttribute11Trim([extensionAttribute11])Update
ExpressionextensionAttribute12Trim([extensionAttribute12])Update
ExpressionextensionAttribute13Trim([extensionAttribute13])Update
ExpressionextensionAttribute14Trim([extensionAttribute14])Update
ExpressionextensionAttribute15Trim([extensionAttribute15])Update
ExpressioninfoLeft(Trim([info]),448)Update
ExpressionmailTrim([mail])Update
ExpressionmailNicknameIIF(IsPresent([mailNickname]), [mailNickname], [cn])Update
ConstantmsExchRecipientDisplayType6Update
ConstantmsExchRecipientTypeDetails128Update
ExpressionproxyAddressesRemoveDuplicates(Trim(ImportedValue(“proxyAddresses”)))Update
ExpressiontargetAddress“SMTP:” & [mail]Update
  1. Click Add.

Create Custom Sync Schedule

  1. Disable the default AAD Connect synchronization schedule.
    1. Launch an elevated PowerShell prompt.
    2. Run Import-Module ADSync
    3. Run Set-ADSyncScheduler -SyncCycleEnabled $False
  2. Create new scheduled task to call each of the required run profiles for AD, AAD, and Shared GAL connectors. The scheduled task should be configured to execute every 30 minutes using an account that is a member of both the AADSync Admins group and the local Administrators group.
  3. Replace the value after -ConnectorName with the connector name as it is displayed in the AAD Connect Synchronization Service Manager. It is cAsE sENsItIvE.
  4. The values for -RunProfileName must explicitly match one of the values specified in the run profile configuration for the connector. It is cAsE sENsItIvE.

Sample Scheduled Task Script

Import-Module ADSync

Invoke-ADSyncRunProfile -ConnectorName “activedirectory.com” -RunProfileName “Delta Import”

Invoke-ADSyncRunProfile -ConnectorName “tenant.onmicrosoft.com – AAD” -RunProfileName “Delta Import”

Invoke-ADSyncRunProfile -ConnectorName “Shared GAL” -RunProfileName “Delta Import”

Invoke-ADSyncRunProfile -ConnectorName “activedirectory.com” -RunProfileName “Delta Synchronization”

Invoke-ADSyncRunProfile -ConnectorName “tenant.onmicrosoft.com – AAD” -RunProfileName “Delta Synchronization”

Invoke-ADSyncRunProfile -ConnectorName “Shared GAL” -RunProfileName “Delta Synchronization”

Invoke-ADSyncRunProfile -ConnectorName “tenant.onmicrosoft.com – AAD” -RunProfileName “Export”

Invoke-ADSyncRunProfile -ConnectorName “activedirectory.com” -RunProfile “Export”

Invoke-ADSyncRunProfile -ConnectorName “Shared GAL” -RunProfileName “Export”

Run Custom Sync Schedule

If you ran the script at the top of this post, it would have created a custom sync schedule script for you.  You can execute that, or, if you created your own custom sync schedule script, run that instead. You should be able to click on the Shared GAL connector to see the progress.

AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

Verify

Now that we have objects running through the synchronization rules, we should be able to check a few places to make sure that objects are flowing.

First, check the the Shared GAL containers in the resource forest for contact objects.

AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

Next, after a round of sync cycles, you should be able to check your tenants for objects from the partner organizations forest as contact objects.

AAD Connect, a dedicated resource forest, a custom connector, and a bunch of transform rules: a GalSync story (Part 2)

That’s all she wrote! Be fruitful and multiply contacts!

Published by Aaron Guilmette

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.

Reader Comments

  1. Thanks for confirming that ADMT is still the best way to go about the migration of accounts from one forest to another.
    The only outstanding issue now, is that the latest ADMT version does not appear to confirm support for Window 2016/2019 domain controllers.

  2. Very interesting deep dive into the guts of the AAD Connect tool.
    Is there an easy enough way to modify the script (or follow the instructions) to create AD accounts rather than AD contacts? The context is that this could be used to replace the need for ADMT during user account migrations between source and target AD forests.

Leave a Reply

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