Friday, April 4, 2025

Microsoft Entra HR and Inbound Provisioning – World’s Best Boss Edition Cross Post

 

My Microsoft Entra HR and Inbound Provisioning – World’s Best Boss Edition post is now up on the new Core Infrastructure and Security Tech Community site.

 

Hello everyone, this is David Loder again, with another topic inspired by a recent customer interaction. They had a complex on-prem AD setup that was preventing them from showing their desired management org structure in the M365 Exchange Online Global Address List.

 

A white mug with black text on it.A diagram of a company structure.

 

In this article we are going to look at the Entra ID HR/Inbound Provisioning flows, specifically at the boss manager attribute. Almost all customers with which I work are still running hybrid environments. Therefore, we’ll be looking at how HR/Inbound Provisioning gets the manager data to Active Directory correctly so that Entra Connect Sync provides accurate data to the tenant. If you happen to be working with a cloud-only tenant, then there’s no drama for the manager attribute, and all scenarios work without issue. There can be complexity with the manager attribute so there’s some foundational understanding that’s needed to understand why the manager attribute can be challenging and how we can overcome these problems. Finally, I’ll bring all this knowledge together to show a design that can help customers easily handle the most complex AD and sync environments.

We’ll begin with the simplest example: a single forest, single domain AD environment. We have a single provisioning agent installed, and the single domain registered with the agent.

If we have a manager Alice with a reporting employee Bob, we can send a transaction to the API endpoint that will create both Alice and Bob and set Alice as Bob’s manager.

 

{

"userName": "EMP1222",

"nickName": "EMP1222",

"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {

    "manager": {

        "value": ""

    },

    "employeeNumber": "1222"

},

"name": {

    "familyName": "Manager",

    "givenName": "Alice"

},

"externalId": "1222",

"active": true,

"displayName": "Alice Manager"

},

{

"userName": "EMP1513",

"nickName": "EMP1513",

"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {

    "manager": {

        "value": "1222"

    },

    "employeeNumber": "1513"

},

"name": {

    "familyName": "Employee",

    "givenName": "Bob"

},

"externalId": "1513",

"active": true,

"displayName": "Bob Employee"

}

 

Note the direction of that relationship: we say Bob has Alice as their manager, not that Alice has Bob as a reporting employee. In AD, one can have only a single manager, while one might have several reporting employees. That aligns with the API template in that we provide the manager ID on the employee record.

If we review the provisioning log for that transaction, we’ll see the creation of Bob (ordering of the Creates can be indeterminate) but do not attempt to set manager during the Create step. Next, we see the creation of Alice. Finally, there is an Update to Bob’s account to set Alice as their manager.

 

 

If the user accounts happen to be provisioned in the reverse order, we’ll still observe the same outcome. The sync engine is smart enough to skip setting manager references until it reaches the end of its cycle. If we had sent a transaction that only contained Bob but still listed Alice as their manager, Bob would have finished the provisioning cycle with no manager in AD because the sync engine doesn’t know anything about Alice yet.

This sequence of events is necessary because of the somewhat unique properties of the manager attribute from a schema perspective. So, let’s refresh our knowledge on how the manager attribute works in Active Directory. This knowledge is important to understand how to handle some of the more complex setups we’ll get to later in the article.

The schema definition for manager has these important attributes:

 

Get-ADObject -LDAPFilter 'cn=manager' -SearchBase ((Get-ADRootDSE).schemaNamingContext) -Properties linkid,attributesyntax,isSingleValued

 

attributesyntax   : 2.5.5.1

DistinguishedName : CN=Manager,CN=Schema,CN=Configuration,DC=contoso,DC=com

isSingleValued    : True

linkid            : 42

Name              : Manager

ObjectClass       : attributeSchema

ObjectGUID        : acbc9875-5e6f-4f6a-84d3-9205d843f218

 

Linkid, being 42, is an even number, meaning this is the writeable side of a linked-pair; directReports being the read-only side with linkid 43. The attributesyntax of 2.5.5.1 indicates this is an Object (DN) syntax. Object DN syntax means that the value we want to write must point to the distinguished name (DN) of another object of which the Domain Controller already has knowledge. This is an important constraint as we’ll see later.

The next item of importance is observed in the provisioning logs with the outcome of the Create steps. If we look at the details of Alice’s creation, we see an item called ‘Target ID’ being returned with a GUID value.

 

A screenshot of a computer

AI-generated content may be incorrect.

 

If we look at Alice’s account in AD, we’ll notice that Target ID is their objectGUID.

 

A screenshot of a computer

AI-generated content may be incorrect.

 

Then, if we look at the update step for Bob, where Bob’s manager value is set, we’ll notice that same objectGUID value.

 

A screenshot of a computer

AI-generated content may be incorrect.

There are several ways to represent Object (DN) values. The most common is the DN string (e.g. CN=Alice Manager,OU=userids,DC=contoso,DC=com), but there are other representations. If one searches AD using the LDAP_SERVER_EXTENDED_DN_OID control, then Object (DN) based attributes will also return the objectGUID and SID values where applicable. If we were to LDAP trace the Cloud Sync agent, we would see that the queries, the search results and the modify commands are all using LDAP_SERVER_EXTENDED_DN_OID. This allows the agent to set the manager attribute via the manager’s objectGUID. No searching the directory is necessary to find their current DN, and objectGUID is unchanging within the forest. So long as the provisioning engine knows the objectGUID (Target ID) of the manager, the manager reference will be correctly set. But this means that both the manager and employee must have been provisioned (or joined) within this one HR/Inbound provisioning app so it has knowledge of the manager’s objectGUID.

Coincidentally, as I was finalizing this writeup, a colleague reached out to me about a customer that had a PoC running with Workday against an already populated AD. The problem was their newly provisioned test users had no manager. My first question was had they provisioned the manager yet through their on-demand provision testing.

Now that we’ve covered the basic use case, let’s introduce the first complexity and have the AD forest be multi-domain. For this example, we’ll continue to have Alice be the manager, but their account will reside in the root domain and Bob’s in a child domain. In this scenario, we would again see the same flow. Both Alice and Bob’s accounts are created in their respective domains. Then Bob’s account is updated, using the Target ID of Alice for setting the manager value. The single-forest, multi-domain scenario really isn’t different from the single domain scenario. The same steps are followed by the provisioning agent that is connected to the domain in which Bob’s account resides.

With the single forest story complete and a base understanding of how the manager attribute functions in both AD and the provisioning engine, we need to increase the complexity once again.

I was having discussions with a multinational company about a problem they were having. In their current business model, each region has its own AD forest. All the forests are brought together with Entra Connect Sync into a single Entra ID tenant. Currently, all employees have an in-region manager, which is reflected in AD and the Global Address List data, partially built using the manager attribute in AD. Cross-region managers were rare and a cross-region manager was not shown in AD, the GAL, or any other tools. I was brought in because their business was changing. Cross-region managers were about to become much more common, and they wanted that manager relationship shown in the M365 services (Teams, Outlook) instead of the current in-region manager.

If this was years ago, and on-premises AD was the only directory, then without hesitation, we would have stood up a MIM GALSync solution. That traditional on-prem solution syncs a copy of the manager into the other forest as a contact object, then uses that contact object to be the DN for the manager attribute. Because the contact object is present in the same forest as the reporting employee’s account, the restriction that manager points to an object that already exists in that forest is satisfied.

Let’s illustrate the scenario to explain how the traditional on-prem solution worked. Start by assuming we have two domains, EMEA and AMERICA, each in their own forest. Alice’s account is in AMERICA and Bob’s is in EMEA. The MIM GALSync process would create a contact for Alice in EMEA and keep it in sync with Alice’s GAL details like name and email address. Bob’s AD manager attribute can then be updated to point to Alice’s contact object. Both objects are in EMEA so we can successfully write that DN value to Bob’s manager attribute, and tools like Outlook, which displays the GAL, can show that Alice is Bob’s cross-region manager.

The default sync rules in Entra Connect Sync understand this traditional GALSync scenario. The “In from AD – Contact Join” rule will join Alice’s contact object in EMEA with their user object in AMERICA, based on the default join rule using the mail attribute. With the default rules, user objects are higher precedence than contact objects, so most of the data contributed to Alice’s metaverse person object should reflect their AMERICA user details.

A screenshot of a computer

AI-generated content may be incorrect.

 

We can confirm that Alice’s metaverse person object is having data contributed from both connector space objects. The AMERICA connector being their main user object, and the EMEA connector being the GALSync contact.

A screenshot of a computer

AI-generated content may be incorrect.

 

Bob, only having an account in the EMEA domain, will have their metaverse data contributed solely from that domain.

A screenshot of a computer

AI-generated content may be incorrect.

A screenshot of a computer

AI-generated content may be incorrect.

 

Notice that the referential integrity within Entra Connect Sync has established the link to Bob’s manager as Alice’s metaverse person object. The hyperlink indicates a referential link to another object and can be clicked to view Alice’s metaverse person object. Because both Alice’s EMEA contact and AMERICA user objects are joined together to the same metaverse person object, Bob’s manager reference, that exists solely in the EMEA domain, maintains referential integrity as Entra Connect Sync translates the reference from the EMEA connector space to the metaverse.

With Alice’s user and contact objects successfully joined in the metaverse, only a single user object for Alice is provisioned into the Entra tenant. And Bob’s Entra user object has Alice’s Entra user object as its manager.

A screenshot of a computer

AI-generated content may be incorrect.

 

When all the proper data is provided and all the joins work correctly, the referential integrity of the manager relationship is maintained through each layer in Entra Connect Sync. That starts with the inbound AD connector space, merges in the metaverse with default joins, this consolidated outcome is then outbound provisioned to the Entra connector space and finally exported to the Entra tenant.

Whew. We have finally covered enough background details to help our customer modernize their HR provisioning. We covered the constraints of the manager attribute in AD. We covered updating the manager attribute with Entra’s HR provisioning app. And we covered how Entra Connect Sync needs to maintain referential integrity to correctly sync the manager attribute in a multi-forest environment.

Now, how do we accomplish the goal of getting cross-region managers to show up in an M365 Exchange Online GAL, from a multi-forest AD environment, without the use of MIM GALSync? We’ll assume only the M365 Exchange Online GAL needs to be properly populated. If all human-based mailboxes are located in ExO, only the ExO GAL needs to display the desired content. Our solution doesn’t have to correctly populate an existing on-prem Exchange org GAL because no one will view it. If that wasn’t the case, we likely be stuck with an on-prem GALSync solution.

Let’s review our technical design constraints.

  • The customer still needs to provision users into their corresponding regional AD forest.
  • Entra HR/Inbound provisioning can only provision user objects.
  • Entra HR/Inbound provisioning can only set manager values when both the manager and the employee are present within the same provisioning app.
  • One Entra HR/Inbound provisioning app can only target a single AD forest.
  • The AD manager attribute can only target objects that exist in the same forest.
  • Referential integrity within Entra Connect Sync starts with a singular connector space.

Because the AD manager attribute can only target an object in the same forest, and Entra HR/Inbound provisioning can only provision users, let’s see what happens if we decide to provision a copy of all the AMERICA users into EMEA. We could introduce a new provisioning app and match the provisioning logic for EMEA users but put the new AMERICA users in a different OU. We’d probably want the AMERICA users to be disabled, so add more logic to disable AMERICA users but not EMEA users. Then we need to worry about Entra Connect Sync. With two user objects, default precedence is chosen by which AD connector was added first. Either their disabled EMEA user or their enabled AMERICA would win precedence. We’d have to do some rule customization to keep the real EMEA users operating with the desired default flows but prevent the EMEA copy of AMERICA-based users from contributing any values. Yet we’d still have to allow the joins to happen to maintain referential integrity. If we somehow managed to craft all the necessary sync rules and provisioning app config, we’ve now introduced a new failure mode that didn’t exist before. If there are any cross-forest group memberships across a trust, we’ve made it such that an admin can accidentally select the disabled in-forest user instead of remembering to select the real cross-forest user.

That seems like a lot of customization to ensure we don’t break anything existing. What if we take a slightly different approach where we purposefully avoid needing to customize any existing flows in the HR/Inbound provisioning app or in Entra Connect Sync?

The design I offered to my customer was to introduce a “consolidation forest”. Rather than reusing an existing forest and having to work around all the challenges with customizations to not break existing processes, we use a brand-new forest just to contain all the manager references in a single domain. The new forest would only need a Domain Admin, a GMSA for the new cloud provisioning agent, and a read-only user account for the existing Entra Connect Sync. This forest doesn’t need to trust any existing forests. No one except the existing Entra Connect Sync and the new cloud provisioning agent need to communicate with it. It doesn’t require redundancy or backup as we could repopulate it from the HR/Inbound provisioning app. With a new forest we can introduce a new HR/Inbound provisioning app without changing any existing ones. All the users provisioned can be disabled. We only need to flow a single joinable attribute like mail; and, of course, manager. The existing Entra Connect Sync rules remain unchanged. New sync rules for the new forest can all be disabled except for a simple join-only rule that only contributes manager.

 

Our final architecture diagram looks like this.

 

A diagram of a diagram

AI-generated content may be incorrect.

 

In all, I think the consolidation forest is a more elegant solution. Very little additional infrastructure is needed when you’re already in a multi-forest environment. No customization to existing flows and very simple configs needed for the new forest.

The customer was thrilled with this solution. It satisfied their business case needs. They could continue with a desired path for HR/Inbound provisioning. They didn’t have to introduce any new services into their portfolio. And their sync architecture remained highly standardized to a default setup.

 

Thanks for spending a little bit of your time with me.

 

-Dave

 

Friday, November 1, 2024

Claims X-Ray Retired - Claims X-Ray Replacement

Sadly, Microsoft has retired the ADFSHelp site, and with it, the Claims X-Ray application.

It looks like the official replacement is the Gallery App called Microsoft Entra SAML Toolkit. Unfortunately, it requires a registration to trust your incoming token, instead of trusting all like Claims X-Ray did.

For OIDC/OAuth Microsoft offers https://jwt.ms. With instructions at Configure OIDC SSO for gallery and custom applications - Microsoft Entra ID | Microsoft Learn to configure a sample app registration. The launcher URL follows this pattern:

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?client_id={client_id}&response_type=id_token&redirect_uri=https://jwt.ms&scope=openid&nonce={random_value}

There are also some third-party offerings that provide a solution similar to Claims X-Ray.

There is a Microsoft employee unofficially providing a Claims X-Ray replacement at https://claimsxray.net/ 

Another is RSA and their SAML 2.0 Test Service Provider at https://sptest.iamshowcase.com/.


Monday, May 24, 2021

Multi-Tenant B2B Sync with MIM Graph Connector

Cross Post from https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/multi-tenant-b2b-sync-with-mim-graph-connector/ba-p/2381682

Hello everyone, this is David Loder again, sporting Microsoft’s new Customer Engineer title, but still a Hybrid Identity engineer from Detroit. Over the past year I’ve seen an uptick in requests from customers looking to modernize their GALSync solution. Either they’re wanting to control use of SharePoint and Teams B2B capabilities or looking to enable a GALSync with a cloud-only organization. And they’re asking for assistance and guidance that I hope to provide today.

Before I get started on how Microsoft Identity Manger (MIM) 2016 can help provide the basis for a supported GALSync solution, I want to ensure everyone knows that Microsoft provides a managed service SaaS offering to help with any multi-tenant syncing scenarios. It’s called Active Directory Synchronization Service (ADSS) and does all the back-end tenant syncing automagically. The great benefit with ADSS is that it’s a fully supported solution.  MIM as a product is fully supported, but as has always been the case, any customizations put into it are best-effort support. Reach out to your account team if you want more information about ADSS. Now on with the MIM discussion.

Historically, Microsoft has provided a supported GALSync solution in our on-premises sync engine, MIM. Documentation on the GALSync configuration was first provided back in the Microsoft Identity Integration Server (MIIS) 2003 timeframe. Additional documentation is available at the GALSync Resources Wiki. Despite its age, that guidance still holds true today. But it is limited to an Active Directory to Active Directory user to contact sync design.

Today, we offer the Microsoft Graph Connector. It provides the ability to connect to an Azure AD tenant, and to manage B2B invitations. However, it is not a drop-in replacement for GALSync. We can get there, but we need to fill in some missing components.

There are many scenarios that Graph Connector could satisfy, and they can get increasingly difficult. For this blog I’ll focus on the simplest scenario and then discuss what considerations one would have in order to move 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 that you’ve moved past the point where you need identical GALs in both on-premises Exchange and Exchange Online. Most of the customers I work with have gotten to that point. Maybe they still have some service account mailboxes left to move, but all humans who need to view a GAL have been moved to Exchange Online. That simplifies the requirements as there’s no longer a need for creating on-premises 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 supported and still available for download. Otherwise, MIM is in extended support through 2026. To keep our infrastructure footprint small, this solution will use only a Sync Service install, and will not use the Portal or any declarative provisioning.

With a base MIM install in place we’re almost ready to make a Graph connection to our first tenant. But before we do so, we need to have a discussion of scope, because scope is the major factor in determining the complexity of solution. When talking about scope, I’m going to be very exact in terms of objects and attributes as much of our terminology in this space is vague and subject to perspective to understand meaning.

“We want users from a partner tenant….” Starting with this phrase, we need to break it down to a scoped object and attribute definition. The Graph Connector exposes user and contact (technically orgContact) object classes. We will only want to bring in users from the partner tenant. Except the user object class covers both internal and external (a.k.a. B2B) users. Typically, we only want to bring in the internal users from the partner tenant. We can tell the difference between them because external users have a creationType attribute equal to ‘Invitation’, whereas internal users have a null creationType. The other possible choice one might consider is the userType attribute with values of either ‘Guest’ or ‘Member’. But I think that is a poor 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 one’s default visibility to certain workloads such as SharePoint or Azure AD itself. Guest is sometimes incorrectly used interchangeably with B2B, but those two terms are not equivalent.

“Make B2B users in our home tenant….” Given the previous discussion, this phrase is now rather easy to scope. We’ll be looking at user objects with creationType=’Invitation’.

With our purposefully simplistic scope defined, let’s build the first Graph connection to the partner tenant. Install the Graph Connector on the MIM system. There have been lots of fixes recently so be sure to use the current version.

Start with creating a new Graph (Microsoft) connector.


Provide registered app credentials to connect to the partner tenant. The app registration needs 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, keep the Add objects filter unchecked. Unfortunately, we cannot use the filtering capability to return only the internal users where creationType is null. The Graph API provides more advanced filtering capabilities, but it requires a Header value to be set, which the Graph Connector does not currently expose as a configurable setting.

On the Select Object Types page check user.

On the Select Attributes page, let’s select a minimum number of attributes to enable decent GAL functionality as part of the B2B sync. Additional attributes can be added if the GAL needs to be more fully populated. Select creationType, displayName, givenName, id, mail, showInAddressList, and surname.



The anchor attribute on the Configure Anchors page will automatically be set to id.

On the Configure Connector Filter page, I will keep this example simple by using a declared filter of creationType Is present. This will filter out any external users that may happen to already exist in the partner tenant. But this filtering will come at the expense of increased Delta Sync times due to having to process each filtered disconnector every sync cycle.



For Configure Join and Projection Rules, we’ll join on id first, mail second, otherwise project as a person.



This is the inbound partner tenant user flow, so provide a direct inbound flow for each attribute. Several of the selected attributes are not default metaverse attributes, so the metaverse schema will need to be extended to account for these attributes.



Leave the Configure Deprovisioning page at the default of ‘Make them disconnectors’ and Configure Extensions page will also be left at its default of empty.

Create the Full Import and Full Sync run profiles. Execute them to confirm that the partner tenant users are projected into the metaverse. Also create the Delta Import and Delta Sync run profiles. We won’t use them now, but will need them later. I’ve gotten spoiled from AADC creating run profiles by default.

Now that the inbound side from the partner tenant is complete, let’s create the outbound side for the home tenant. The setup will be similar to the inbound side, but with some minor changes.

The App Registration in the home tenant will require the 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, that permission does not provide enough access for this scenario.

For the Schema 1 page, we’ll need to leave the Add objects filter checkbox uncheck again. Even though we could technically set a graph filter of creationType eq 'Invitation', using a filter breaks Delta Imports for the Graph Connector (with a no-start-ma error). We will have to continue to use MIM filtering the keep the scope correct since Delta Imports are very important for most of my customers.

On the Global Parameters page set the Invite redirect URL to https://myapps.microsoft.com/?tenantid=GUIDValue. Leave the send mail checkbox unchecked unless you want to start automatically spamming all your invitees.



On the Select Attributes page, include userPrincipalName and userType in addition to the list of attributes from the inbound side. We’re selecting UPN just so we can see the full results of the invitation process, not because we’ll be doing any syncing of that attribute.

For the Configure Connector Filter page, we reverse it from the inbound partner tenant setting and use a filter of creationType Is not present.

On the Configure Join and Projection Rules page, only add the Join Rule for mail. There should be no Projection Rule as we want all the external users to project into the metaverse from the inbound partner tenant.

For the Configure Attribute Flow page, add a direct export (allowing nulls) for displayName, givenName, mail, showInAddressList and surname. Add a constant export of Guest for userType. While an external user is typically Guest by default, the Graph Connector defaults to Member, so we need to override that. Also add a constant export of Invitation for creationType. For the creationType, we’re flowing that just 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 any matching mail values for existing external users those should join. Otherwise, the existing external users will show up as disconnectors. 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 amount of provisioning code to provision the external users from the metaverse into the home tenant MA. From the Tools > Options… menu check the Enable metaverse rules extension checkbox. Then click the Create Rules Extension Project… button. I’ll provide sample code for Visual C#, so choose that selection and the version of Visual Studio to use to compile the project.

This is a sample implementation for the IMVSynchronization.Provision method.

void IMVSynchronization.Provision (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 present, create a new connector.

    if (numConnectors == 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 rename is needed

    }

    else

    {

        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 we are managing. We also set a random GUID-based DN and id in order to successfully export the invitation, but those values will be replaced by the real Azure AD values during the first confirming import.

Build the solution in Visual Studio and make sure the extension DLL gets copied to the Microsoft Forefront Identity Manager\2010\Synchronization Service\Extensions folder. Back in the Options dialog, ensure the DLL that was just created is selected for the Rules extension name, and check the Enable Provisioning Rules Extension checkbox.



To begin with a small test, pick a sample user from the partner tenant MA and commit a Full Sync Preview against them. That should generate a pending export in the home tenant MA.



The small piece of magic with the Graph Connector is that if a user has a pending add with mail but no UPN, they will go through the invitation process to make them an external user, rather than being created as an internal user. We can see the pending export with the temporary DN and id, the GALSync attributes we wired up, and our constant userType of Guest. This test user has an OnMicrosoft.com mail address in the partner tenant as I have not added a custom domain to that tenant. The actual mail value is ultimately immaterial so long as it doesn’t already belong to the destination tenant.

Run the Export, followed by a confirming Delta Import.



We see that the user got successfully invited, got its real DN and id and has all the attributes we set. Notice the UPN got automatically set by AAD in the expected format of mail#EXT#@tenant. It also was given a default setting of showInAddressList = false. By default, invited external users are hidden from the GAL.

Complete a second delta sync cycle (Delta Import, Delta Sync, Export) and showInAddressList should get set to its synced value. For this example user, that would be a null value.


After exporting the updated showInAddressList value, we can confirm that our GALSync is functional. Log in to Outlook on the web in the home tenant, open the People app and select the All Users GAL. We should see our newly synced user present in the GAL.


Finally, to complete the deprovisioning aspect of the GALSync, configure the Object Deletion Rule for 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 delete of the external B2B user to our home tenant.

That’s the end of the setup for GALSync from a single source to a single destination tenant.

As I alluded to at the beginning, more complex setups are possible. Consider a bi-directional GALSync where the partner tenant also needs the users from our home tenant. 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 where one AADC is needed for each tenant. It allows the provisioning code to know the tenant for which it is responsible, cleanly separates inbound from outbound flows and causes no precedence problems. It also allows the partner to control the app registration which possesses write access into their tenant.

Or consider a full-mesh setup where the tenants are all peers in one org that decided to segment their tenants for some reason. We could design a single MIM solution that manages every tenant. We could do two connectors to each tenant to allow us to separate internal user from external and continue to manage the flows separately. We’d only have to prevent same-tenant provisioning in the provisioning code. I could also see a solution that uses only one connector to each tenant. We could come up with a mechanism to track 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 might have concern about the number of disconnectors and corresponding delta sync times, there are a few advanced techniques we could implement to alleviate that concern. We could project and terminate objects in the metaverse instead of keeping them as disconnectors. Or we could replace the Graph Connector with a PowerShell Connector and take care of all the Graph logic ourselves, avoiding the scenarios where the Graph Connector has limitations.

Hopefully, this has shed some light on considerations for a modern GALSync solution.

Thanks for spending a little bit of your time with me.

-Dave

 

Disclaimer: The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of 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 whatsoever (including, without limitation, 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.


Tuesday, February 16, 2021

MIM account deprovisioning déjà vu

I was doing some research for one of my customers that was having an AD account provisioning problem with MIM. I wanted to make sure that I wasn't introducing a potential deprovisioning problem with my change for them, so I was revisiting the deprovisioning scenarios in my lab.  I hit on this support forum article: FIM 2010 R2 Deprovisioning with Outbound System Scoping Filters (microsoft.com). Reading through that and get to the bottom and see that it was asked and summarized by me. About a decade ago. Totally forgot about that work. No deprovisioning improvements ever came to FIM/MIM. At least AADConnect does a nice job of declarative deprovisioning when falling out of scope of a provisioning rule.


[Update 2026.06.05 - Archive is gone, adding original forum content]

FIM 2010 R2 Deprovisioning with Outbound System Scoping Filters
This question is specific to the new Outbound System Scoping Filters in R2 and not the old(er) style MPR, Set and WF. Basically, how do you declaratively disconnect in this scenario? If I have an Outbound SR using Outbound System Scoping Filters, the Enable Deprovisioning option is greyed out. That option only becomes available when the older style MPR, Set and WF choice is used. When the MV object is in scope of the filter it gets successfully provisioned. But when it falls out of scope of the filter it remains connected. There is no provisioning disconnect generated. So I can't deprovision when using scoping filters??   

July 20th, 2012 11:09am

That's right you can't deprovision with the scope SRs - I think the doco is clear on this? To actually delete an object you could still use an outbound SR the original way, where you have to use an MPR and Workflow to attach and then remove the SR. At least I think you'd do that - I'd use a classic rule myself.http://www.wapshere.com/missmiis

July 23rd, 2012 6:57am

I don't think the documentation is clear on this at all. I had incorrectly assumed the deprovisioning would have been as simple as the provisioning part with an automatic link removal when falling out of scope, or at least the choice to check the Enable Deprovisioning checkbox to turn that piece on. But thanks for the confirmation of what I feared - that this feature can't be used for the full lifecycle management. I'll just add this to the list of functions that FIM doesn't do that we need to manage ourselves through our custom "mini-FIM" implementation. That now includes
  • Object deletes with unmanaged child objects
  • Cross-Domain moves
  • Writing binary formatted attributes like Terminal Services Profile
  • Object deletes when using Filter Based Outbound Synchronization
So since there's already a case where FIM can't delete an object that should be deleted, I may as well keep using the Filter Based OSRs and just handle all of the deletes myself.  

Friday, April 3, 2020

Migrated Code/Scripts From Technet Script Gallery To GitHub

If you did not know already Microsoft will be retiring Technet Script Gallery.  More information here: https://docs.microsoft.com/en-us/teamblog/technet-gallery-retirement



Currently it is in read-only mode, which means you can edit existing contributions as an owner, but you will not be able to add new contributions. In June 2020 Microsoft will shut it down. Therefore if you have your code stored there, make sure to get it and migrate it to GitHub. If you need scripts from it, go and get those scripts before the shutdown.



My scripts are still available in Technet Script Gallery, but I also already migrated them to my new GitHub public repository at https://github.com/dloder0/AAD-Connect-MIM-FIM-Scripts



Friday, January 24, 2020

Getting Reports from Long Running Performance Monitor Data Collector Sets


When trying to track down an elusive Active Directory performance problem, gathering stats using the Active Directory Diagnostics Data Collector Set is the best method for insight as to what the Domain Controller is doing. However, having super busy DCs and not knowing exactly when the problem is going to occur can make capturing the data and generating a useful report harder.  There are also limitations with the Data Collector Sets that we have to take into consideration to come up with a solution that really works.

Let me start with explaining how, and more importantly when, the various features of the Data Collector Sets work.

The first thing to know is that you cannot change the properties of the built-in System Data Collector Sets.  You must create a custom User Defined Data Collector Set to be able to change its behavior. Create a new Data Collector Set by right-clicking the User Defined node and select New > Data Collector Set. Give it a name, create it from an existing template. Use the Active Directory Diagnostics as the template. Finish the wizard.



The DCS has settings that can be used to keep the collection from consuming too much disk capacity. Right-click the new DCS and select Data Manager Settings.  The Resource Policy of Delete Oldest causes older folders to be deleted to keep from growing too large. You can adjust the size or folder count as needed. However, two things have to occur to have the data purging trigger.  First, the “Enable data management and report generation” property has to be selected.  It should be selected by default.  But second, these Data Manager rules don’t trigger until the DCS stops.  That’s an important distinction for the next set of DCS properties.



Now right-click the DCS, select Properties and switch to the Stop Condition tab. The Overall duration, which defaults to 5 minutes, controls when the DCS will automatically stop. Overall duration is the only setting that causes the DCS to stop on its own. The Limits section can be used to force the DCS to start using a new folder with new files for its collection, but setting a limit does not stop the DCS and therefore does not trigger the data purging configuration that is set in the Data Manager section.



The final feature of a DCS to explain is the report feature. Like data purging, report generation also only occurs when the DCS stops. Only the collection folder that was active when the DCS was stopped is used as the input source for the report. If you configure a limit and end up with multiple source folders for a single execution, only the last folder is used. Also, the size of the collected data in the collection folder determines how long the report generation will take.  Larger source data results in longer report generation times. While the report is being generated, all data collection has stopped and that same DCS cannot be restarted. Other than the report name in the Data Manager section, there is no GUI for managing report definitions.

With this explanation for how a DCS works complete, I have a problem I had been trying to solve for a customer of mine. They had an elusive Active Directory performance problem that they couldn’t predict when it would happen, couldn’t cause it to happen on demand and it could be many days before it reoccurred. But they could tell when it had happened and wanted more diagnostic data for what the Domain Controller was doing at that time.

So we needed our DCS to behave with the following characteristics:
  • Continuously capture data over several days without gaps
  • Do not save more data than the DC capacity
  • Generate a report against an identified collection
The first problem to solve is how to collect data without gaps yet still allow purging to run. The solution is to allow the DCS to stop, so purging can happen, but not run a report when it does stop so we don’t waste capture time running a report against a time period that likely didn’t include the event we were trying to capture.  There’s no GUI for the report definition.  It’s located in the XML of the DCS itself. Right-click your custom DCS and select Save Template. Open the XML in notepad and find the ReportSchema node. You’ll see there are nine report definitions that are included. Delete all the Imports except one and change the file to a non-existent filename. Having one invalid Import for the report causes the smallest possible report to be generated, which finishes in a fraction of a second, minimizing the amount of time we’re not collecting data. Having zero Report Imports causes a default set of reports to run, which we want to avoid since they take time to finish. Save the edited content to a new XML file.

That section of XML should now look something like this:
<ReportSchema>
     <Report name="wpdcAdvisor" version="1" threshold="9999">
         <Import file="%systemroot%\pla\reports\NoReport.xml">
         </Import>
     </Report>
</ReportSchema>
With this change to the XML complete, create a new DCS from a template, but this time browse to the edited XML file instead of selecting from the list.

For this new DCS, set the Data Manager purging rules as needed and clear the Overall duration checkbox for the Stop Condition. Now we have a DCS that we can start and when we stop it, it quickly stops, creates a very small, useless report and purges the oldest data. By clearing the Overall duration checkbox, this DCS will run until stopped by another method (manual or scripted). It will not stop on its own.

On my customer’s busy DCs we can’t let the collection run too long, otherwise there’s a chance a report can never be generated so our plan is to stop and start the DCS once an hour. The built in scheduler on the DCS properties isn’t that great so we’ll use Task Manager instead.  We created a small batch file with these commands:
logman.exe stop AD_DCS_NoReport
logman.exe start AD_DCS_NoReport
Then we created a task that ran under the SYSTEM account that ran that batch file once an hour.

After the event reoccurred, we knew what collection set the data should be in, so we needed to generate a report for that particular 1 hour block, which we have to do manually. In each DCS collection folder, you should see four files: Active Directory.etl, AD Registry.xml, NtKernel.etl and Performance Counter.blg. There is a fifth file that we need to generate the report. We will manually create it. Create a new text file in the folder called reportdefinition.txt. In that text file, add the following XML and save it.
<Report name="wpdcAdvisor" version="1" threshold="9999"><Import file="%systemroot%\pla\reports\Report.System.Common.xml"/><Import file="%systemroot%\pla\reports\Report.System.Summary.xml"/><Import file="%systemroot%\pla\reports\Report.System.Performance.xml"/><Import file="%systemroot%\pla\reports\Report.System.CPU.xml"/><Import file="%systemroot%\pla\reports\Report.System.Network.xml"/><Import file="%systemroot%\pla\reports\Report.System.Disk.xml"/><Import file="%systemroot%\pla\reports\Report.System.Memory.xml"/><Import file="%systemroot%\pla\reports\Report.System.Configuration.xml"/><Import file="%systemroot%\pla\Reports\Report.AD.xml"/></Report>
You may recognize that these are the same files that show up in the XML that we edited.

Finally, execute the following command line from within the capture directory you want to use for the report.
tracerpt.exe *.blg *.etl -df reportdefinition.txt -report report.html -f html
If everything went right, you should end up with a normal DCS diagnostic report that you can review which covers the time period from when the event occurred.

As a neat trick, if you need to see more than the top 25 items that the report defaults to, you can run the following command to get full XML output:
tracerpt.exe –lr "Active Directory.etl"
For additional reading on similar issues that led me to this solution, I offer up the Canberry PFE team blog Issues with Perfmon reporting - Turning ETL into HTML, the Directory Services Team blog Are your DCs too busy to be monitored?: AD Data Collector Set solutions for long report compile times or report data deletion and the Core Infrastructure and Security blog Taming Perfmon: Data Collector Sets.