DistributionGroupMigration.psm1

function Connect-ExchangeOnline {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        # Specifies that no MFA will be used.
        [Parameter()]
        [switch]$NoMFA
    )

    if (Get-Command -Name 'Get-UnifiedGroup' -ErrorAction SilentlyContinue) {
        return $true
    }
    else {
        if ($NoMFA) {
            try {
                $CurrentWarningPreference = $WarningPreference
                $WarningPreference = 'SilentlyContinue'
                Connect-EXOLegacy
                if (Get-Command -Name 'Get-UnifiedGroup' -ErrorAction SilentlyContinue) {
                    return $true
                }
                else {
                    return $false
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $WarningPreference = $CurrentWarningPreference
            }
        }
        else {
            try {
                $CurrentWarningPreference = $WarningPreference
                $WarningPreference = 'SilentlyContinue'
                Connect-EXO
                if (Get-Command -Name 'Get-UnifiedGroup' -ErrorAction SilentlyContinue) {
                    return $true
                }
                else {
                    return $false
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $WarningPreference = $CurrentWarningPreference
            }
        }
    }
}
function Connect-ExchangeOnPremise {
    [CmdletBinding()]
    [OutputType([bool])]
    param(
        # Specifies an Exchange On-premise server hosting the PowerShell endpoint.
        [Parameter()]
        [string]$ExchangeServer
    )

    if (Get-Command -Name 'Get-EXCHMailbox' -ErrorAction SilentlyContinue) {
        return $true
    }
    else {
        try {
            $CurrentWarningPreference = $WarningPreference
            $WarningPreference = 'SilentlyContinue'
            $Session = New-PSSession -Name EXCH -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$($ExchangeServer)/PowerShell/" -Authentication Kerberos -AllowRedirection -ErrorAction Stop -WarningAction SilentlyContinue
            Import-Module (Import-PSSession -Session $Session -Prefix EXCH -ErrorAction Stop -WarningAction SilentlyContinue) -Prefix EXCH -Global -ErrorAction Stop -WarningAction SilentlyContinue
            return $true
        }
        catch {
            try {
                Write-PSFMessage -Level Warning -Message 'Unable to use Kerberos authentication for Exchange On-premise. Provide credentials to try again.'
                $Session = New-PSSession -Name EXCH -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$($ExchangeServer)/PowerShell/" -Credential (Get-Credential -Message 'Exchange On-premise credentials') -AllowRedirection -ErrorAction Stop -WarningAction SilentlyContinue
                Import-Module (Import-PSSession -Session $Session -Prefix EXCH -ErrorAction Stop -WarningAction SilentlyContinue) -Prefix EXCH -Global -ErrorAction Stop -WarningAction SilentlyContinue
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
        finally {
            $WarningPreference = $CurrentWarningPreference
        }
    }
}
function Complete-OnPremDSTGroupToCloud {
    <#
    .SYNOPSIS
    Completes a migration of one or more synchronized (Azure AD Connect) distribution groups in Exchange Online generated from Initialize-OnPremDSTGroupToCloud.
 
    .DESCRIPTION
    Complete-OnPremDSTGroupToCloud will rename and remove the prefix from the distribution group created from Initialize-OnPremDSTGroupToCloud.
    Complete-OnPremDSTGroupToCloud requires that PSFClixml objects exist in the target LogPath location which is generated from Initialize-OnPremDSTGroupToCloud.
    The function Complete-OnPremDSTGroupToCloud goes through the following steps:
 
    1. Validate that the initialized distribution group is exist in Exchange Online.
    2. Validate that the old synchronized distribution does not exist in Exchange Online.
    3. Remove the prefix from all properties on the initialized distribution group in Exchange Online.
 
    The following properties will have the prefix removed for the initialized distribution group:
    "Alias", "DisplayName", "Name", "PrimarySmtpAddress", "EmailAddresses"
 
    .EXAMPLE
    Complete-OnPremDSTGroupToCloud -Group 'dstgroup001@contoso.com'
 
    [11:12:06][Complete-OnPremDSTGroupToCloud] Successfully removed the prefix from all properties on "dstgroup001@contoso.com".
    [11:12:06][Complete-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after completion, to "C:\Users\UserName\AppData\Roaming\WindowsPowerShell\PSFramework\Logs".
 
    This example retrieves the exported initialized distribution group object from the default path and removes the prefix.
 
    .EXAMPLE
    Complete-OnPremDSTGroupToCloud -Group 'dstgroup002@contoso.com' -LogPath "C:\Log"
 
    [11:12:06][Complete-OnPremDSTGroupToCloud] Successfully removed the prefix from all properties on "dstgroup002@contoso.com".
    [11:12:06][Complete-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after completion, to "C:\Log".
 
    This example retrieves the exported initialized distribution group object from the defined path "C:\Log" and removes the prefix.
    The LogPath parameter specifies an alternate path for where all logs and the distribution group XML-objects is created from Initialize-OnPremDSTGroupToCloud.
 
    .EXAMPLE
    Complete-OnPremDSTGroupToCloud -Group 'dstgroup003@contoso.com' -NoMFA
 
    [11:12:06][Complete-OnPremDSTGroupToCloud] Successfully removed the prefix from all properties on "dstgroup003@contoso.com".
    [11:12:06][Complete-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after completion, to "C:\Users\UserName\AppData\Roaming\WindowsPowerShell\PSFramework\Logs".
 
    This example retrieves the exported initialized distribution group object from the default path and removes the prefix.
    When NoMFA switch is issued the connection to Exchange Online PowerShell will be using the native experience instead of modern authentication.
 
    .LINK
    https://github.com/PhilipHaglund/DistributionGroupMigration/blob/master/docs/en-US/Complete-OnPremDSTGroupToCloud.md
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies one or more distribution groups to be migrated. Recommended to use PrimarySmtpAddress as input to have a unique value.
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [Alias('PrimarySmtpAddress')]
        [string[]]$Group,

        <#
        Specifies the path for all logs and the distribution group XML-objects.
        Default the LogPath uses the 'PSFramework.Logging.FileSystem.LogPath' which defaults to "$env:APPDATA\WindowsPowerShell\PSFramework\Logs" for Windows PowerShell and
        "$env:APPDATA\PowerShell\PSFramework\Logs" for PowerShell Core.
        #>

        [Parameter()]
        [string]$LogPath = (Get-PSFConfigValue -FullName 'PSFramework.Logging.FileSystem.LogPath'),

        # Specifies that No MFA will be used when connecting to Exchange Online.
        [Parameter()]
        [switch]$NoMFA
    )
    begin {
        try {
            Set-PSFConfig -FullName 'PSFramework.Logging.FileSystem.LogPath' -Value $LogPath -ErrorAction Stop
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
        if ($PSCmdlet.ShouldProcess('Office365', 'Connect-ExchangeOnline')) {
            try {
                $PreviousErrorActionPreference = $ErrorActionPreference
                $ErrorActionPreference = 'Continue'
                if (Connect-ExchangeOnline -NoMFA:$NoMFA -ErrorAction Stop -WarningAction SilentlyContinue) {
                    Write-PSFMessage -Level Verbose -Message 'Connected to Exchange Online.'
                }
                else {
                    throw [System.AccessViolationException]::New('Unable to establish a session to Exchange Online')
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $ErrorActionPreference = $PreviousErrorActionPreference
            }
        }

        [regex]$PrefixAttribute = 'Alias|DisplayName|Name|PrimarySmtpAddress'
    }
    process {
        :Group foreach ($GroupId in $Group) {
            if ($PSCmdlet.ShouldProcess($GroupId)) {
                try {
                    $ExoGroup = $null
                    $DistributionGroupObject = [PSCustomObject]@{
                        'EXO'              = $null
                        'EXCH'             = $null
                        'Manager'          = $null
                        'Members'          = $null
                        'InitializedGroup' = $null
                        'CompletedGroup'   = $null
                        'Prefix'           = $null
                    }

                    $DistributionGroupObject = Import-PSFClixml -Path "$LogPath\$GroupId.byte" -ErrorAction Stop

                    try {
                        $ExoGroup = Get-DistributionGroup -Identity $DistributionGroupObject.EXO.PrimarySmtpAddress -ErrorAction Stop
                        if ($null -ne $ExoGroup) {
                            Write-PSFMessage -Level Warning -Message ('The original synchronized distribution group {0} still exist in Exchange Online. Will not continue with current group.' -f $GroupId)
                            continue
                        }
                        elseif ($ExoGroup.Count -gt 1) {
                            Write-PSFMessage -Level Warning -Message ('More than one distribution group found in Exchange Online with identity {0}. Will not continue with current group.' -f $GroupId)
                            continue
                        }
                    }
                    catch {
                        Write-PSFMessage -Level Verbose -Message ('Original synchronized distribution group is correctly removed from Exchange Online.' -f $GroupId)
                    }

                    $InitializedGroup = Get-DistributionGroup $DistributionGroupObject.InitializedGroup.PrimarySmtpAddress -ErrorAction Stop
                    if ($null -eq $InitializedGroup) {
                        Write-PSFMessage -Level Warning -Message ('Unable to find synchronized initialized distribution group {0} in Exchange Online. Will not continue with current group.' -f $GroupId)
                        continue
                    }
                    elseif ($InitializedGroup.Count -gt 1) {
                        Write-PSFMessage -Level Warning -Message ('More than one initialized distribution group found in Exchange Online with identity {0}. Will not continue with current group.' -f $GroupId)
                        continue
                    }

                    Write-PSFMessage -Level Host -Message ('Successfully retrieved the initialized distribution group with identity "{0}".' -f $InitializedGroup.PrimarySmtpAddress)

                    $Command = Get-Command -Name Set-DistributionGroup -ErrorAction Stop
                    [hashtable]$InitializeSetGroup = @{ }

                    foreach ($Parameter in ($Command.Parameters.GetEnumerator() | Sort-Object)) {
                        if ($Parameter.Key -match $PrefixAttribute) {
                            Write-PSFMessage -Level Verbose -Message ('Removing prefix "{0}" for property {0}.' -f $DistributionGroupObject.Prefix, $Parameter.Key)
                            $InitializeSetGroup.Add("$($Parameter.Key)", ($DistributionGroupObject.InitializedGroup."$($Parameter.Key)" -replace $DistributionGroupObject.Prefix))
                        }
                        elseif ($Parameter.Key -match 'HiddenFromAddressListsEnabled') {
                            $HiddenFromAddressListsEnabled = switch ($DistributionGroupObject.EXO.HiddenFromAddressListsEnabled) {
                                $true { $true }
                                $false { $false }
                                default { $true }
                            }
                        }
                    }
                    $EmailAddresses = foreach ($Address in $DistributionGroupObject.InitializedGroup.EmailAddresses) {
                        if ($Address -match '^smtp\:') {
                            $Address -replace ('^(smtp\:){0}(.+)' -f $DistributionGroupObject.Prefix), '$1$2'
                        }
                    }

                    Write-PSFMessage -Level Verbose -Message ('Updating distribution group {0}.' -f $DistributionGroupObject.InitializedGroup.PrimarySmtpAddress)
                    Set-DistributionGroup -Identity $DistributionGroupObject.InitializedGroup.PrimarySmtpAddress @InitializeSetGroup -HiddenFromAddressListsEnabled:$HiddenFromAddressListsEnabled -ErrorAction Stop
                    Set-DistributionGroup -Identity $InitializeSetGroup['PrimarySmtpAddress'] -EmailAddresses $EmailAddresses -ErrorAction Stop
                    $CompletedGroup = Get-DistributionGroup -Identity $InitializeSetGroup['PrimarySmtpAddress'] -ErrorAction Stop
                    Write-PSFMessage -Level Host -Message ('Successfully removed the prefix from all properties on {0}.' -f $InitializeSetGroup['PrimarySmtpAddress'])

                    $DistributionGroupObject | Add-Member -MemberType NoteProperty -Name CompletedGroup -Value $CompletedGroup -Force -ErrorAction Stop
                    $DistributionGroupObject | Export-PSFClixml -Path "$LogPath\Completed_$GroupId.byte" -Depth 5 -ErrorAction Stop
                    Write-PSFMessage -Level Host -Message ('Exported a PSFClixml object of the distribution group, before and after completion, to "{0}".' -f "$LogPath\Completed_$GroupId.byte")
                    Write-PSFMessage -Level Verbose -Message ('Use Import-PFClixml -Path "{0}" to view the content.' -f "$LogPath\Completed_$GroupId.byte")
                }
                catch {
                    if ($DistributionGroupObject) {
                        $DistributionGroupObject | Export-PSFClixml -Path "$LogPath\$GroupId.byte" -Depth 5 -ErrorAction Stop
                        Write-PSFMessage -Level Critical -Message ('Error occurred, will export current configuration of all distribution groups collected and its properties to a PSFClixml object to {0}.' -f "$LogPath\$GroupId.byte")
                        Write-PSFMessage -Level Verbose -Message ('Use Import-PFClixml -Path "{0}" to view the content.' -f "$LogPath\$GroupId.byte")
                    }
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
    }
}
function Initialize-OnPremDSTGroupToCloud {
    <#
    .SYNOPSIS
    Creates a copy of one or more synchronized (Azure AD Connect) distribution groups in Exchange Online with a defined prefix.
 
    .DESCRIPTION
    Initialize-OnPremDSTGroupToCloud will create a copy of one or more synchronized (Azure AD Connect) distribution groups in Exchange Online with a defined prefix, default 'PreMig-.
    The function Initialize-OnPremDSTGroupToCloud goes through the following steps:
 
    1. Validate that the distribution group is exist in both Exchange Online and Exchange On-premises and that the property, "IsDirSynced", exist for the Exchange Online distribution group.
    2. Validate Members of the distribution group. If the Members object is not existing in Exchange Online as a valid mail recipient the function will hard fail unless the force parameter is used.
    3. Validate ManagedBy of the distribution group. If the ManagedBy object is not an existing in Exchange Online as a valid mail recipient the function will hard fail unless the force parameter is used.
    4. Creates a copy of the synchronized distribution group in Exchange Online with a defined prefix on all properties that must remain unique.
 
    The following properties will receive the prefix for the created distribution group:
    "Alias", "DisplayName", "Name", "PrimarySmtpAddress", "EmailAddresses"
 
    .EXAMPLE
    Initialize-OnPremDSTGroupToCloud -Group 'dstgroup001@contoso.com' -ExchangeServer exchprod01.contoso.com
 
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Successfully created a cloud only distribution group with identity "PreMig-dstgroup001@contoso.com".
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after initialization, to "C:\Users\UserName\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\dstgroup001@contoso.com.byte".
 
    This example creates a "Cloud Only" (Exchange Online) copy of the Exchange On-premise distribution group 'dstgroup001@contoso.com' with the prefix 'PreMig-'.
 
    .EXAMPLE
    Initialize-OnPremDSTGroupToCloud -Group 'dstgroup002@contoso.com' -ExchangeServer exchprod01.contoso.com -Prefix 'Mig1234-'
 
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Successfully created a cloud only distribution group with identity "Mig1234-dstgroup002@contoso.com".
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after initialization, to "C:\Users\UserName\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\dstgroup002@contoso.com.byte".
 
    This example creates a "Cloud Only" (Exchange Online) copy of the Exchange On-premise distribution group 'dstgroup001@contoso.com' with the prefix 'Mig1234-'.
 
    .EXAMPLE
    Initialize-OnPremDSTGroupToCloud -Group 'dstgroup003@contoso.com' -ExchangeServer exchprod01.contoso.com -Force
 
    WARNING: [11:12:03][Initialize-OnPremDSTGroupToCloud] Excluding manager Administrator@contoso.local for group dstgroup003@contoso.com because recipient does not exist in Exchange Online as a valid recipient.
    WARNING: [11:12:03][Initialize-OnPremDSTGroupToCloud] Excluding member Administrator@contoso.local for group dstgroup003@contoso.com because recipient does not exist in Exchange Online as a valid recipient.
 
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Successfully created a cloud only distribution group with identity "PreMig-dstgroup003@contoso.com".
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after initialization, to "C:\Users\UserName\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\dstgroup003@contoso.com.byte".
 
    This example creates a "Cloud Only" (Exchange Online) copy of the Exchange On-premise distribution group 'dstgroup001@contoso.com' with the prefix 'PreMig-'.
    If any member of the Members property or and manager of the ManagedBy property does not exist as a valid mail recipient in Exchange Online,
    that member or manager will be excluded from the created distribution group in Exchange Online.
 
    .EXAMPLE
    Initialize-OnPremDSTGroupToCloud -Group 'dstgroup004@contoso.com' -ExchangeServer exchprod01.contoso.com -NoMFA
 
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Successfully created a cloud only distribution group with identity "PreMig-dstgroup004@contoso.com".
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after initialization, to "C:\Users\UserName\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\dstgroup004@contoso.com.byte".
 
    This example creates a "Cloud Only" (Exchange Online) copy of the Exchange On-premise distribution group 'dstgroup001@contoso.com' with the prefix 'Mig1234-'.
    When NoMFA switch is issued the connection to Exchange Online PowerShell will be using the native experience instead of modern authentication.
 
    .EXAMPLE
    Initialize-OnPremDSTGroupToCloud -Group 'dstgroup005@contoso.com' -ExchangeServer exchprod01.contoso.com -LogPath "C:\Log"
 
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Successfully created a cloud only distribution group with identity "PreMig-dstgroup005@contoso.com".
    [11:12:06][Initialize-OnPremDSTGroupToCloud] Exported a PSFClixml object of the distribution group, before and after initialization, to "C:\Log\dstgroup005@contoso.com.byte".
 
    This example creates a "Cloud Only" (Exchange Online) copy of the Exchange On-premise distribution group 'dstgroup005@contoso.com' with the prefix 'PreMig-'.
    The LogPath parameter specifies an alternate path for all logs and the distribution group XML-objects.
 
    .LINK
    https://github.com/PhilipHaglund/DistributionGroupMigration/blob/master/docs/en-US/Initialize-OnPremDSTGroupToCloud.md
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies one or more distribution groups to be migrated. Recommended to use PrimarySmtpAddress as input to have a unique value.
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [Alias('PrimarySmtpAddress')]
        [string[]]$Group,

        # Specifies an Exchange On-premise server hosting the PowerShell endpoint.
        [Parameter(
            Mandatory
        )]
        [Alias('EXCH')]
        [string]$ExchangeServer,

        <#
        Specifies a prefix to be used when creating a duplicate distribution group. Default value 'PreMig-'
        To avoid already existing prefixes use a prefix which is unique.
        Validation will occur against the regular expression "^[a-z0-9]{4,9}\-".
        "a-z" a single character in the range between a and z (case insensitive)
        "0-9" a single character in the range between 0 and 9 (case insensitive)
        "{4,9}" Matches between 4 and 9 times, as many times as possible
        "\-"" matches the character - literally (case insensitive)
        #>

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern("^[a-z0-9]{4,9}\-")]
        [string]$Prefix = 'PreMig-',

        # Specifies that managers and members of a distribution group will be removed from the distribution group if they don't are eligible to be a manager or member of a cloud only distribution group.
        [Parameter()]
        [switch]$Force,

        <#
        Specifies the path for all logs and the distribution group XML-objects.
        Default the LogPath uses the 'PSFramework.Logging.FileSystem.LogPath' which defaults to "$env:APPDATA\WindowsPowerShell\PSFramework\Logs" for Windows PowerShell and
        "$env:APPDATA\PowerShell\PSFramework\Logs" for PowerShell Core.
        #>

        [Parameter()]
        [string]$LogPath = (Get-PSFConfigValue -FullName 'PSFramework.Logging.FileSystem.LogPath'),

        # Specifies that No MFA will be used when connecting to Exchange Online.
        [Parameter()]
        [switch]$NoMFA
    )
    begin {
        try {
            Set-PSFConfig -FullName 'PSFramework.Logging.FileSystem.LogPath' -Value $LogPath -ErrorAction Stop
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
        if ($PSCmdlet.ShouldProcess('Office365', 'Connect-ExchangeOnline')) {
            try {
                $PreviousErrorActionPreference = $ErrorActionPreference
                $ErrorActionPreference = 'Continue'
                if (Connect-ExchangeOnline -NoMFA:$NoMFA -ErrorAction Stop -WarningAction SilentlyContinue) {
                    Write-PSFMessage -Level Verbose -Message 'Connected to Exchange Online.'
                }
                else {
                    throw [System.AccessViolationException]::New('Unable to establish a session to Exchange Online')
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $ErrorActionPreference = $PreviousErrorActionPreference
            }
        }
        if ($PSCmdlet.ShouldProcess($ExchangeServer, 'Connect-ExchangeOnPremise')) {
            try {
                $PreviousErrorActionPreference = $ErrorActionPreference
                $ErrorActionPreference = 'Continue'
                if (Connect-ExchangeOnPremise -ExchangeServer $ExchangeServer -ErrorAction Stop -WarningAction SilentlyContinue) {
                    Write-PSFMessage -Level Verbose -Message 'Connected to Exchange On-premise.'
                }
                else {
                    throw [System.AccessViolationException]::New('Unable to establish a session to Exchange On-Premise server {0}. Error: {1}' -f $ExchangeServer, $_.Exception)
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $ErrorActionPreference = $PreviousErrorActionPreference
            }
        }
        [array]$ValidRecipientTypeDetails = @('UserMailbox', 'LegacyMailbox' , 'SharedMailbox' , 'TeamMailbox' , 'MailUser' , 'LinkedMailbox' , 'RemoteUserMailbox' , 'RemoteSharedMailbox', 'RemoteTeamMailbox', 'MailContact', 'User', 'UniversalSecurityGroup', 'MailUniversalSecurityGroup')
        [regex]$ExcludeNew = 'ManagedBy|OrganizationalUnit'
        [regex]$ExcludeSet = 'ManagedBy|OrganizationalUnit|Alias|DisplayName|Name|PrimarySmtpAddress|Identity|WindowsEmailAddress|UMDtmfMap'
        [regex]$AddPrefix = 'Alias|DisplayName|Name|PrimarySmtpAddress'
    }
    process {
        :Group foreach ($GroupId in $Group) {
            if ($PSCmdlet.ShouldProcess($GroupId)) {
                try {
                    $ExoGroup = $null
                    $ExchGroup = $null
                    $ManagerId = $null
                    $OnlineManagerId = $null
                    $Members = $null
                    $Member = $null
                    $OnlineMemberId = $null
                    [Collections.ArrayList]$ValidManagers = [Collections.ArrayList]::new()
                    [Collections.ArrayList]$ValidMembers = [Collections.ArrayList]::new()
                    $DistributionGroupObject = [PSCustomObject]@{
                        'EXO'              = $null
                        'EXCH'             = $null
                        'Manager'          = $null
                        'Members'          = $null
                        'InitializedGroup' = $null
                        'Prefix'           = $Prefix
                    }

                    $ExoGroup = Get-DistributionGroup -Identity $GroupId -Filter { IsDirSynced -eq $true } -ErrorAction Stop
                    if ($null -eq $ExoGroup) {
                        Write-PSFMessage -Level Warning -Message ('Unable to find synchronized distribution group {0} in Exchange Online. Will not continue with current group.' -f $GroupId)
                        continue
                    }
                    elseif ($ExoGroup.Count -gt 1) {
                        Write-PSFMessage -Level Warning -Message ('More than one distribution group found in Exchange Online with identity {0}. Will not continue with current group.' -f $GroupId)
                        continue
                    }
                    $DistributionGroupObject.EXO = $ExoGroup

                    $ExchGroup = Get-ExchDistributionGroup -Identity $ExoGroup.PrimarySmtpAddress -ErrorAction Stop
                    if ($null -eq $ExchGroup) {
                        Write-PSFMessage -Level Warning -Message ('Unable to find distribution group {0} in Exchange On-premises. Will not continue with current group.' -f $GroupId) -WarningAction Continue
                        continue
                    }
                    elseif ($ExchGroup.Count -gt 1) {
                        Write-PSFMessage -Level Warning -Message ('More than one distribution group found in Exchange On-premises with identity {0}. Will not continue with current group.' -f $GroupId) -WarningAction Continue
                        continue
                    }
                    $DistributionGroupObject.EXCH = $ExchGroup

                    if ($PSBoundParameters.ContainsKey('Force') -and $ExchGroup.Managedby.Count -eq 0) {
                        Write-PSFMessage -Level Warning -Message ('No existing manager found for group {0}. Will add current user as manager.' -f $GroupId) -WarningAction Continue
                    }
                    elseif ($ExchGroup.Managedby.Count -eq 0) {
                        Write-PSFMessage -Level Warning -Message ('No existing manager found. Will not continue with current group {0}.' -f $GroupId) -WarningAction Continue
                        continue Group
                    }
                    foreach ($Manager in $ExchGroup.Managedby) {
                        $ManagerId = (Get-EXCHRecipient -Identity $Manager -ErrorAction SilentlyContinue).PrimarySmtpAddress
                        $OnlineManagerId = Get-Recipient -Identity $ManagerId -ErrorAction SilentlyContinue
                        if ($OnlineManagerId.RecipientTypeDetails -in $ValidRecipientTypeDetails) {
                            $null = $ValidManagers.Add($OnlineManagerId)
                        }
                        elseif ($PSBoundParameters.ContainsKey('Force')) {
                            Write-PSFMessage -Level Warning -Message ('Excluding manager {0} for group {1} because recipient does not exist in Exchange Online as a valid recipient.' -f $ManagerId, $GroupId) -WarningAction Continue
                        }
                        else {
                            Write-PSFMessage -Level Warning -Message ('Manager {0} does not exist in Exchange Online as a valid recipient. Will not continue with current group {1}.' -f $ManagerId, $GroupId) -WarningAction Continue
                            continue Group
                        }
                    }
                    $DistributionGroupObject.Manager = $ValidManagers

                    $Members = Get-EXCHDistributionGroupMember -Identity $ExchGroup.Identity -ErrorAction SilentlyContinue
                    if ($PSBoundParameters.ContainsKey('Force') -and $null -eq $Members) {
                        Write-PSFMessage -Level Warning -Message ('Excluding all members of {0} because no member recipients was found.' -f $ExchGroup.Identity) -WarningAction Continue
                    }
                    elseif ($null -eq $Members) {
                        Write-PSFMessage -Level Warning -Message ('No members found in group. Will not continue with current group {1}.' -f $ExchGroup.Identity, $GroupId) -WarningAction Continue
                        continue Group
                    }
                    foreach ($Member in $Members) {
                        $OnlineMemberId = Get-Recipient -Identity $Member.PrimarySmtpAddress -ErrorAction SilentlyContinue
                        if ($OnlineMemberId.RecipientTypeDetails -in $ValidRecipientTypeDetails) {
                            $null = $ValidMembers.Add($OnlineMemberId)
                        }
                        elseif ($PSBoundParameters.ContainsKey('Force')) {
                            Write-PSFMessage -Level Warning -Message ('Excluding member {0} for group {1} because recipient does not exist in Exchange Online as a valid recipient.' -f $Member.PrimarySmtpAddress, $GroupId) -WarningAction Continue
                        }
                        else {
                            Write-PSFMessage -Level Warning -Message ('Member {0} does not exist in Exchange Online as a valid recipient. Will not continue with current group {1}.' -f $ManagerId, $GroupId) -WarningAction Continue
                            continue Group
                        }
                    }
                    $DistributionGroupObject.Members = $ValidMembers

                    $Command = Get-Command -Name New-DistributionGroup -ErrorAction Stop
                    [hashtable]$InitializeNewGroup = @{ }

                    foreach ($Parameter in ($Command.Parameters.GetEnumerator() | Sort-Object)) {
                        if ($Parameter.Key -match $ExcludeNew) {
                            continue
                        }
                        elseif ($Parameter.Key -match $AddPrefix -and ($DistributionGroupObject.EXCH."$($Parameter.Key)")) {
                            Write-PSFMessage -Level Verbose -Message ('Adding prefix "{0}" for property {0}.' -f $Prefix, $Parameter.Key)
                            $InitializeNewGroup.Add("$($Parameter.Key)", ('{0}{1}' -f $Prefix, $DistributionGroupObject.EXCH."$($Parameter.Key)"))
                        }
                        elseif ($DistributionGroupObject.EXCH."$($Parameter.Key)") {
                            $InitializeNewGroup.Add("$($Parameter.Key)", $DistributionGroupObject.EXCH."$($Parameter.Key)")
                        }
                    }

                    $InitializedNewGroup = New-DistributionGroup @InitializeNewGroup -ErrorAction Stop
                    Write-PSFMessage -Level Host -Message ('Successfully created a cloud only distribution group with identity "{0}".' -f $InitializedNewGroup.PrimarySmtpAddress)
                    $DistributionGroupObject.InitializedGroup = $InitializedNewGroup

                    $Command = Get-Command -Name Set-DistributionGroup -ErrorAction Stop
                    [hashtable]$InitializeSetGroup = @{ }

                    foreach ($Parameter in ($Command.Parameters.GetEnumerator() | Sort-Object)) {
                        if ($Parameter.Key -match $ExcludeSet) {
                            continue
                        }
                        elseif ($Parameter.Key -match 'EmailAddresses' -and ($DistributionGroupObject.EXCH."$($Parameter.Key)")) {
                            Write-PSFMessage -Level Verbose -Message ('Adding prefix "{0}" for property {0}.' -f $Prefix, $Parameter.Key)
                            $EmailAddresses = foreach ($Address in $DistributionGroupObject.EXO.EmailAddresses) {
                                if ($Address -match '^smtp\:') {
                                    $Address -replace '^(smtp\:)(.+)', ('$1{0}$2' -f $Prefix)
                                }
                            }
                            $InitializeSetGroup.Add("$($Parameter.Key)", $EmailAddresses)
                        }
                        elseif ($DistributionGroupObject.EXCH."$($Parameter.Key)") {
                            $InitializeSetGroup.Add("$($Parameter.Key)", $DistributionGroupObject.EXCH."$($Parameter.Key)")
                        }
                    }

                    if ($DistributionGroupObject.Manager) {
                        $InitializeSetGroup.Add('ManagedBy', $DistributionGroupObject.Manager)
                    }

                    Write-PSFMessage -Level Verbose -Message ('Updating properties for distribution group {0}.' -f $InitializedNewGroup.PrimarySmtpAddress)
                    Set-DistributionGroup -Identity $InitializedNewGroup.PrimarySmtpAddress @InitializeSetGroup -HiddenFromAddressListsEnabled:$true -BypassSecurityGroupManagerCheck -ErrorAction Stop
                    Write-PSFMessage -Level Verbose -Message ('Updating membership for distribution group {0}.' -f $InitializedNewGroup.PrimarySmtpAddress)
                    Update-DistributionGroupMember -Identity $InitializedNewGroup.PrimarySmtpAddress -Members @($DistributionGroupObject.Members.PrimarySmtpAddress) -BypassSecurityGroupManagerCheck -Confirm:$false -ErrorAction Stop
                    $InitializedGroup = Get-DistributionGroup -Identity $InitializedNewGroup.PrimarySmtpAddress -ErrorAction Stop

                    $DistributionGroupObject.InitializedGroup = $InitializedGroup
                    $DistributionGroupObject | Export-PSFClixml -Path "$LogPath\$GroupId.byte" -Depth 5 -ErrorAction Stop
                    Write-PSFMessage -Level Host -Message ('Exported a PSFClixml object of the distribution group, before and after initialization, to "{0}".' -f "$LogPath\$GroupId.byte")
                    Write-PSFMessage -Level Verbose -Message ('Use Import-PFClixml -Path "{0}" to view the content.' -f "$LogPath\$GroupId.byte")
                }
                catch {
                    if ($DistributionGroupObject) {
                        $DistributionGroupObject | Export-PSFClixml -Path "$LogPath\$GroupId.byte" -Depth 5 -ErrorAction Stop
                        Write-PSFMessage -Level Critical -Message ('Error occurred, will export current configuration of all distribution groups collected and its properties to a PSFClixml object to {0}.' -f "$LogPath\$GroupId.byte")
                        Write-PSFMessage -Level Verbose -Message ('Use Import-PFClixml -Path "{0}" to view the content.' -f "$LogPath\$GroupId.byte")
                    }
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
    }
}
function Remove-OnPremDSTGroup {
    <#
    .SYNOPSIS
    Remove one or more distribution groups in Exchange On-premise generated from Initialize-OnPremDSTGroupToCloud.
 
    .DESCRIPTION
    Remove-OnPremDSTGroup will remove the original distribution group in Exchange On-premise (also Active Directory) with data from Initialize-OnPremDSTGroupToCloud.
    Remove-OnPremDSTGroup requires that PSFClixml objects exist in the target LogPath location which is generated from Initialize-OnPremDSTGroupToCloud.
    The function Remove-OnPremDSTGroup goes through the following steps:
 
    1. Validate that the distribution group is exist in both Exchange Online and Exchange On-premises and that the property, "IsDirSynced", exist for the Exchange Online distribution group.
    2. Validate that the initialized distribution group is exist in Exchange Online.
    3. Remove the source distribution group from Exchange On-premise, which will also remove the Active Directory object.
 
    .EXAMPLE
    Remove-OnPremDSTGroup -Group 'dstgroup001@contoso.com' -ExchangeServer exchprod01.contoso.com
 
    [11:12:06][Remove-OnPremDSTGroup] Successfully removed the source distribution group with identity "dstgroup001@contoso.com" from Exchange On-premise.
 
    This example removes the source distribution group from Exchange On-premise, exchprod01.contoso.com, with the identity 'dstgroup001@contoso.com'.
 
    .EXAMPLE
    Remove-OnPremDSTGroup -Group 'dstgroup002@contoso.com' -ExchangeServer exchprod01.contoso.com -NoMFA
 
    [11:12:06][Remove-OnPremDSTGroup] Successfully removed the source distribution group with identity "dstgroup002@contoso.com" from Exchange On-premise.
 
    This example removes the source distribution group from Exchange On-premise, exchprod01.contoso.com, with the identity 'dstgroup002@contoso.com'.
    When NoMFA switch is issued the connection to Exchange Online PowerShell will be using the native experience instead of modern authentication.
 
    .EXAMPLE
    Remove-OnPremDSTGroup -Group 'dstgroup003@contoso.com' -ExchangeServer exchprod01.contoso.com -LogPath "C:\Log"
 
    [11:12:06][Remove-OnPremDSTGroup] Successfully removed the source distribution group with identity "dstgroup003@contoso.com" from Exchange On-premise.
 
    This example removes the source distribution group from Exchange On-premise, exchprod01.contoso.com, with the identity 'dstgroup003@contoso.com'.
    The LogPath parameter specifies an alternate path for all logs and the distribution group XML-objects.
 
    .EXAMPLE
    Remove-OnPremDSTGroup -Group 'dstgroup004@contoso.com' -ExchangeServer exchprod01.contoso.com -Force
 
    WARNING: [11:12:03][Remove-OnPremDSTGroup] Excluding validation of existence for the initialized distribution group.
    [11:12:06][Remove-OnPremDSTGroup] Successfully removed the source distribution group with identity "dstgroup004@contoso.com" from Exchange On-premise.
 
    This example removes the source distribution group from Exchange On-premise, exchprod01.contoso.com, with the identity 'dstgroup004@contoso.com'.
    The Force parameter will not connect to Exchange Online and validate the existence for the initialized distribution group.
 
    .LINK
    https://github.com/PhilipHaglund/DistributionGroupMigration/blob/master/docs/en-US/Remove-OnPremDSTGroup.md
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies one or more distribution groups to be migrated. Recommended to use PrimarySmtpAddress as input to have a unique value.
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [Alias('PrimarySmtpAddress')]
        [string[]]$Group,

        # Specifies an Exchange On-premise server hosting the PowerShell endpoint.
        [Parameter(
            Mandatory
        )]
        [Alias('EXCH')]
        [string]$ExchangeServer,

        <#
        Specifies the path for all logs and the distribution group XML-objects.
        Default the LogPath uses the 'PSFramework.Logging.FileSystem.LogPath' which defaults to "$env:APPDATA\WindowsPowerShell\PSFramework\Logs" for Windows PowerShell and
        "$env:APPDATA\PowerShell\PSFramework\Logs" for PowerShell Core.
        #>

        [Parameter()]
        [string]$LogPath = (Get-PSFConfigValue -FullName 'PSFramework.Logging.FileSystem.LogPath'),

        # Specifies that no validation will take place for the existence of the initialized distribution group before the distribution group removal.
        [Parameter()]
        [switch]$Force,

        # Specifies that No MFA will be used when connecting to Exchange Online. If the Force parameter is specified the NoMFA parameter will have no effect.
        [Parameter()]
        [switch]$NoMFA
    )
    begin {
        try {
            Set-PSFConfig -FullName 'PSFramework.Logging.FileSystem.LogPath' -Value $LogPath -ErrorAction Stop
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
        if (-not $PSBoundParameters.ContainsKey('Force')) {
            if ($PSCmdlet.ShouldProcess('Office365', 'Connect-ExchangeOnline')) {
                try {
                    $PreviousErrorActionPreference = $ErrorActionPreference
                    $ErrorActionPreference = 'Continue'
                    if (Connect-ExchangeOnline -NoMFA:$NoMFA -ErrorAction Stop -WarningAction SilentlyContinue) {
                        Write-PSFMessage -Level Verbose -Message 'Connected to Exchange Online.'
                    }
                    else {
                        throw [System.AccessViolationException]::New('Unable to establish a session to Exchange Online')
                    }
                }
                catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
                finally {
                    $ErrorActionPreference = $PreviousErrorActionPreference
                }
            }
        }
        if ($PSCmdlet.ShouldProcess($ExchangeServer, 'Connect-ExchangeOnPremise')) {
            try {
                $PreviousErrorActionPreference = $ErrorActionPreference
                $ErrorActionPreference = 'Continue'
                if (Connect-ExchangeOnPremise -ExchangeServer $ExchangeServer -ErrorAction Stop -WarningAction SilentlyContinue) {
                    Write-PSFMessage -Level Verbose -Message 'Connected to Exchange On-premise.'
                }
                else {
                    throw [System.AccessViolationException]::New('Unable to establish a session to Exchange On-Premise server {0}. Error: {1}' -f $ExchangeServer, $_.Exception)
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $ErrorActionPreference = $PreviousErrorActionPreference
            }
        }
    }
    process {
        :Group foreach ($GroupId in $Group) {
            if ($PSCmdlet.ShouldProcess($GroupId)) {
                try {

                    $DistributionGroupObject = Import-PSFClixml -Path "$LogPath\$GroupId.byte" -ErrorAction Stop

                    if (-not $PSBoundParameters.ContainsKey('Force')) {
                        $InitializedGroup = Get-DistributionGroup $DistributionGroupObject.InitializedGroup.PrimarySmtpAddress -ErrorAction Stop
                        if ($null -eq $InitializedGroup) {
                            Write-PSFMessage -Level Warning -Message ('Unable to find synchronized initialized distribution group {0} in Exchange Online. Will not continue with current group.' -f $GroupId)
                            continue
                        }
                        elseif ($InitializedGroup.Count -gt 1) {
                            Write-PSFMessage -Level Warning -Message ('More than one initialized distribution group found in Exchange Online with identity {0}. Will not continue with current group.' -f $GroupId)
                            continue
                        }
                    }

                    $ExchGroup = Get-ExchDistributionGroup -Identity $DistributionGroupObject.EXCH.PrimarySmtpAddress -ErrorAction Stop
                    if ($null -eq $ExchGroup) {
                        Write-PSFMessage -Level Warning -Message ('Unable to find distribution group {0} in Exchange On-premises. Will not continue with current group.' -f $GroupId) -WarningAction Continue
                        continue
                    }
                    elseif ($ExchGroup.Count -gt 1) {
                        Write-PSFMessage -Level Warning -Message ('More than one distribution group found in Exchange On-premises with identity {0}. Will not continue with current group.' -f $GroupId) -WarningAction Continue
                        continue
                    }

                    if ($PSBoundParameters.ContainsKey('Force') -or $PSCmdlet.ShouldContinue($ExchGroup.PrimarySmtpAddress, $MyInvocation.MyCommand.Name)) {
                        Remove-ExchDistributionGroup -Identity $ExchGroup.PrimarySmtpAddress -ErrorAction Stop -Confirm:$false
                        Write-PSFMessage -Level Host -Message ('Successfully removed the source distribution group with identity "{0}" from Exchange On-premise.' -f $DistributionGroupObject.EXCH.PrimarySmtpAddress)
                    }
                }
                catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
    }
}
function Set-NoAADSyncOnPremDSTGroup {
    <#
    .SYNOPSIS
    Set the 'adminDescription' property to 'Group_%PARAM%' for one or more distribution groups generated from Initialize-OnPremDSTGroupToCloud.
 
    .DESCRIPTION
    Set-NoAADSyncOnPremDSTGroup will set the 'adminDescription' property to 'Group_%PARAM%' so the target distribution group is excluded from the Azure AD Connect synchronization.
    Set-NoAADSyncOnPremDSTGroup requires that PSFClixml objects exist in the target LogPath location which is generated from Initialize-OnPremDSTGroupToCloud.
    The function Set-NoAADSyncOnPremDSTGroup goes through the following steps:
 
    1. Validate that the distribution group is exist in both Exchange Online and Exchange On-premises and that the property, "IsDirSynced", exist for the Exchange Online distribution group.
    2. Validate that the initialized distribution group is exist in Exchange Online.
    3. Set the 'adminDescription' property to 'Group_%PARAM%' for the target distribution group in Active Directory, which will remove the Exchange Online distribution group after the next AAD Connect sync cycle.
 
    Notice: The ActiveDirectory module is required for this function to work.
 
    .EXAMPLE
    Set-NoAADSyncOnPremDSTGroup -Group 'dstgroup001@contoso.com' -ExchangeServer exchprod01.contoso.com
 
    [11:12:06][Set-NoAADSyncOnPremDSTGroup] Successfully set the adminDescription property to "Group_NoAADSync" for the source distribution group "dstgroup001@contoso.com".
 
    This example sets the adminDescription property to 'Group_NoAADSync' for the target source distribution group "dstgroup001@contoso.com" using Active Directory.
 
    .EXAMPLE
    Set-NoAADSyncOnPremDSTGroup -Group 'dstgroup002@contoso.com' -ExchangeServer exchprod01.contoso.com -NoMFA
 
    [11:12:06][Set-NoAADSyncOnPremDSTGroup] Successfully set the adminDescription property to "Group_NoAADSync" for the source distribution group "dstgroup002@contoso.com".
 
    This example sets the adminDescription property to 'Group_NoAADSync' for the target source distribution group "dstgroup001@contoso.com" using Active Directory.
    When NoMFA switch is issued the connection to Exchange Online PowerShell will be using the native experience instead of modern authentication.
 
    .EXAMPLE
    Set-NoAADSyncOnPremDSTGroup -Group 'dstgroup003@contoso.com' -ExchangeServer exchprod01.contoso.com -LogPath "C:\Log"
 
    [11:12:06][Set-NoAADSyncOnPremDSTGroup] Successfully set the adminDescription property to "Group_NoAADSync" for the source distribution group "dstgroup003@contoso.com".
 
    This example sets the adminDescription property to 'Group_NoAADSync' for the target source distribution group "dstgroup001@contoso.com" using Active Directory.
    The LogPath parameter specifies an alternate path for all logs and the distribution group XML-objects.
 
    .EXAMPLE
    Set-NoAADSyncOnPremDSTGroup -Group 'dstgroup004@contoso.com' -ExchangeServer exchprod01.contoso.com -Suffix 'NoO365Sync'
 
    [11:12:06][Set-NoAADSyncOnPremDSTGroup] Successfully set the adminDescription property to "Group_NoO365Sync" for the source distribution group "dstgroup004@contoso.com".
 
    This example sets the adminDescription property to 'Group_NoAADSync' for the target source distribution group "dstgroup001@contoso.com" using Active Directory.
    The Suffix parameter specifies an alternate suffix for to put in the adminDescription property.
 
    .EXAMPLE
    Set-NoAADSyncOnPremDSTGroup -Group 'dstgroup005@contoso.com' -ExchangeServer exchprod01.contoso.com -Force
 
    WARNING: [11:12:03][Set-NoAADSyncOnPremDSTGroup] Excluding validation of existence for the initialized distribution group.
    [11:12:06][Set-NoAADSyncOnPremDSTGroup] Successfully set the adminDescription property to "Group_NoAADSync" for the source distribution group "dstgroup005@contoso.com".
 
    This example removes the source distribution group from Exchange On-premise, exchprod01.contoso.com, with the identity 'dstgroup004@contoso.com'.
    The Force parameter will not connect to Exchange Online and validate the existence for the initialized distribution group.
 
    .LINK
    https://github.com/PhilipHaglund/DistributionGroupMigration/blob/master/docs/en-US/Set-NoAADSyncOnPremDSTGroup.md
    #>

    [CmdletBinding(
        SupportsShouldProcess
    )]
    param (
        # Specifies one or more distribution groups to be migrated. Recommended to use PrimarySmtpAddress as input to have a unique value.
        [Parameter(
            Mandatory,
            ValueFromPipeline
        )]
        [Alias('PrimarySmtpAddress')]
        [string[]]$Group,

        # Specifies an Exchange On-premise server hosting the PowerShell endpoint.
        [Parameter(
            Mandatory
        )]
        [Alias('EXCH')]
        [string]$ExchangeServer,

        <#
        Specifies the path for all logs and the distribution group XML-objects.
        Default the LogPath uses the 'PSFramework.Logging.FileSystem.LogPath' which defaults to "$env:APPDATA\WindowsPowerShell\PSFramework\Logs" for Windows PowerShell and
        "$env:APPDATA\PowerShell\PSFramework\Logs" for PowerShell Core.
        #>

        [Parameter()]
        [string]$LogPath = (Get-PSFConfigValue -FullName 'PSFramework.Logging.FileSystem.LogPath'),

        # Specifies that no validation will take place for the existence of the initialized distribution group before the distribution group removal.
        [Parameter()]
        [switch]$Force,

        # Specifies the suffix for the property adminDescription. Default the suffix is 'NoAADSync'. The string will concatenate the [string]'Group_' with $Suffix.
        [Parameter()]
        [string]$Suffix = 'NoAADSync',

        # Specifies that No MFA will be used when connecting to Exchange Online. If the Force parameter is specified the NoMFA parameter will have no effect.
        [Parameter()]
        [switch]$NoMFA
    )
    begin {
        try {
            Import-Module -Name ActiveDirectory -Force -ErrorAction Stop -WarningAction Stop
        }
        catch {
            Write-PSFMessage -Level Critical -Message ('The ActiveDirectory module is not installed. {1} will not continue.' -f $env:COMPUTERNAME, 'Set-NoAADSyncOnPremDSTGroup')
            $PSCmdlet.ThrowTerminatingError($_)
        }
        try {
            Set-PSFConfig -FullName 'PSFramework.Logging.FileSystem.LogPath' -Value $LogPath -ErrorAction Stop
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
        if (-not $PSBoundParameters.ContainsKey('Force')) {
            if ($PSCmdlet.ShouldProcess('Office365', 'Connect-ExchangeOnline')) {
                try {
                    $PreviousErrorActionPreference = $ErrorActionPreference
                    $ErrorActionPreference = 'Continue'
                    if (Connect-ExchangeOnline -NoMFA:$NoMFA -ErrorAction Stop -WarningAction SilentlyContinue) {
                        Write-PSFMessage -Level Verbose -Message 'Connected to Exchange Online.'
                    }
                    else {
                        throw [System.AccessViolationException]::New('Unable to establish a session to Exchange Online')
                    }
                }
                catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
                finally {
                    $ErrorActionPreference = $PreviousErrorActionPreference
                }
            }
        }
        if ($PSCmdlet.ShouldProcess($ExchangeServer, 'Connect-ExchangeOnPremise')) {
            try {
                $PreviousErrorActionPreference = $ErrorActionPreference
                $ErrorActionPreference = 'Continue'
                if (Connect-ExchangeOnPremise -ExchangeServer $ExchangeServer -ErrorAction Stop -WarningAction SilentlyContinue) {
                    Write-PSFMessage -Level Verbose -Message 'Connected to Exchange On-premise.'
                }
                else {
                    throw [System.AccessViolationException]::New('Unable to establish a session to Exchange On-Premise server {0}. Error: {1}' -f $ExchangeServer, $_.Exception)
                }
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
            finally {
                $ErrorActionPreference = $PreviousErrorActionPreference
            }
        }
        $AdminDescription = "Group_$Suffix"
    }
    process {
        :Group foreach ($GroupId in $Group) {
            if ($PSCmdlet.ShouldProcess($GroupId)) {
                try {

                    $DistributionGroupObject = Import-PSFClixml -Path "$LogPath\$GroupId.byte" -ErrorAction Stop

                    if (-not $PSBoundParameters.ContainsKey('Force')) {
                        $InitializedGroup = Get-DistributionGroup $DistributionGroupObject.InitializedGroup.PrimarySmtpAddress -ErrorAction Stop
                        if ($null -eq $InitializedGroup) {
                            Write-PSFMessage -Level Warning -Message ('Unable to find synchronized initialized distribution group {0} in Exchange Online. Will not continue with current group.' -f $GroupId)
                            continue
                        }
                        elseif ($InitializedGroup.Count -gt 1) {
                            Write-PSFMessage -Level Warning -Message ('More than one initialized distribution group found in Exchange Online with identity {0}. Will not continue with current group.' -f $GroupId)
                            continue
                        }
                    }

                    $ExchGroup = Get-ExchDistributionGroup -Identity $DistributionGroupObject.EXCH.PrimarySmtpAddress -ErrorAction Stop
                    if ($null -eq $ExchGroup) {
                        Write-PSFMessage -Level Warning -Message ('Unable to find distribution group {0} in Exchange On-premises. Will not continue with current group.' -f $GroupId) -WarningAction Continue
                        continue
                    }
                    elseif ($ExchGroup.Count -gt 1) {
                        Write-PSFMessage -Level Warning -Message ('More than one distribution group found in Exchange On-premises with identity {0}. Will not continue with current group.' -f $GroupId) -WarningAction Continue
                        continue
                    }

                    $ADGroup = Get-ADGroup -Identity $DistributionGroupObject.EXCH.DistinguishedName -ErrorAction Stop
                    Set-ADGroup -Identity $ADGroup.DistinguishedName -Replace @{'adminDescription' = $AdminDescription } -ErrorAction Stop
                    Write-PSFMessage -Level Host -Message ('Successfully set the adminDescription property to "{0}" for the source distribution group "dstgroup001@contoso.com". {0}.' -f $AdminDescription, $DistributionGroupObject.EXCH.PrimarySmtpAddress)
                }
                catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
    }
}