Inter-forest Migration: How to use a “hybrid” ImmutableID when federating with Azure AD

Introduction

When you are working on an inter-forest migration in an environment that is integrated with Azure AD / Office 365 you should take care how to manage the ImmutableID. This has impact on how Single Sign On works with Azure AD, and especially with some of the services depending on Azure AD. This post describes how you can deal with such a challenge.

Requirements

In our case the following requirements had to be met:

  • Migrate user objects from forest A to forest B;
  • Use one single ADFS farm for both forest A and forest B;
  • Minimize the dependencies and achieve more flexibility during the migration;
  • Assure that “new” users in forest B can use their native ImmutableID (based on ObjectGUID);
  • Assure that “migrated” users in forest B can use their old ImmutableID until this attribute is updated in Azure AD.

In this scenario we have chosen to maintain the original ImmutableID (depending on the ObjectGUID) for forest A users and migrate this attribute with the user object to Forest B. To assure that ADFS is able to use this ImmutableID for migrated users and still use the native ImmutableID for not-migrated users we had to make some changes in the “Microsoft Office 365 Identity Platform” relying party in ADFS.

The attribute where we stored the “hybrid” ImmutableID is Active Directory extensionAttribute2.

Current situation

The original rules in this environment where as follows:

Rule 1: Query Active Directory and issue UserPrincipalName and ObjectGUID as ImmutableID.

c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname”]

=> issue(store = “Active Directory”, types = (“http://schemas.xmlsoap.org/claims/UPN”, “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID”), query = “samAccountName={0};userPrincipalName,objectGUID;{1}”, param = regexreplace(c.Value, “(?<domain>[^\\]+)\\(?<user>.+)”, “${user}”), param = c.Value);

Rule 2:

c:[Type == “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID”]

=> issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier”, Value = c.Value, Properties[“http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format”] = “urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified”);

Rule 3: (Optional if you use multi domain support)

c:[Type == “http://schemas.xmlsoap.org/claims/UPN”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid”, Value = regexreplace(c.Value, “.+@(?<domain>.+)”, “http://${domain}/adfs/services/trust/”));

Approach

To comply to the defined requirements we had to set-up a set workflow that supports the use of extensionAttribute2 as ImmutableID for migrated users and leaving the use of the original ImmutableID for non-migrated users and new users. In our case the following logic was defined:

If extensionAttribute2 has a value ending with “==” then issue this value as ImmutableID. If extensionAttribute2 does not end with “==” then issue ObjectGUID as Immutable ID.

The following MSOL Federation claims are left intact:

  • UPN;
  • ImmutableID –> NameIdentifier.

Solution

As ADFS only supports only issuance statement per rule, we need quite a bit more rules to achieve our goals.

Rule 1: Query Active Directory attribute store and create attributes that are used in subsequent rules. The following attributes are queries in Active Directory: UPN, ObjectGUID and extensionAttribute2.

c:[Type == “http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname”]

=> add(store = “Active Directory”, types = (“UPN_ADValue”, “ImmutableID_ADValue”, “extensionAttribute2_ADValue”), query = “samAccountName={0};userPrincipalName,objectGUID,extensionAttribute2;{1}”, param = regexreplace(c.Value, “(?<domain>[^\\]+)\\(?<user>.+)”, “${user}”), param = c.Value);

Rule 2: Issue UPN_ADValue as the UPN without modification

c:[Type == “UPN_ADValue”]

=> issue(Type = “http://schemas.xmlsoap.org/claims/UPN”, Value = c.Value);

Rule 3: Check if extensionAttribute2 ends with “==”, if true this would indicate that this value should be a valid ImmutableID value. Then this rule issues this value as ImmutableID.

c:[Type == “extensionAttribute2_ADValue”, Value =~ “==$”]

=> issue(Type = “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID”, Value = c.Value);

Rule 4: If extensionAttribute2 does not ends with “==”, extensionAttribute2_ADValueExists value is set to false as passed to be used in subsequent rules indicating that the extensionAttribute2 does not contain a correct value or is not set.

NOT EXISTS([Type == “extensionAttribute2_ADValue”, Value =~ “==$”])

=> add(Type = “extensionAttribute2_ADValueExists”, Value = “FALSE”);

Rule 5: If extensionAttribute2_ADValueExists is False, ImmutableID will be based on the ObjectGUID of the Active Directory object. This rule also makes sure that “new” objects in forest B can use their native ObjectGUID as ImmutableID.

c1:[Type == “ImmutableID_ADValue”]

&& c2:[Type == “extensionAttribute2_ADValueExists”, Value == “FALSE”]

=> issue(Type = “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID”, Value = c1.Value);

Rule 6: Containing the original value used for federation with Microsoft Online

c:[Type == “http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID”]

=> issue(Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier”, Value = c.Value, Properties[“http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format”] = “urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified”);

Rule 7: (Optional if you use multi domain support)

c:[Type == “http://schemas.xmlsoap.org/claims/UPN”]
=> issue(Type = “http://schemas.microsoft.com/ws/2008/06/identity/claims/issuerid”, Value = regexreplace(c.Value, “.+@(?<domain>.+)”, “http://${domain}/adfs/services/trust/”));

Conclusion

Using these rules as a replacement for the original rules it was possible to create a scenario where the migration dependencies are minimized related to ADFS and still enabling existing users in forest B to use the ImmutableID based on their ObjectGUID.

Please note that this solution should only be used during the migration and when you finish your migration project you should alter the ImmutableIDs in Azure AD to match to the ImmutableID based on the ObjectGUID in forest B.

This concludes the ADFS part of this migration, I’m planning to post an additional post that handles AAD Connect which has more or less the same challenge.

leave your comment