Public/Move-AvdSessionHost.ps1

function Move-AvdSessionHost {
    <#
    .SYNOPSIS
    Moving sessionhosts from an Azure Virtual Desktop hostpool to a new one.
    .DESCRIPTION
    The function will move sessionhosts to a new Azure Virtual Desktop hostpool.
    .PARAMETER FromHostpoolName
    Enter the source AVD Hostpool name
    .PARAMETER FromResourceGroupName
    Enter the source Hostpool resourcegroup name
    .PARAMETER ToHostpoolName
    Enter the destination AVD Hostpool name
    .PARAMETER ToResourceGroupName
    Enter the destination Hostpool resourcegroup name
    .PARAMETER SessionHostName
    Enter the sessionhosts name avd-hostpool/avd-host-1.avd.domain
    .PARAMETER Id
    Enter the sessionhosts resource id
    .PARAMETER Force
    Force the move of the sessionhost
    .EXAMPLE
    Move-AvdSessionHost -FromHostpoolName avd-hostpool -FromResourceGroupName rg-avd-01 -ToHostpoolName avd-hostpool-02 -ToResourceGroupName rg-avd-02 -SessionHostName avd-host-1.avd.domain
    .EXAMPLE
    Move-AvdSessionHost -Id /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rg-avd-01/providers/Microsoft.DesktopVirtualization/hostPools/avd-hostpool/sessionHosts/avd-host-1.avd.domain -ToHostpoolName avd-hostpool-02 -ToResourceGroupName rg-avd-02
    #>

    [CmdletBinding(DefaultParameterSetName = 'All')]
    param
    (
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'SingleObject')]
        [ValidateNotNullOrEmpty()]
        [string]$FromHostpoolName,
    
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'SingleObject')]
        [ValidateNotNullOrEmpty()]
        [string]$FromResourceGroupName,
        
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'ResourceID')]
        [parameter(Mandatory, ParameterSetName = 'SingleObject')]
        [ValidateNotNullOrEmpty()]
        [string]$ToHostpoolName,
    
        [parameter(Mandatory, ParameterSetName = 'All')]
        [parameter(Mandatory, ParameterSetName = 'ResourceID')]
        [parameter(Mandatory, ParameterSetName = 'SingleObject')]
        [ValidateNotNullOrEmpty()]
        [string]$ToResourceGroupName,

        [parameter(Mandatory, ParameterSetName = 'SingleObject')]
        [string]$SessionHostName,
        
        [parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ResourceID')]
        [String]$Id,

        [parameter()]
        [switch]$Force = $false
    )
    Begin {
        Write-Verbose "Start moving session hosts"
        AuthenticationCheck
        $token = GetAuthToken -resource $global:AzureApiUrl
    }
    Process {
        switch ($PsCmdlet.ParameterSetName) {
            All {
                try {
                    $sessionHosts = Get-AvdSessionHost -HostpoolName $FromHostpoolName -ResourceGroupName $FromResourceGroupName
                }
                catch {
                    Write-Error "Something went wrong, $_"
                }
            }
            SingleObject {
                if ($SessionHostName.Contains($FromHostpoolName)) { 
                    Write-Verbose "SessionHostName contains hostpool name, removing it"
                    $SessionHostName = $SessionHostName.Replace($FromHostpoolName + "/", "")
                }
                $sessionHosts = Get-AvdSessionHost -HostpoolName $FromHostpoolName -ResourceGroupName $FromResourceGroupName -Name $SessionHostName
            }
            ResourceID {
                try {
                    $sessionHosts = Get-AvdSessionHost -Id $Id
                }
                catch {
                    Write-Error "Please provide the Get-AvdSessionHost output"
                }
            }
        }         
        if ($sessionHosts) {
            Write-Verbose "Found $($sessionHosts.count) sessionhosts"
            $sessionHosts | ForEach-Object {
                try {
                    if ($Force.IsPresent) {
                        Write-Verbose "Force parameter is present, skipping confirmation, moving all sessionhosts even if they are in use"
                        Write-Verbose "Removing sessionhost $($_.name) from $FromHostPoolName"
                    }
                    else {
                        $confirm = Read-Host "Are you sure you want to move sessionhost $($_.name) from $FromHostpoolName to $ToHostpoolName? (y/n)"
                        if ($confirm -ne "y") {
                            Write-Verbose "Skipping sessionhost $($_.name)"
                            continue
                        }
                    }
                    $sessionhostDeleteUrl = "{0}{1}?api-version={2}&force={3}" -f $global:AzureApiUrl , $_.id, $global:sessionHostApiVersion, $Force
                    $deleteParameters = @{
                        uri     = $sessionhostDeleteUrl
                        Headers = $token
                        Method  = "DELETE"
                    }
                    Invoke-RestMethod @deleteParameters
                    $vmResourceId = $_.properties.resourceId
                    Write-Verbose "Requesting new token in hostpool $ToHostpoolName"
                    $avdHostpoolToken = Update-AvdRegistrationToken -HostpoolName $ToHostpoolName -ResourceGroupName $ToResourceGroupName
                    # Script part
                    $global = [System.Collections.ArrayList]@()
                    $global.Add('Set-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\RDInfraAgent -Name RegistrationToken -Value ' + $($avdHostpoolToken.properties.registrationInfo.token) + '') | Out-Null
                    $global.Add('Set-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\RDInfraAgent -Name IsRegistered -Value 0') | Out-Null
                    $global.Add('Restart-Service -Name RDAgentBootLoader') | Out-Null
                    $moveBody = @{
                        commandId = "RunPowerShellScript"
                        script    = $global
                    }   
                    $runCommandUrl = "{0}{1}/runCommand?api-version={2}" -f $global:AzureApiUrl, $vmResourceId, $global:vmApiVersion
                    Write-Verbose "Moving sessionhost $name to $ToHostPoolName"
                    $parameters = @{
                        URI     = $runCommandUrl 
                        Method  = "POST"
                        Body    = $moveBody | ConvertTo-Json
                        Headers = $token
                    }
                    Invoke-RestMethod @parameters
                }
                catch {
                    Throw $_.Exception.Message
                }
            }
            else {
                Write-Error "No sessionhosts found in hostpool $FromHostpoolName"
            }
        }
    }
}