Back in October, MS announced that the new “MS Azure AD Sync Services” (AAD Sync) had gone GA, which means that DirSync is now officially deprecated.
Ironically, I got wind that “Azure AD Connect” would be replacing Azure AD Sync Services, not but a few weeks before:
Deprecated on release? Not quite… it appears that Azure AD Sync Services will be at the core of Azure AD Connect, so time spent migrating to Azure AD Sync Services will not be a complete waste of time.
I decided to make this migration happen now, because we will be ramping up to provision faculty and staff with access to Office 365 Pro Plus software in the near future, and I would like to be working with a tool that does not require me to run it in an unsupported configuration (we removed the “Replicating Directory Changes” right from our DirSync service account, which at least at one time was considered a Bad Thing To Do.).
The Good News:
- The out-of-box configuration does not require a service account with the “Replicating Directory Changes” right, making filtering of sensitive user attributes much easier.
- The initial setup wizard allows you to de-select attributes that you don’t want to sync to Azure. No more wading though dense miisclient.exe dialogs and walking off-support to do basic filtering! The require attributes are listed in grey, so that you don’t risk filtering an attribute that Azure absolutely must have.
- AAD Sync actually has some documentation available on its installation and use over at MSDN:
…And there actually are quite a few important details in this seemingly cursory documentation.
- AAD Sync entirely replaces the UI-heavy, un-maintainable attribute filtering tools in DirSync with a new “Declarative Provisioning” engine, that allow robust attribute filtering and transformation rules to be defined by the sys admin.
- Custom “Inbound” attribute filters will be preserved during product upgrades, according to the documentation.
- The documentation has been updated with instructions on how to make domain-based sync filters actually work in a multi-domain forest. This was a key detail that was missing in the DirSync documentation.
The Bad News:
- The Declarative Provisioning language, while based on VB for Applications (The “best known language by systems administrators”, according to the docs. Whaaaat?!?!), is not actually VBA. Debugging methods for dealing with this new VB-like thing are not at all documented, and examples of its usage are few and far between.
So what did I learn during this deployment process that was Blog-worthy? How to debug Declarative Provisioning!
Debugging Declarative Provisioning (Import Expression Filters):
Let’s take an example. We want to sync only students, and users who are to be provisioned for use of Lync in Office 365 into Azure. To accomplish this, we have populated students with the text value “Student” in extensionAttribute1. Lync users have had extensionAttribute2 populated with the text value “lync”.
When running the Synchronization Rule Editor, we create a new “inbound” rule, assign it to object type “inetOrgPerson”, link type is “join”, and give it a precedence under 100. We skip the Scoping and Join Rules options, and go to “Transformations”.
The flowType gets set to “Expression”, the Target Attribute to “cloudFiltered”, and we define a VBA-like expression in the “Source” field:
My expression follows:
IIF(CBool(InStr([extensionAttribute1], "Student", 1)), NULL, IIF(CBool(InStr([extensionAttribute2], "lync", 1)), NULL, True)
So what is the intent here? “cloudFiltered” is a metaverse attribute that can be set to suppress synchronization of an account upon export to Azure AD. If set to “True”, the account should not get synced to Azure.
Our expression uses the “IIF” fuction to check to see if “extensionAttrubute1″ contains “Student”. If so, then “IIF” returns “NULL”, and NULL is fed to the “cloudFiltered” attribute (which, according to the docs, will cause the attribute to be cleared if it is already set). However, if extensionAttribute1 does not contain “Student”, we perform a second test to see if “extensionAttribute2″ contains “lync”. If so, cloudFiltered again gets set to NULL. If “extensionAttribute2″ does not contain “lync”, then “cloudFiltered” gets set to the boolean value “True”.
- Items enclosed in square braces  refer to metaverse attributes, and serve as a sort of automatic variable for provisioning.
- Note that the InStr() function used here is not the same one documented in the VB for Applications language reference:
The “start position” parameter in the docs is not supported here, although it does appear that the “compare” parameter is supported (represented by the “1″ in my example, which means perform a case-insensitive comparison).
- Everything is case sensitive… even the function names! “IIF()” is all caps. CBool(), CStr(), InStr(), and Error() are all “Caml Cased”.
- IIF() is a bit of a strange beast by itself. You might need this syntax reference:
Now that we have a filter, how to we test it? The docs say “Run a full import followed by a full sync” on the AD management agent. While this will work, it also is a bit time consuming. If there are syntax errors in your expression, you will get a lot of errors in your sync logs. Is there something better?
As it turns out, yes there is. Special thanks to Brian Desmond (co-author of the o’Riley press “Active Directory Cookbook”) for putting me on the right track:
- In the MIISClient.exe, right click your AD management agent, and select “Search Connector Space”:
- Specify the DN of a user that you want to test your expression on in the search box. You can quickly get a DN using PowerShell with the ActiveDirectory module:
Get-ADUser -Identity [samAccountName]
Click “Search”, click the returned object, and click “Preview”:
- In the “Preview” dialog, select either a Full or Delta sync, then click “Generate Preview”, and select the “Import Attribute Flow” option on the left. Review the list of Sync Rules on the bottom pane to make sure your rule was processed. Any rule that generates a change will be displayed in the top pane:
In this case, the expression did not generate a change in the existing metaverse object.
- If an expression that you have authored generates an error, a full stack trace will appear in the Error Details section of the Preview pane. Scroll down to the bottom of the stack, where it is likely that the actual problem with your expression will be identified:
In this example, I inserted an “Error()” function with a null argument. The language engine did not like this.
- Back in the “Start Preview” tab, you could “Commit Preview” to apply the filter rules for this specific object, and then view the results in the Metaverse Search component of miisclient.exe.
Using the preview tool, I was able to get my filter expressions working correctly with minimal fuss and delay. The final hurdle to getting filtered attributes set correctly was understanding what the various “run profiles” in AAD Sync actually do.
- AD Agent – Full Import: Appears to sync data from AD to the AAD Sync Metaverse without applying Declarative Provisioning Filters. I think. Not 100% on this one.
- AD Agent – Full Synchronization: Appears to apply Declarative Provisioning filters on all metaverse objects.
- Azure AD Agent – Full Sync: Appears to apply export filtering logic to metaverse objects entering the Azure connector space.
- Azure AD Agent – Export: Appears to sync metaverse objects to Azure without applying filtering logic. My impression is that you must do a “sync” before an export, if you want logic applied to Azure AD objects as you intended.
My understanding of the run profiles may be flawed, but I will note that if I perform syncs in the following sequence, I get the expected results:
AD Agent:Full Import -> AD Agent:Full Sync -> Azure Agent:Full Sync -> Azure Agent:Export
Although it appears that the more authoritative way to get all of your records reconciled is:
AD Agent: MV: Azure Agent: ~~~~~~~~~ ~~~ ~~~~~~~~~~~~ Full Import => MV MV MV MV => Export MV Delta Sync Export <= MV (Only needed in Azure write-back scenarios)
However, with other sequences, I have seen strange results such as metaverse objects not getting updated with the intended filter results, or objects getting declared as “disconnectors” by the Azure AD Agent, and thus getting deleted from Azure. Ugh!
That’s all for today… hopefully this info will help keep someone else out of trouble, too.