DSCResources/MSFT_xExchMaintenanceMode/MSFT_xExchMaintenanceMode.psm1

function Get-TargetResource
{
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSDSCUseVerboseMessageInDSCResource", "")]
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Boolean]
        $Enabled,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential,

        [Parameter()]
        [System.String[]]
        $AdditionalComponentsToActivate,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [ValidateSet('None','Lossless','GoodAvailability','BestAvailability','BestEffort')]
        [System.String]
        $MountDialOverride = 'None',

        [Parameter()]
        [System.Boolean]
        $MovePreferredDatabasesBack = $false,

        [Parameter()]
        [System.Boolean]
        $SetInactiveComponentsFromAnyRequesterToActive = $false,

        [Parameter()]
        [System.Boolean]
        $SkipActiveCopyChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipAllChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipClientExperienceChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipCpuChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipHealthChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipLagChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipMaximumActiveDatabasesChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipMoveSuppressionChecks = $false,

        [Parameter()]
        [System.String]
        $UpgradedServerVersion
    )

    LogFunctionEntry -Parameters @{"Enabled" = $Enabled} -Verbose:$VerbosePreference

    #Load TransportMaintenanceMode Helper
    Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)"))\TransportMaintenance.psm1" -Verbose:0

    #Establish remote Powershell session
    GetRemoteExchangeSession -Credential $Credential -CommandsToLoad "Get-*" -Verbose:$VerbosePreference

    $maintenanceModeStatus = GetMaintenanceModeStatus -EnteringMaintenanceMode $Enabled -DomainController $DomainController
    $atDesiredVersion = IsExchangeAtDesiredVersion -DomainController $DomainController -UpgradedServerVersion $UpgradedServerVersion

    if ($null -ne $maintenanceModeStatus)
    {
        #Determine which components are Active
        $activeComponents = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.State -eq "Active"}

        [System.String[]]$activeComponentsList = @()

        if ($null -ne $activeComponents)
        {
            foreach ($activeComponent in $activeComponents)
            {
                $activeComponentsList += $activeComponent.Component
            }
        }

        $activeComponentCount = $activeComponentsList.Count


        #Figure out what our Enabled state should really be in case UpgradedServerVersion was passed
        $isEnabled = $Enabled

        if ($Enabled -eq $true -and $atDesiredVersion -eq $true)
        {
            $isEnabled = $false
        }


        $returnValue = @{
            Enabled              = [System.Boolean] $isEnabled
            ActiveComponentCount = [System.Int32] $activeComponentCount
            ActiveComponentsList = [System.String[]] $activeComponentsList
            ActiveDBCount        = [System.Int32] (GetActiveDBCount -MaintenanceModeStatus $maintenanceModeStatus -DomainController $DomainController)
            ActiveUMCallCount    = [System.Int32] (GetUMCallCount -MaintenanceModeStatus $maintenanceModeStatus -DomainController $DomainController)
            ClusterState         = [System.String] $maintenanceModeStatus.ClusterNode.State
            QueuedMessageCount   = [System.Int32] (GetQueueMessageCount -MaintenanceModeStatus $maintenanceModeStatus)
        }
    }

    $returnValue
}

function Set-TargetResource
{
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Boolean]
        $Enabled,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential,

        [Parameter()]
        [System.String[]]
        $AdditionalComponentsToActivate,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [ValidateSet('None','Lossless','GoodAvailability','BestAvailability','BestEffort')]
        [System.String]
        $MountDialOverride = 'None',

        [Parameter()]
        [System.Boolean]
        $MovePreferredDatabasesBack = $false,

        [Parameter()]
        [System.Boolean]
        $SetInactiveComponentsFromAnyRequesterToActive = $false,

        [Parameter()]
        [System.Boolean]
        $SkipActiveCopyChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipAllChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipClientExperienceChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipCpuChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipHealthChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipLagChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipMaximumActiveDatabasesChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipMoveSuppressionChecks = $false,

        [Parameter()]
        [System.String]
        $UpgradedServerVersion
    )

    LogFunctionEntry -Parameters @{"Enabled" = $Enabled} -Verbose:$VerbosePreference

    #Load TransportMaintenanceMode Helper
    Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)"))\TransportMaintenance.psm1" -Verbose:0

    #Get ready for calling DAG maintenance scripts later
    $scriptsFolder = Join-Path -Path ((Get-ItemProperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v15\Setup).MsiInstallPath) -ChildPath "Scripts"
    $startDagServerMaintenanceScript = Join-Path -Path "$($scriptsFolder)" -ChildPath "StartDagServerMaintenance.ps1"
    $stopDagServerMaintenanceScript = Join-Path -Path "$($scriptsFolder)" -ChildPath "StopDagServerMaintenance.ps1"

    #Override Write-Host, as it is used by the target scripts, and causes a DSC error since the session is not interactive
    New-Alias Write-Host Write-Verbose

    #Check if setup is running.
    $setupRunning = Get-IsSetupRunning

    if ($setupRunning -eq $true)
    {
        Write-Verbose "Exchange Setup is currently running. Skipping maintenance mode checks."
        return
    }

    #Establish remote Powershell session
    GetRemoteExchangeSession -Credential $Credential -CommandsToLoad "*" -Verbose:$VerbosePreference

    #If the request is to put the server in maintenance mode, make sure we aren't already at the (optional) requested Exchange Server version
    $atDesiredVersion = IsExchangeAtDesiredVersion -DomainController $DomainController -UpgradedServerVersion $UpgradedServerVersion

    if ($Enabled -eq $true -and $atDesiredVersion -eq $true)
    {
        Write-Verbose "Server is already at or above the desired upgrade version of '$($UpgradedServerVersion)'. Skipping putting server into maintenance mode."
        return
    }

    #Continue on with setting the maintenance mode state
    $maintenanceModeStatus = GetMaintenanceModeStatus -EnteringMaintenanceMode $Enabled -DomainController $DomainController

    if ($null -ne $maintenanceModeStatus)
    {
        #Set vars relevant to both 'Enabled' code paths
        $htStatus = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.Component -eq "HubTransport"}
        $haStatus = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.Component -eq "HubTransport"}

        #Put the server into maintenance mode
        if ($Enabled -eq $true)
        {
            #Block DB activation on this server
            if ($maintenanceModeStatus.MailboxServer.DatabaseCopyAutoActivationPolicy -ne "Blocked")
            {
                Write-Verbose "Setting DatabaseCopyAutoActivationPolicy to Blocked"
                SetMailboxServer -Identity $env:COMPUTERNAME -DomainController $DomainController -AdditionalParams @{"DatabaseCopyAutoActivationPolicy" = "Blocked"}
            }

            #Set UM to draining before anything else
            $changedUM = ChangeComponentState -Component "UMCallRouter" -Requester "Maintenance" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Draining" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController

            #Start HT maintenance if required
            if ($htStatus.State -ne "Inactive")
            {
                Write-Verbose "Entering Transport Maintenance"
                [System.String[]]$transportExclusions = GetMessageRedirectionExclusions -DomainController $DomainController
                Start-TransportMaintenance -LoadLocalShell $false -MessageRedirectExclusions $transportExclusions -Verbose
            }

            #Wait for remaining UM calls to drain
            if ($changedUM)
            {
                WaitForUMToDrain -DomainController $DomainController
            }

            #Run StartDagServerMaintenance script to put cluster offline and failover DB's
            if ($maintenanceModeStatus.ClusterNode.State -eq "Up" -or
                $maintenanceModeStatus.MailboxServer.DatabaseCopyAutoActivationPolicy -ne "Blocked" -or
                (GetActiveDBCount -MaintenanceModeStatus $maintenanceModeStatus -DomainController $DomainController) -ne 0)
            {
                Write-Verbose "Running StartDagServerMaintenance.ps1"

                $dagMemberCount = GetDAGMemberCount

                #Setup parameters for StartDagServerMaintenance.ps1
                $startDagScriptParams = @{
                    serverName = $env:COMPUTERNAME
                    Verbose = $true
                }

                if ((Get-ExchangeVersion) -in '2016','2019')
                {
                    $startDagScriptParams.Add('pauseClusterNode', $true)
                }

                if ($dagMemberCount -ne 0 -and $dagMemberCount -le 2)
                {
                    $startDagScriptParams.Add('overrideMinimumTwoCopies', $true)
                }

                if ($SkipAllChecks -or $SkipMoveSuppressionChecks)
                {
                    $startDagScriptParams.Add("Force", 'true')
                }

                #Execute StartDagServerMaintenance.ps1
                . $startDagServerMaintenanceScript @startDagScriptParams
            }

            #Set remaining components to offline
            ChangeComponentState -Component "ServerWideOffline" -Requester "Maintenance" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Inactive" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController | Out-Null

            #Check whether we are actually in maintenance mode
            $testResults = Test-TargetResource @PSBoundParameters

            if ($testResults -eq $false)
            {
                throw "Server is not fully in maintenance mode after running through steps to enable maintenance mode."
            }
        }
        #Take the server out of maintenance mode
        else
        {
            #Bring ServerWideOffline and UMCallRouter back online
            ChangeComponentState -Component "ServerWideOffline" -Requester "Maintenance" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Active" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController | Out-Null
            ChangeComponentState -Component "UMCallRouter" -Requester "Maintenance" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Active" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController | Out-Null

            #Run StopDagServerMaintenance.ps1 if required
            if ($maintenanceModeStatus.ClusterNode.State -ne "Up" -or `
                $maintenanceModeStatus.MailboxServer.DatabaseCopyAutoActivationPolicy -ne "Unrestricted" -or`
                $haStatus.State -ne "Active")
            {
                Write-Verbose "Running StopDagServerMaintenance.ps1"

                #Run StopDagServerMaintenance.ps1 in try/catch, so if an exception occurs, we can at least finish
                #doing the rest of the steps to take the server out of maintenance mode
                try
                {
                    . $stopDagServerMaintenanceScript -serverName $env:COMPUTERNAME -Verbose
                }
                catch
                {
                    Write-Error "Caught exception running StopDagServerMaintenance.ps1: $($_.Exception.Message)"
                }
            }

            #End Transport Maintenance
            if ($htStatus.State -ne "Active")
            {
                Write-Verbose "Ending Transport Maintenance"
                Stop-TransportMaintenance -LoadLocalShell $false -Verbose
            }

            #Bring components online that may have been taken offline by a failed setup run
            ChangeComponentState -Component "Monitoring" -Requester "Functional" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Active" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController | Out-Null
            ChangeComponentState -Component "RecoveryActionsEnabled" -Requester "Functional" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Active" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController | Out-Null

            #Bring online any specifically requested components
            if ($null -ne $AdditionalComponentsToActivate)
            {
                foreach ($component in $AdditionalComponentsToActivate)
                {
                    if ((IsComponentCheckedByDefault -ComponentName $component) -eq $false)
                    {
                        $status = $null
                        $status = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.Component -like "$($component)"}

                        if ($null -ne $status -and $status.State -ne 'Active')
                        {
                            ChangeComponentState -Component $component -Requester "Functional" -ServerComponentState $maintenanceModeStatus.ServerComponentState -State "Active" -SetInactiveComponentsFromAnyRequesterToActive $SetInactiveComponentsFromAnyRequesterToActive -DomainController $DomainController | Out-Null
                        }
                    }
                }
            }

            if ($MovePreferredDatabasesBack -eq $true)
            {
                MovePrimaryDatabasesBack -DomainController $DomainController -MountDialOverride $MountDialOverride -SkipActiveCopyChecks $SkipActiveCopyChecks -SkipAllChecks $SkipAllChecks -SkipClientExperienceChecks $SkipClientExperienceChecks -SkipCpuChecks $SkipCpuChecks -SkipHealthChecks $SkipHealthChecks -SkipLagChecks $SkipLagChecks -SkipMaximumActiveDatabasesChecks $SkipMaximumActiveDatabasesChecks -SkipMoveSuppressionChecks $SkipMoveSuppressionChecks
            }
        }
    }
    else
    {
        throw "Failed to retrieve maintenance mode status of server."
    }

    Remove-HelperSnapin

    Remove-Item Alias:Write-Host -ErrorAction SilentlyContinue
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Boolean]
        $Enabled,

        [Parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential,

        [Parameter()]
        [System.String[]]
        $AdditionalComponentsToActivate,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [ValidateSet('None','Lossless','GoodAvailability','BestAvailability','BestEffort')]
        [System.String]
        $MountDialOverride = 'None',

        [Parameter()]
        [System.Boolean]
        $MovePreferredDatabasesBack = $false,

        [Parameter()]
        [System.Boolean]
        $SetInactiveComponentsFromAnyRequesterToActive = $false,

        [Parameter()]
        [System.Boolean]
        $SkipActiveCopyChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipAllChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipClientExperienceChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipCpuChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipHealthChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipLagChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipMaximumActiveDatabasesChecks = $false,

        [Parameter()]
        [System.Boolean]
        $SkipMoveSuppressionChecks = $false,

        [Parameter()]
        [System.String]
        $UpgradedServerVersion
    )

    LogFunctionEntry -Parameters @{"Enabled" = $Enabled} -Verbose:$VerbosePreference

    #Load TransportMaintenanceMode Helper
    Import-Module "$((Get-Item -LiteralPath "$($PSScriptRoot)"))\TransportMaintenance.psm1" -Verbose:0

    $setupRunning = Get-IsSetupRunning

    if ($setupRunning -eq $true)
    {
        Write-Verbose "Exchange Setup is currently running. Skipping maintenance mode checks."
        return $true
    }

    #Establish remote Powershell session
    GetRemoteExchangeSession -Credential $Credential -CommandsToLoad "Get-*" -Verbose:$VerbosePreference

    $serverVersion = Get-ExchangeVersion

    $maintenanceModeStatus = GetMaintenanceModeStatus -EnteringMaintenanceMode $Enabled -DomainController $DomainController

    $testResults = $true

    if ($null -eq $maintenanceModeStatus)
    {
        Write-Error -Message "Failed to retrieve maintenance mode status for server."

        $testResults = $false
    }
    else
    {
        #Make sure server is fully in maintenance mode
        if ($Enabled -eq $true)
        {
            $atDesiredVersion = IsExchangeAtDesiredVersion -DomainController $DomainController -UpgradedServerVersion $UpgradedServerVersion

            if ($atDesiredVersion -eq $true)
            {
                Write-Verbose "Server is already at or above the desired upgrade version of '$($UpgradedServerVersion)'. Skipping putting server into maintenance mode."
                return $true
            }
            else
            {
                if ($maintenanceModeStatus.MailboxServer.DatabaseCopyAutoActivationPolicy -ne "Blocked")
                {
                    Write-Verbose "DatabaseCopyAutoActivationPolicy is not set to Blocked"
                    $testResults = $false
                }

                if ($null -ne ($MaintenanceModeStatus.ServerComponentState | Where-Object {$_.State -ne "Inactive" -and $_.Component -ne "Monitoring" -and $_.Component -ne "RecoveryActionsEnabled"}))
                {
                    Write-Verbose "One or more components have a status other than Inactive"
                    $testResults = $false
                }

                if ($maintenanceModeStatus.ClusterNode.State -eq "Up")
                {
                    Write-Verbose "Cluster node has a status of Up"
                    $testResults = $false
                }

                if ((IsServerPAM -DomainController $DomainController) -eq $true)
                {
                    Write-Verbose "Server still has the Primary Active Manager role"
                    $testResults = $false
                }


                [int]$messagesQueued = GetQueueMessageCount -MaintenanceModeStatus $maintenanceModeStatus

                if ($messagesQueued -gt 0)
                {
                    Write-Verbose "Found $($messagesQueued) messages still in queue"
                    $testResults = $false
                }


                [int]$activeDBCount = GetActiveDBCount -MaintenanceModeStatus $maintenanceModeStatus -DomainController $DomainController

                if ($activeDBCount -gt 0)
                {
                    Write-Verbose "Found $($activeDBCount) replicated databases still activated on this server"
                    $testResults = $false
                }


                [int]$umCallCount = GetUMCallCount -MaintenanceModeStatus $maintenanceModeStatus -DomainController $DomainController

                if ($umCallCount -gt 0)
                {
                    Write-Verbose "Found $($umCallCount) active UM calls on this server"
                    $testResults = $false
                }
            }
        }
        #Make sure the server is fully out of maintenance mode
        else
        {
            $activeComponents = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.State -eq "Active"}

            if ($null -eq $activeComponents)
            {
                Write-Verbose "No Components found with a status of Active"
                $testResults = $false
            }

            if ($null -eq ($activeComponents | Where-Object {$_.Component -eq "ServerWideOffline"}))
            {
                Write-Verbose "Component ServerWideOffline is not Active"
                $testResults = $false
            }

            if ($serverVersion -in '2013','2016')
            {
                if ($null -eq ($activeComponents | Where-Object {$_.Component -eq "UMCallRouter"}))
                {
                    Write-Verbose "Component UMCallRouter is not Active"
                    $testResults = $false
                }
            }

            if ($null -eq ($activeComponents | Where-Object {$_.Component -eq "HubTransport"}))
            {
                Write-Verbose "Component HubTransport is not Active"
                $testResults = $false
            }

            if ($maintenanceModeStatus.ClusterNode.State -ne "Up")
            {
                Write-Verbose "Cluster node has a status of $($maintenanceModeStatus.ClusterNode.State)"
                $testResults = $false
            }

            if ($maintenanceModeStatus.MailboxServer.DatabaseCopyAutoActivationPolicy -ne "Unrestricted")
            {
                Write-Verbose "DatabaseCopyAutoActivationPolicy is set to $($maintenanceModeStatus.MailboxServer.DatabaseCopyAutoActivationPolicy)"
                $testResults = $false
            }

            if ($null -eq ($activeComponents | Where-Object {$_.Component -eq "Monitoring"}))
            {
                Write-Verbose "Component Monitoring is not Active"
                $testResults = $false
            }

            if ($null -eq ($activeComponents | Where-Object {$_.Component -eq "RecoveryActionsEnabled"}))
            {
                Write-Verbose "Component RecoveryActionsEnabled is not Active"
                $testResults = $false
            }

            if ($null -ne $AdditionalComponentsToActivate)
            {
                foreach ($component in $AdditionalComponentsToActivate)
                {
                    if ((IsComponentCheckedByDefault -ComponentName $component) -eq $false)
                    {
                        $status = $null
                        $status = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.Component -like "$($component)"}

                        if ($null -ne $status -and $Status.State -ne "Active")
                        {
                            Write-Verbose "Component $($component) is not set to Active"
                            $testResults = $false
                        }
                    }
                }
            }
        }
    }

    Remove-HelperSnapin

    return $testResults
}

#Gets a Hashtable containing various objects from Exchange that will be used to determine maintenance mode status
function GetMaintenanceModeStatus
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.Boolean]
        $EnteringMaintenanceMode = $true
    )

    RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToKeep 'DomainController'

    $serverComponentState = GetServerComponentState -Identity $env:COMPUTERNAME -DomainController $DomainController
    $clusterNode = Get-ClusterNode -Name $env:COMPUTERNAME
    $dbCopyStatus = GetMailboxDatabaseCopyStatus -Server $env:COMPUTERNAME -DomainController $DomainController
    $umCalls = GetUMActiveCalls -Server $env:COMPUTERNAME -DomainController $DomainController
    $mailboxServer = GetMailboxServer -Identity $env:COMPUTERNAME -DomainController $DomainController
    $queues = Get-Queue -Server $env:COMPUTERNAME -ErrorAction SilentlyContinue

    #If we're checking queues too soon after restarting Transport, Get-Queue may fail. Wait for bootloader to be active and try again.
    if ($null -eq $queues -and $EnteringMaintenanceMode -eq $true)
    {
        $endTime = [DateTime]::Now.AddMinutes(5)

        Write-Verbose "Waiting up to 5 minutes for the Transport Bootloader to be ready before running Get-Queue. Wait started at $([DateTime]::Now)."

        while ($null -eq $queues -and [DateTime]::Now -lt $endTime)
        {
            Wait-BootLoaderReady -Server $env:COMPUTERNAME -TimeOut (New-TimeSpan -Seconds 15) -PollingFrequency (New-TimeSpan -Seconds 1) | Out-Null
            $queues = Get-Queue -Server $env:COMPUTERNAME -ErrorAction SilentlyContinue
        }
    }

    [System.Collections.Hashtable]$returnValue = @{
        ServerComponentState = $serverComponentState
        ClusterNode = $clusterNode
        Queues = $queues
        DBCopyStatus = $dbCopyStatus
        UMActiveCalls = $umCalls
        MailboxServer = $mailboxServer
    }

    return $returnValue
}

#Gets a count of messages in queues on the local server
function GetQueueMessageCount
{
    [CmdletBinding()]
    [OutputType([System.UInt32])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Collections.Hashtable]
        $MaintenanceModeStatus
    )

    [UInt32]$messageCount = 0

    if ($null -ne $MaintenanceModeStatus.Queues)
    {
        foreach ($queue in $MaintenanceModeStatus.Queues | Where-Object {$_.Identity -notlike "*\Shadow\*"})
        {
            Write-Verbose "Found queue '$($queue.Identity)' with a message count of '$($queue.MessageCount)'."
            $messageCount += $queue.MessageCount
        }
    }
    else
    {
        Write-Warning "No Transport Queues were detected on this server. This can occur if the MSExchangeTransport service is not started, or if Get-Queue was run too quickly after restarting the service."
    }

    return [System.UInt32] $messageCount
}

#Gets a count of database that are replication enabled, and are still activated on the local server (even if they are dismounted)
function GetActiveDBCount
{
    [CmdletBinding()]
    [OutputType([System.UInt32])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Collections.Hashtable]
        $MaintenanceModeStatus,

        [Parameter()]
        [System.String]
        $DomainController
    )

    [UInt32]$activeDBCount = 0

    #Get DB's with a status of Mounted, Mounting, Dismounted, or Dismounting
    $localDBs = $MaintenanceModeStatus.DBCopyStatus | Where-Object {$_.Status -like "Mount*" -or $_.Status -like "Dismount*"}

    #Ensure that any DB's we found actually have copies
    foreach ($db in $localDBs)
    {
        $dbProps = GetMailboxDatabase -Identity "$($db.DatabaseName)" -DomainController $DomainController

        if ($dbProps.ReplicationType -ne "None")
        {
            Write-Verbose "Found database '$($db.DatabaseName)' with a replication type of '$($dbProps.ReplicationType)' and a status of '$($db.Status)'."
            $activeDBCount++
        }
    }

    return [System.UInt32] $activeDBCount
}

#Gets a count of active UM calls on the local server
function GetUMCallCount
{
    [CmdletBinding()]
    [OutputType([System.UInt32])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Collections.Hashtable]
        $MaintenanceModeStatus,

        [Parameter()]
        [System.String]
        $DomainController
    )

    [Uint32]$umCallCount = 0

    $umCalls = GetUMActiveCalls -Server $env:COMPUTERNAME -DomainController $DomainController

    if ($null -ne $umCalls)
    {
        if ($null -eq $umCalls.Count)
        {
            $umCallCount = 1
        }
        else
        {
            $umCallCount = $umCalls.Count
        }
    }

    return [System.UInt32] $umCallCount
}

#Gets a list of servers in the DAG with HubTransport not set to Active, or DatabaseCopyAutoActivationPolicy set to Blocked
function GetMessageRedirectionExclusions
{
    [CmdletBinding()]
    [OutputType([System.String[]])]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController
    )

    [System.String[]]$exclusions = @()

    $mbxServer = GetMailboxServer -Identity $env:COMPUTERNAME -DomainController $DomainController

    if ($null -ne $mbxServer)
    {
        $dag = GetDatabaseAvailabilityGroup -Identity $($mbxServer.DatabaseAvailabilityGroup) -DomainController $DomainController

        if ($null -ne $dag)
        {
            foreach ($server in $dag.Servers)
            {
                if ($server.Name -notlike $env:COMPUTERNAME)
                {
                    $serverName = $server.Name.ToLower()

                    #Check whether HubTransport is active on the specified server
                    $htState = $null
                    $htState = GetServerComponentState -Identity $server.Name -Component "HubTransport" -DomainController $DomainController

                    if ($null -ne $htState -and $htState.State -notlike "Active")
                    {
                        if (($exclusions.Contains($serverName) -eq $false))
                        {
                            $exclusions += $serverName
                            continue
                        }
                    }

                    #Check whether the server is already blocked from database activation
                    $currentMbxServer = $null
                    $currentMbxServer = GetMailboxServer -Identity $server.Name -DomainController $DomainController

                    if ($null -ne $currentMbxServer -and $currentMbxServer.DatabaseCopyAutoActivationPolicy -like "Blocked")
                    {
                        if (($exclusions.Contains($serverName) -eq $false))
                        {
                            $exclusions += $serverName
                            continue
                        }
                    }
                }
            }
        }
    }

    return [System.String[]] $exclusions
}

#If UpgradedServerVersion was specified, checks to see whether the server is already at the desired version
function IsExchangeAtDesiredVersion
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.String]
        $UpgradedServerVersion
    )

    $atDesiredVersion = $false

    if (!([System.String]::IsNullOrEmpty($UpgradedServerVersion)))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToKeep 'DomainController'

        $server = GetExchangeServer -Identity $env:COMPUTERNAME -DomainController $DomainController

        if ($null -ne $server)
        {
            [System.String[]]$versionParts = $UpgradedServerVersion.Split('.')

            if ($null -ne $versionParts -and $versionParts.Length -eq 4)
            {
                if (([int]::Parse($server.AdminDisplayVersion.Major) -ge [int]::Parse($versionParts[0])) -and
                    ([int]::Parse($server.AdminDisplayVersion.Minor) -ge [int]::Parse($versionParts[1])) -and
                    ([int]::Parse($server.AdminDisplayVersion.Build) -ge [int]::Parse($versionParts[2])) -and
                    ([int]::Parse($server.AdminDisplayVersion.Revision) -ge [int]::Parse($versionParts[3])))
                {
                    $atDesiredVersion = $true
                }
                else
                {
                    Write-Verbose "Desired server version '$($UpgradedServerVersion)' is greater than the actual server version '$($server.AdminDisplayVersion)'"
                }
            }
            else
            {
                throw "Invalid version format for `$UpgradedServerVersion. Should be in the format ##.#.####.#"
            }
        }
    }

    return $atDesiredVersion
}

#Checks to see whether the specified component is one that is already checked by default
function IsComponentCheckedByDefault
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ComponentName
    )

    [System.Boolean]$checkedByDefault = $false

    if ($ComponentName -like "ServerWideOffline" -or $ComponentName -like "UMCallRouter" -or $ComponentName -like "HubTransport" -or $ComponentName -like "Monitoring" -or $ComponentName -like "RecoveryActionsEnabled")
    {
        $checkedByDefault = $true
    }

    return $checkedByDefault
}

#Gets a count of members in this servers DAG
function GetDAGMemberCount
{
    [CmdletBinding()]
    [OutputType([System.Int32])]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController
    )

    [System.Int32]$count = 0

    $server = GetMailboxServer -Identity $env:COMPUTERNAME -DomainController $DomainController

    if ($null -ne $server -and ![System.String]::IsNullOrEmpty($server.DatabaseAvailabilityGroup))
    {
        $dag = GetDatabaseAvailabilityGroup -Identity "$($server.DatabaseAvailabilityGroup)" -DomainController $DomainController

        if ($null -ne $dag)
        {
            $count = $dag.Servers.Count
        }
    }

    return $count
}

function IsServerPAM
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController
    )

    $isPAM = $false

    $server = GetMailboxServer -Identity $env:COMPUTERNAME -DomainController $DomainController

    if ($null -ne $server -and ![System.String]::IsNullOrEmpty($server.DatabaseAvailabilityGroup))
    {
        $dag = GetDatabaseAvailabilityGroup -Identity "$($server.DatabaseAvailabilityGroup)" -DomainController $DomainController

        if ($null -ne $dag -and $dag.PrimaryActiveManager -like $env:COMPUTERNAME)
        {
            $isPAM = $true
        }
    }

    return $isPAM
}

#Waits up the the specified WaitMinutes for existing UM calls to finish. Returns True if no more UM calls are active.
function WaitForUMToDrain
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.UInt32]
        $SleepSeconds = 15,

        [Parameter()]
        [System.UInt32]
        $WaitMinutes = 5
    )

    [System.Boolean]$umDrained = $false

    $endTime = [DateTime]::Now.AddMinutes($WaitMinutes)

    Write-Verbose "Waiting up to $($WaitMinutes) minutes for active UM calls to finish"

    while ($fullyInMaintenanceMode -eq $false -and [DateTime]::Now -lt $endTime)
    {
        Write-Verbose "Checking whether all UM calls are finished at $([DateTime]::Now)."

        $umCalls = $null

        GetUMActiveCalls -Server $env:COMPUTERNAME -DomainController $DomainController

        if ($null -eq $umCalls -or $umCalls.Count -eq 0)
        {
            $umDrained = $true
        }
        else
        {
            Write-Verbose "There are still active UM calls as of $([DateTime]::Now). Sleeping for $($SleepSeconds) seconds. Will continue checking until $($endTime)."
            Start-Sleep -Seconds $SleepSeconds
        }
    }

    return $umDrained
}

#Checks whether a Component is at the specified State, if not, changes the component to the state.
#Returns whether a change was made to the component state
function ChangeComponentState
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Component,

        [Parameter(Mandatory = $true)]
        [System.String]
        $Requester,

        [Parameter(Mandatory = $true)]
        $ServerComponentState,

        [Parameter(Mandatory = $true)]
        [System.String]
        $State,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.Boolean]
        $SetInactiveComponentsFromAnyRequesterToActive = $false
    )

    [System.Boolean]$madeChange = $false

    $componentState = $MaintenanceModeStatus.ServerComponentState | Where-Object {$_.Component -like "$($Component)"}

    if ($null -ne $componentState)
    {
        #If we're already Inactive don't bother setting to Draining.
        if ($State -like "Draining" -and $componentState.State -like "Inactive")
        {
            return $false
        }
        elseif ($componentState.State -notlike "$($State)")
        {
            Write-Verbose "Setting $($componentState.Component) component to $($State) for requester $($Requester)"

            SetServerComponentState -Component $componentState.Component -State $State -Requester $Requester -DomainController $DomainController

            $madeChange = $true

            if ($State -eq "Active" -and $SetInactiveComponentsFromAnyRequesterToActive -eq $true)
            {
                $additionalRequesters = $null
                $additionalRequesters = $componentState.LocalStates | Where-Object {$_.Requester -notlike "$($Requester)" -and $_.State -notlike "Active"}

                if ($null -ne $additionalRequesters)
                {
                    foreach ($additionalRequester in $additionalRequesters)
                    {
                        Write-Verbose "Setting $($componentState.Component) component to Active for requester $($additionalRequester.Requester)"

                        SetServerComponentState -Component $componentState.Component -State Active -Requester $additionalRequester.Requester -DomainController $DomainController
                    }
                }
            }
        }
    }

    return $madeChange
}

#Finds any databases which have an Activation Preference of 1 for this server, which are not currently hosted on this server, and moves them back
function MovePrimaryDatabasesBack
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [ValidateSet('None','Lossless','GoodAvailability','BestAvailability','BestEffort')]
        [System.String]
        $MountDialOverride,

        [Parameter()]
        [System.Boolean]
        $SkipActiveCopyChecks,

        [Parameter()]
        [System.Boolean]
        $SkipAllChecks,

        [Parameter()]
        [System.Boolean]
        $SkipClientExperienceChecks,

        [Parameter()]
        [System.Boolean]
        $SkipCpuChecks,

        [Parameter()]
        [System.Boolean]
        $SkipHealthChecks,

        [Parameter()]
        [System.Boolean]
        $SkipLagChecks,

        [Parameter()]
        [System.Boolean]
        $SkipMaximumActiveDatabasesChecks,

        [Parameter()]
        [System.Boolean]
        $SkipMoveSuppressionChecks
    )

    $databases = GetMailboxDatabase -Server $env:COMPUTERNAME -Status -DomainController $DomainController

    [System.String[]]$databasesWithActivationPrefOneNotOnThisServer = @()

    if ($null -ne $databases)
    {
        foreach ($database in $databases)
        {
            if ($null -ne $database.ActivationPreference)
            {
                foreach ($ap in $database.ActivationPreference)
                {
                    if ($ap.Key.Name -like $env:COMPUTERNAME -and $ap.Value -eq 1)
                    {
                        $copyStatus = $null
                        $copyStatus = GetMailboxDatabaseCopyStatus -Identity "$($database.Name)\$($env:COMPUTERNAME)" -DomainController $DomainController

                        if ($null -ne $copyStatus -and $copyStatus.Status -eq "Healthy")
                        {
                            $databasesWithActivationPrefOneNotOnThisServer += $database.Name
                        }
                    }
                }
            }
        }
    }

    if ($databasesWithActivationPrefOneNotOnThisServer.Count -gt 0)
    {
        Write-Verbose "Found $($databasesWithActivationPrefOneNotOnThisServer.Count) Healthy databases with Activation Preference 1 that should be moved to this server."

        foreach ($database in $databasesWithActivationPrefOneNotOnThisServer)
        {
            Write-Verbose "Attempting to move database '$($database)' back to this server."

            #Do the move in a try/catch block so we can log the error, but not have it prevent other databases from attempting to move
            try
            {
                MoveActiveMailboxDatabase -Identity $database -ActivateOnServer $env:COMPUTERNAME -DomainController $DomainController -MountDialOverride $MountDialOverride -SkipActiveCopyChecks $SkipActiveCopyChecks -SkipAllChecks $SkipAllChecks -SkipClientExperienceChecks $SkipClientExperienceChecks -SkipCpuChecks $SkipCpuChecks -SkipHealthChecks $SkipHealthChecks -SkipLagChecks $SkipLagChecks -SkipMaximumActiveDatabasesChecks $SkipMaximumActiveDatabasesChecks -SkipMoveSuppressionChecks $SkipMoveSuppressionChecks
            }
            catch
            {
                Write-Error "$($_.Exception.Message)"
            }
        }
    }
    else
    {
        Write-Verbose "Found 0 Healthy databases with Activation Preference 1 for this server that are currently not hosted on this server"
    }
}

#region Exchange Cmdlet Wrappers
function GetExchangeServer
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController
    )

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    return (Get-ExchangeServer @PSBoundParameters)
}

function GetDatabaseAvailabilityGroup
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController
    )

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    return (Get-DatabaseAvailabilityGroup @PSBoundParameters -Status)
}

function GetServerComponentState
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $Component,

        [Parameter()]
        [System.String]
        $DomainController
    )

    if ([System.String]::IsNullOrEmpty($Component))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Component'
    }

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    return (Get-ServerComponentState @PSBoundParameters)
}

function SetServerComponentState
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Component,

        [Parameter(Mandatory = $true)]
        [System.String]
        $Requester,

        [Parameter(Mandatory = $true)]
        [System.String]
        $State,

        [Parameter()]
        [System.String]
        $DomainController
    )

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    Set-ServerComponentState -Identity $env:COMPUTERNAME @PSBoundParameters
}

function GetMailboxDatabase
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.String]
        $Server,

        [Parameter()]
        [switch]
        $Status
    )

    if ([System.String]::IsNullOrEmpty($Identity))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Identity'
    }

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    if ([System.String]::IsNullOrEmpty($Server))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Server'
    }

    return (Get-MailboxDatabase @PSBoundParameters)
}

function GetMailboxDatabaseCopyStatus
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.String]
        $Server
    )

    if ([System.String]::IsNullOrEmpty($Identity))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Identity'
    }

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    if ([System.String]::IsNullOrEmpty($Server))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Server'
    }

    return (Get-MailboxDatabaseCopyStatus @PSBoundParameters)
}

function GetMailboxServer
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController
    )

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    return (Get-MailboxServer @PSBoundParameters)
}

function SetMailboxServer
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [System.Collections.Hashtable]
        $AdditionalParams
    )

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    AddParameters -PSBoundParametersIn $PSBoundParameters -ParamsToAdd $AdditionalParams
    RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'AdditionalParams'

    Set-MailboxServer @PSBoundParameters
}

function GetUMActiveCalls
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Server,

        [Parameter()]
        [System.String]
        $DomainController
    )

    $umActiveCalls = $null

    $serverVersion = Get-ExchangeVersion

    if ($serverVersion -in '2013','2016')
    {
        if ([System.String]::IsNullOrEmpty($DomainController))
        {
            RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
        }

        $umActiveCalls = Get-UMActiveCalls @PSBoundParameters
    }

    return $umActiveCalls
}

function MoveActiveMailboxDatabase
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [System.String]
        $ActivateOnServer,

        [Parameter()]
        [System.String]
        $Identity,

        [Parameter()]
        [System.String]
        $DomainController,

        [Parameter()]
        [ValidateSet('None','Lossless','GoodAvailability','BestAvailability','BestEffort')]
        [System.String]
        $MountDialOverride,

        [Parameter()]
        [System.String]
        $MoveComment,

        [Parameter()]
        [System.String]
        $Server = $env:COMPUTERNAME,

        [Parameter()]
        [System.Boolean]
        $SkipActiveCopyChecks,

        [Parameter()]
        [System.Boolean]
        $SkipAllChecks,

        [Parameter()]
        [System.Boolean]
        $SkipClientExperienceChecks,

        [Parameter()]
        [System.Boolean]
        $SkipCpuChecks,

        [Parameter()]
        [System.Boolean]
        $SkipHealthChecks,

        [Parameter()]
        [System.Boolean]
        $SkipLagChecks,

        [Parameter()]
        [System.Boolean]
        $SkipMaximumActiveDatabasesChecks,

        [Parameter()]
        [System.Boolean]
        $SkipMoveSuppressionChecks
    )

    if ([System.String]::IsNullOrEmpty($ActivateOnServer))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'ActivateOnServer'
    }

    if ([System.String]::IsNullOrEmpty($DomainController))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'DomainController'
    }

    if ([System.String]::IsNullOrEmpty($Identity))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Identity'
    }

    if ([System.String]::IsNullOrEmpty($MoveComment))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'MoveComment'
    }

    if ([System.String]::IsNullOrEmpty($Server))
    {
        RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'Server'
    }

    #Setup parameters in a format Move-ActiveMailboxDatabase expects
    $moveDBParams = @{
        Confirm     = $false
        Erroraction = 'Stop'
    }

    if ($SkipActiveCopyChecks)
    {
        $moveDBParams.Add("SkipActiveCopyChecks", $true)
    }

    if ($SkipClientExperienceChecks)
    {
        $moveDBParams.Add("SkipClientExperienceChecks", $true)
    }

    if ($SkipHealthChecks)
    {
        $moveDBParams.Add("SkipHealthChecks", $true)
    }

    if ($SkipLagChecks)
    {
        $moveDBParams.Add("SkipLagChecks", $true)
    }

    if ($SkipMaximumActiveDatabasesChecks)
    {
        $moveDBParams.Add("SkipMaximumActiveDatabasesChecks", $true)
    }

    if ((Get-ExchangeVersion) -in '2016','2019')
    {
        if ($SkipAllChecks)
        {
            $moveDBParams.Add("SkipAllChecks", $true)
        }

        if ($SkipCpuChecks)
        {
            $moveDBParams.Add("SkipCpuChecks", $true)
        }

        if ($SkipMoveSuppressionChecks)
        {
            $moveDBParams.Add("SkipMoveSuppressionChecks", $true)
        }
    }

    #Remove the PSBoundParameters we just re-formatted
    RemoveParameters -PSBoundParametersIn $PSBoundParameters -ParamsToRemove 'SkipActiveCopyChecks','SkipClientExperienceChecks','SkipLagChecks','SkipMaximumActiveDatabasesChecks','SkipMoveSuppressionChecks','SkipHealthChecks','SkipCpuChecks','SkipAllChecks'

    #Execute mailbox DB move
    Move-ActiveMailboxDatabase @PSBoundParameters @moveDBParams
}
#endregion

<#
    .SYNOPSIS
        Removes the Exchange PowerShell snapin, which is loaded by the
        Start/StopDagMaintennace.ps1 scripts in the $Exscripts
        directory. Prevents an issue where if a snapin is added by multiple
        modules during the same session, subsequent additions of the same
        module fail with 'An item with the same key has already been added'.
 
    .NOTES
        This similar function exists in the files
        MSFT_xExchAntiMalwareScanning.psm1 and MSFT_xExchMaintenanceMode.psm1.
        This was initially attempted to be put in xExchangeHelper.psm1 instead.
        However when xExchangeHelper.psm1 is loaded as a NestedModule from
        xExchange.psd1, functions within xExchangeHelper.psm1 do not appear to
        be able to detect added snapins loaded by scripts called from other
        modules. The added snapins were only detectable when running
        Get-PSSnapin directly from functions within
        MSFT_xExchAntiMalwareScanning.psm1 and MSFT_xExchMaintenanceMode.psm1.
#>

function Remove-HelperSnapin
{
    [CmdletBinding()]
    param()

    $snapinsToRemove = @('Microsoft.Exchange.Management.Powershell.E2010')

    foreach ($snapin in $snapinsToRemove)
    {
        if ($null -ne (Get-PSSnapin -Name $snapin -ErrorAction SilentlyContinue))
        {
            Write-Verbose -Message "'$snapin' snapin is currently loaded. Removing."

            Remove-PSSnapin -Name $snapin -ErrorAction SilentlyContinue -Confirm:$false
        }
    }
}

Export-ModuleMember -Function *-TargetResource