Hello everyone, this isDavid Loderagain with Microsoft's new Customer Engineer title, but still Hybrid Identity Engineer from Detroit. Over the past year, I've seen an increase in requests from customers looking to modernize their GALSync solution. They either want to control usage of SharePoint and Teams B2B capabilities, or enable GALSync with a cloud-only organization. And they are asking for the support and guidance that I hope to give today.
Before I begin how Microsoft Identity Manager (MIM) 2016 can help provide the foundation for a supported GALSync solution, I want to make sure everyone is aware that Microsoft provides a SaaS offering for managed services that is common to all multi-tenant synchronization scenarios helps. It's called Active Directory Synchronization Service (ADSS) and it does all the back-end tenant synchronization automatically. The great advantage of ADSS is that it is a fully supported solution. MIM as a product is fully supported, but as has always been the case, all customizations within it are supported to the best of our ability. Contact your account team for more information on ADSS. Now onto the MIM discussion.
In the past Microsoft provided a supported GALSync solution in our local synchronization engine MIM.GALSync configuration documentationwas first deployed in the Microsoft Identity Integration Server (MIIS) 2003 period. Further documents are available from theGALSync-Resourcen-Wiki. Despite its age, this guide still applies today. But it is limited to one Active Directory-to-Active Directory user to contact the sync design.
Today we offer thatMicrosoft Graph-Connector. It provides the ability to connect to and manage an Azure AD tenantB2B invitations. However, it is not a direct replacement for GALSync. We can get there, but we need to add some missing components.
There are many scenarios that Graph Connector could fulfill and they can get progressively more difficult. For this blog, I will focus on the simplest scenario and then discuss what to think about to move on to more complex scenarios.
In a classic GALSync solution, we sync users from a partner AD to become contacts in our home AD. For this Azure AD replacement, we want to sync users from a partner tenant and make them B2B users in our home tenant. This assumes you've gotten past the point where you need identical GALs in both Exchange on-premises and Exchange Online. Most of the clients I work with have gotten to this point. They may still need to move some service account mailboxes, but all of the people who need to view a GAL have been moved to Exchange Online. This simplifies requirements by eliminating the need to create local objects for Exchange to use.
The first component we need is MIM 2016. If you are an Azure AD Premium customer,MIM is still fully supportedand stillavailable for download. Otherwise MIM is inextended supportby 2026. To keep our infrastructure small, this solution only uses aInstall synchronization service, and will not use the portal or declarative deployments.
With a basic MIM installation, we're almost ready to establish a Graph connection to our first tenant. But before we do that, we need to have a discussion about scope, since scope is the main factor in determining the complexity of the solution. When I talk about scope, I'll be very specific about objects and attributes, since much of our terminology in this area is vague and subject to perspective to understand meaning.
"We want users from a partner tenant..." Starting with this sentence, we need to break it down to a scoped object and attribute definition. The Graph Connector exposes object classes for users and contacts (technically orgContact). We only want to include users from the partner tenant. Except that the user object class covers both internal and external (aka B2B) users. Usually we only want to integrate the internal users of the partner tenant. We can tell the difference between them because external users have a creationType attribute equal to "Invitation" while internal users have a null creationType. The other possible choice to consider is the userType attribute with the values "Guest" or "Member". But I think that's a bad choice. Internal users are member by default and external users are guest by default, but userType can be changed for both. Guest vs Member only controls default visibility for specific workloads such asSharePointorAzure ADitself. Guest is sometimes mistakenly used interchangeably with B2B, but these two terms are not equivalent.
"Make B2B users in our home tenant..." Considering the previous discussion, this phrase is pretty easy to grasp now. We consider user objects with creationType='Invitation'.
After defining our intentionally simplified area, we build the first graph connection to the partner tenant. Install the Graph Connector on the MIM system. There have been a lot of fixes lately so make sure you use thoseCurrent Version.
Begin by creating a new Graph (Microsoft) connector.
Offerregistered app credentialsto connect to the partner client. App registration requires at least User.Read.All and Directory.Read.All with admin consent. This is an example from one of my temporary demo tenants.
On the Schema 1 page, leave the Add Objects filter unchecked. Unfortunately we can't use the filter function to return only the internal users where creationType is null . The Graph API offers moreadvanced filter functions, but a header value must be set, which the Graph Connector does not currently expose as a configurable setting.
On the Select Object Types page, activate the user.
On the Select Attributes page, let's select a minimum number of attributes to enable decent GAL functionality as part of B2B sync. Additional attributes can be added if the GAL needs to be more fully populated. Choose creationType, displayName, givenName, id, mail, showInAddressList and surname.
The anchor attribute on the Configure Anchor page is automatically set to id.
On the Configure Connector Filter page, I'll keep this example simple by using a declared filter "creationType Is present". This will filter out any external users that may already exist in the partner tenant. But this filtering comes at the cost of increased delta sync times since each filtered separator must be processed in each sync cycle.
For configure join and projection rules, we will join with ID first, then by email, otherwise we will project as person.
This is the inbound partner tenant user flow, so provide a direct inbound flow for each attribute. Some of the selected attributes are not standard metaverse attributes, so the metaverse schema needs to be extended to accommodate those attributes.
Leave the Configure Deprovisioning page default to Create Separator and the Configure Extensions page will also default to blank.
Create the Full Import and Full Sync Run Profiles. Run it to confirm that the partner tenant's users are projected into the metaverse. Also create the Delta Import and Delta Sync execution profiles. We won't use them now, but will need them later. I was spoiled by AADC to create run profiles by default.
After the inbound side of the partner tenant is completed, let's create the outbound side for the home tenant. The setup will be similar to the incoming page, but with some minor changes.
App registration in the home tenant requires Directory.Read.All and User.ReadWrite.All permissions. There is a User.Invite.All permission, but since we need to sync GAL attributes after the invite, this permission doesn't provide enough access for this scenario.
For the Scheme 1 page, we need to leave the Add object filter check box unchecked again. Even if we could technically hire onegraphic filterof creationType eq 'Invitation', breaks the use of a filter Delta Imports for the Graph Connector (with a no-start-ma error). We still need to use MIM filtering to keep the range correct as delta imports are very important for most of my clients.
On the Global Parameters page, set the Redirect URL for invitationshttps://myapps.microsoft.com/?tenantid=GUIDValue. Leave the Send email box unchecked unless you want to automatically spam all of your invitees.
On the Select Attributes page, add userPrincipalName and userType in addition to the list of attributes from the entry page. We're only selecting UPN so we can see the full results of the invitation process, not because we're syncing that attribute.
For the Configure Connector Filter page, we reverse it from the inbound partner's tenant setting and use a filter of "creationType Is not present".
On the Configure Join and Projection Rules page, add only the Email join rule. There shouldn't be a projection rule as we want all external users to project from the incoming partner tenant into the metaverse.
For the Configure Attribute Flow page, add a direct export (which allows null values) for displayName, givenName, mail, showInAddressList, and surname. Add a constant export from Guest for userType. While an external user is usually a guest by default, the Graph Connector is a member by default, so we need to override that. Also add a constant export of Invitation for creationType. For the creationType we only put this in to satisfy the MA filter, not that it affects the invitation process.
On the Configure Deprovisioning page, change the selection to Stage a delete on the object for the next export run.
Create and run the Full Import and Full Sync Run Profiles. If there are matching email values for existing external users, they should join. Otherwise, the existing external users will appear as separators. Also create the Delta Import, Delta Sync, and Export run profiles. We won't use them now, but will need them later.
Finally, we need a small amountdeployment codeto deploy the external users from the metaverse in the home tenant MA. In the Tools > Options… menu, select the Enable metaverse rule expansion check box. Then click the Create Rule Extension Project… button. I will provide sample code for Visual C#, so select this choice and the version of Visual Studio to use to compile the project.
This is an example implementation for theIMVSynchronization.ProvisionMethod.
void IMVSynchronization.Provisioning (MVEntry mventry)
{
string container = "OBJECT=user";
string rdn = "CN=" + Guid.NewGuid().ToString();
ConnectedMA HomeTenantMA = mventry.ConnectedMAs["HomeTenant"];
ReferenceValue dn = HomeTenantMA.EscapeDNComponent(rdn).Concat(container);
int numConnectors = HomeTenantMA.Connectors.Count;
// If there is no connector, create a new connector.
if (number of connections == 0)
{
CSEntry csentry = HomeTenantMA.Connectors.StartNewConnector("user");
csentry.DN = dn;
csentry["id"].StringValue = Guid.NewGuid().ToString();
csentry.CommitNewConnector();
}
else if (numConnectors == 1)
{
//Do nothing, no renaming required
}
anders
{
throw (new UnexpectedDataException("multiple connectors:" + numConnectors.ToString()));
}
}
A few things to note in this code. We need the name of the home tenant MA as the connected MA that we manage. We also set a random GUID-based DN and ID to export the invitation successfully, but these values will be replaced with the real Azure AD values on the first confirming import.
Build the solution in Visual Studio and ensure that the extension DLL is copied to the Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions folder. In the Options dialog box, ensure that the DLL you just created is selected for the rule extension name and check the Enable Provisioning Rules Extension checkbox.
To start with a little test, select a sample user from the partner tenant's MA and run a full sync preview on them. This should generate a pending export in the home tenant MA.
The little magic of the Graph Connector is that a user who has a pending addition with email but no UPN goes through the invitation process to make them an external user instead of being created as an internal user. We can see the pending export with the temporary DN and ID, associated GALSync attributes, and our constant userType Guest. This test user has an OnMicrosoft.com email address in the partner tenant because I didn't add a custom domain to that tenant. The actual postage value is ultimately irrelevant as long as it does not already belong to the target tenant.
Run the export followed by a confirming delta import.
We see that the user was successfully invited, has their real DN and ID, and has all the attributes we set. Note that the UPN was automatically set by AAD in the expected format of mail#EXT#@tenant. It was also given a default of showInAddressList = false. By default, invited external users are hidden from the GAL.
Do a second delta sync cycle (delta import, delta sync, export) and showInAddressList should be set to its synced value. For this sample user, that would be a null value.
After exporting the updated showInAddressList value, we can confirm that our GALSync is working. In the home tenant, sign in to Outlook on the web, open the Contacts app, and select the All Users GAL. We should see our newly synced user in the GAL.
Finally, to complete the deprovisioning aspect of GALSync, configure the Object Deletion Rule on the Person object class to delete the metaverse object when the partner tenant connector is disconnected, and set the MA's deprovisioning action to Stage a delete. This way, a deletion of the user from the partner tenant will cascade a deletion of the external B2B user to our home tenant.
This is the end of the setup for GALSync from a single source to a single target client.
As I indicated at the beginning, more complex setups are possible. Imagine a bi-directional GALSync where the partner tenant also needs our home tenant's users. One way to keep the architecture simple is to maintain one MIM instance per tenant. We simply duplicate this setup in the opposite direction. This is identical to the AADC architecture, which requires an AADC for each tenant. It allows the provisioning code to know the tenant it is responsible for, cleanly separates inbound from outbound flows, and doesn't cause precedence issues. It also allows the partner to control the app registry that has write access to its tenant.
Or consider a full mesh setup where the tenants are all peers in an organization that for some reason have decided to segment their tenants. We could design a single MIM solution that manages each tenant. We could add two connectors to each tenant to separate internal users from external and still manage the flows separately. We would just need to prevent deployment to the same tenant in the deployment code. I could also see a solution using just one connector for each tenant. We could develop a mechanism to track the authority of address spaces so we know which source tenant is responsible for each user and use that knowledge to then create the external B2B users in the other tenants.
For larger deployments where we may have concerns about the number of circuit breakers and the corresponding delta synchronization times, there are some advanced techniques we could implement to address these concerns. We could project and terminate objects in the metaverse instead of keeping them as breakpoints. Or we could replace the graph connector with a PowerShell connector and take care of all the graph logic ourselves, avoiding scenarios where the graph connector has limitations.
Hopefully this has shed some light on the considerations for a modern GALSync solution.
Thanks for spending some time with me.
- Dave
Disclaimer: The sample scripts are not supported by any standard Microsoft support program or service. The sample scripts are provided "AS IS" and without warranty of any kind. Microsoft further disclaims all implied warranties, including but not limited to any implied warranties of merchantability or fitness for a particular purpose. The entire risk of using or executing the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the Scripts be liable for any damages of any kind (including, but not limited to, damages for loss of business profits, business interruption, loss of business information). , or other pecuniary loss) arising out of the use of, or inability to use, the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.