Office 365 – Distribution List Migrations Version 2.0 – Part 1

Introduction to the Distribution List Migration Module version 2.0

In September 2018, I began the process of outlining and implementing a method for administrators to migrate distribution lists to Office 365. The goal was to provide a scripted method that not only dealt with the group and the group’s members – but all the dependencies that may exist on the group. This culminated in the release of the Distribution List Migration script v1.0.

Version 1.0 achieved many of the results that I had sought. Lists were migratable, the source of authority moved to the cloud, and the majority of the dependencies that may have existed on the group were retained in both the on-premises and Office 365 organizations. The script was dependent on an Exchange server existing in the environment – and leveraged many of the list management cmdlets that were available.

Today, I am publishing a PowerShell module for Distribution List migrations. This is the v2 iteration of the ideas first developed in v1. I have taken a great deal of time to address many of the customer concerns and feature requests. Here are some of the highlights:

  • Remove the requirement to have Exchange on-premises in order to perform the migrations.

In v1, it was a requirement to have a basic authenticated remote PowerShell session to an on-premises Exchange server in order to do migrations. This presented a unique challenge for some customers who had completed their migrations and uninstalled their last Exchange server. In v2, I moved all core functions to use LDAP and Active Directory PowerShell cmdlets. This eliminates the requirement to have Exchange on-premises and also increased the speed and performance of the overall migration. To accomplish this task, the script must be run from a machine that has the Active Directory Remote Server Administration Tools installed. This can be a Windows 10 client or a Windows Server 2012 R2 or later server. The script tests for the presence of the appropriate commands and will error if the module’s commands are not found.

  • Uses Exchange Online Management v2 PowerShell to perform Exchange Online operations.

In v1, the script used a basic authentication remote PowerShell session to Exchange Online. Using the legacy connection method relied on basic authentication – which is being replaced by modern authentication. Exchange Online Management v2 allows administrators to use either credentials or a certificate for authentication and it leverages modern authentication. The legacy connection was also prone to underlying disconnects of the HTTPS session, which would often result in the script failing during long duration operations. To compensate, v1 of the script routinely disconnected and reconnected the session after so many operations – resulting in a time delay in the overall migration process. Exchange Online Management v2 will automatically re-establish the session (if the underlying session is closed) when a command is called. The script is now available to GCC customers using modern authentication. Lastly, we can leverage the new Graph-based cmdlets to test for recipients, which provides further performance enhancements.

  • More flexibility with how credentials are provided to the script.

In v1, credentials were recycled across applications (Active Directory / Azure AD Connect / Exchange Online) and were stored in local files. This often highlighted the support boundaries within customer organizations. For example, one group might manage Active Directory but not have rights to the Azure AD Connect server to perform a sync. The script now ingests a set of credentials for each external connection that is made. As of publishing, there are four sets of credentials – Active Directory, Azure AD Connect, Exchange on-premises, and Exchange Online. The administrator can choose to recycle credentials,but it is no longer necessary to do so. The admin also has the flexibility to provide credentials in a manner that suits them, making it no longer required to read them from an XML file.

  • Module has support for multiple Office 365 environments.

The PowerShell module has been extended with options for use in multiple Office 365 environments including commercial, 21vianet, and GCC.

  • Forcing Azure AD Connect sync is no longer required.

In v1, a remote PowerShell session to the Azure AD Connect server was required in order to trigger a delta sync. In customer escalations, there were times where the desire to migrate a distribution list existed but the inability to establish remote PowerShell sessions or possess the necessary permissions to interact with Azure AD Connect were available. It is still HIGHLY RECOMMENDED that you provide an Azure AD Connect server and credentials. If this information is not provided – the script will loop until the standard synchronization cycle runs and the group is naturally removed from Office 365. This can add at maximum 30 minutes to the migration cycle of the distribution group.

  • Modified recipient verification from primary SMTP address to external directory object ID for user object types.

When a user object is synchronized via Azure AD Connect (modern / supported versions) the Azure Active Directory object ID is written back to the user. This flows into Exchange Online as the externalDirectoryObjectID of the recipient. In v1, safety checks to see if a recipient existed were done based on primary SMTP address. This could be somewhat ambiguous and did not necessarily ensure that the intended recipient existed in Office 365. The safety check of a user is now performed against the external directory object ID. This allows us to ensure that the user found in the on-premises directory matches the user found in Office 365.

  • Administrators now have the option to retain the original group.

In v1, the original group was always deleted post migration. This was done on purpose as a safety precaution against un-doing the distribution list migration. In Azure AD Connect, soft matching of groups occurs anytime the email address of the group on-premises matches the email address of the group in Azure Active Directory. In some cases, admins accidentally moved groups back into the sync scope thereby soft matching the original group to the migrated group. (This could be bad, as it rolls back the migration to the previous group state). In some environments, distribution lists were made as security groups – and several on-premises dependencies were introduced because these groups had permissions on any number of objects. This made deleting the group very challenging, and if the group was retained, the same soft match issue still existed. In v2, we allow the admin to retain the group on-premises. In a future post, I’ll highlight the logic that is used to accomplish this workflow.

  • Administrators can now choose the authentication method to Exchange on-premises remote PowerShell.

In v1, Basic Authentication via Exchange Remote PowerShell was required in order for the script to function. In v2, the admin has the option to specify Kerberos as the type of authentication desired. For Kerberos to function, only a single server should be specified in the Exchange server name. If you are using load-balanced Exchange servers, Basic Authentication should still be configured on the remote PowerShell directories and Basic Authentication should be used.

  • Provide administration interfaces to record dependencies on the distribution group including SendAs rights, full mailbox access, and individual folder permissions.

In v1, logic was provided to track distribution list dependencies, including SendAs and full mailbox access. Individual folder permissions were not retained if they were tied to a group. When used in large environments the total migration time of a distribution group was greatly increased due to the lack of filterable properties to track these items. In v2, we provide administrative interfaces to pre-collect this information into point in time copies. This allows admins to plan their migrations and collect this data. Performance is greatly improved by evaluating this data offline and further supports our no-loss migration strategy. In future posts, I’ll outline the usage of the commands.

You can find more information on the module in upcoming blog posts and at the links below.

PowerShell Gallery | DLConversionV2 2.1.3

GitHub – timmcmic/DLConversionV2

 

 


 

3 thoughts on “Office 365 – Distribution List Migrations Version 2.0 – Part 1

  1. Brian Fisk

    Is the module designed to also run from a server in a different AD domain than the source AD domain where groups are being migrated from?

    As part of a multiple forest consolidation project, I’m migrating DL’s from old forest (on-prem exchange removed) to Exchange Online and leaving them there. I’ve been running the module from my management server in the new forest, specifying a GC DC in the source forest. My AADC server is in the new forest (syncing source forest objects over the domain trust).

    It seemed to work for a few hundred migrations but hundreds more were failing with an error when calling Get-CanonicalName.ps1

    ### BEGIN ERROR SNIPPIT ###
    3/9/2022 7:27:35 AM] – BEGIN GET-CanoicalName
    [3/9/2022 7:27:35 AM] – ********************************************************************************
    [3/9/2022 7:27:35 AM] – GlobalCatalogServer = dancing.disco.stu:3268
    [3/9/2022 7:27:35 AM] – DN Set = CN=funkytown,CN=simpsons,DC=disco,DC=stu
    [3/9/2022 7:27:35 AM] – Credential user name = disco\adm_stu
    [3/9/2022 7:27:35 AM] – Gathering the AD object based on distinguished name.
    You cannot call a method on a null-valued expression.
    At C:\Program Files\WindowsPowerShell\Modules\DLConversionV2\2.5.12\Get-CanonicalName.ps1:100 char:13
    + $functionDomain=$functiontest.canonicalName.split(“/”)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Out-LogFile : You cannot call a method on a null-valued expression.
    At C:\Program Files\WindowsPowerShell\Modules\DLConversionV2\2.5.12\Get-CanonicalName.ps1:110 char:13
    + Out-LogFile -string $_ -isError:$true
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Out-LogFile
    ### END ERROR SNIPPIT ###

    From what I can tell, the Get-ADObject call on line 69 of Get-CanonicalName.ps1 is missing “-server $globalCatalogServer” so when run from a different domain (or from a non-domain controller?), $functionTest is null because no object is returned with the filter (neither is an error).

    ### BEGIN CODE SNIPPIT (Line #69) ###
    $functionTest = get-adobject -filter {distinguishedname -eq $dn} -properties canonicalName -credential $adCredential -errorAction STOP
    ### END CODE SNIPPIT ###

    If it is meant to only be run on a system joined to the domain where you are migrating from, can I expect the “successful” migrations to be stable? I haven’t seen or heard of issues in post-testing.

    Why some were successful and others weren’t is not clear to me other than it looks like failed migrations were involving nested groups. I’ve moved to running the module directly on a DC in the source domain and I don’t have the same error.

    Thank you!!

    Like

    Reply
  2. Pingback: Office 365 – Distribution List Migrations Version 2 | TIMMCMIC

  3. Pingback: Office 365 – Distribution List Migration – Version 2.0 | TIMMCMIC

Leave a comment