Configuración de la conmutación por error de máquinas virtuales entre redes virtualesConfigure VM failover between virtual networks

Se aplica a: System Center 2016 - Virtual Machine ManagerApplies To: System Center 2016 - Virtual Machine Manager

En este artículo se describe cómo controlar la replicación y conmutación por error de máquinas virtuales entre redes virtuales cuando no usa el servicio Azure Site Recovery para administrar la recuperación ante desastres.This article describes how to handle replication and failover of VMs between virtual networks when you're not using the Azure Site Recovery service to manage disaster recovery.

  • Se recomienda que utilice Azure Site Recovery para la replicación de máquinas virtuales.We recommend that you use Azure Site Recovery for replicating VMs. VMM no administra la Réplica de Hyper-V sin Site Recovery y debe usar los cmdlets de PowerShell para la Réplica de Hyper-V a fin de automatizar operaciones de Réplica de Hyper-V.VMM doesn't manage Hyper-V Replica without Site Recovery, and you need to use Hyper-V Replica PowerShell cmdlets to automate Hyper-V Replica operations.
  • Para la recuperación ante desastres, se recomienda usar redes virtuales principales y secundarias independientes.For disaster recovery, we recommend that you use separate primary and secondary virtual networks. Las máquinas virtuales principales se conectan a la red principal, y las máquinas virtuales de réplica, a la red secundaria.Primary VMs connect to the primary network, and replica VMs to the secondary network. De esta forma, se garantiza que ambas máquinas virtuales puedan conectarse a una red al mismo tiempo.This ensures that both VMs can be connected to a network at the same time.
  • Si tiene una única red virtual, utilice Site Recovery para automatizar la administración de red mediante la característica de asignación de red.If you have a single virtual network, use Site Recovery to automate network management using the network mapping feature. Si no utiliza Site Recovery, debe comprobar detenidamente los requisitos previos y el orden en que las máquinas virtuales se conectan a la red.If you don't use Site Recovery, you need to carefully check prerequisites, and the order in which VMs are attached to the network. En particular, la máquina virtual de réplica y la máquina virtual principal no deben estar conectadas a una única máquina virtual al mismo tiempo.In particular, the replica VM and primary VM mustn't be connected to the single virtual network at the same time. De lo contrario, los registros CA-PA deben eliminarse en VMM, lo que causará la pérdida de la conectividad de red.Otherwise, CA-PA records might get deleted in VMM, and cause network connectivity loss.

Solución de ejemploSample solution

En esta solución de ejemplo se describe el siguiente entorno:This sample solution describes the following environment:

  • Un único servidor VMM administra sitios principales y secundarios.A single VMM server manages both primary and secondary sites.
  • Las máquinas virtuales principal y de réplica se hospedan en una única red virtual de Hyper-V.Primary and replica VMs are hosted on a single Hyper-V virtual network.
  • Desea ejecutar una conmutación por error planeada y conservar la dirección IP de la máquina virtual después de la conmutación por error.You want to run a planned failover, and retain the IP address of the VM after the failover.
  • Las máquinas virtuales tienen direcciones IPv4.VMs have IPv4 addresses.

Antes de empezarBefore you start

  • Asegúrese de que la configuración del conmutador virtual y del conmutador lógico es válida y de que coincide con el tejido de VMM.Make sure that the virtual switch and logical switch settings are valid and match in the VMM fabric. De lo contrario, las operaciones de conexión a la red pueden no resultar satisfactorias después de la conmutación por error.If they don't, network attach operations might not succeed after failover.
  • La máquina virtual principal debe estar conectada a una red virtual.The primary VM should be connected to a virtual network
  • La máquina virtual de réplica no debe estar conectada a una red.The replica VM shouldn't be connected to a network
  • Debe asignarse una única dirección IP a cada adaptador de red de la máquina virtual principal.Only one IP address should be assigned to each network adapter of the primary VM. Ejecute este comando para asegurarse de que así sea.Run this command to ensure this. Si hay más de un adaptador de red conectado en la máquina virtual, ejecute el comando para cada adaptador, pero cambie el índice de matriz.If there's more than one connected network adapter on the VM, run it for adapter by changing the array index.

    $VMOnPD = Get-SCVirtualMachine -Name "VM Name" | where {$_.IsPrimaryVM -eq $true}
    Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[0].ID``
    
  • Asegúrese de que la dirección IP que el sistema operativo asigna a la máquina virtual es la misma que la dirección IP indicada anteriormente.Make sure that the IP address assigned to the VM by the operating system is the same as the IP address shown above. Inicie sesión en la máquina virtual y ejecute ipconfig para comprobarlo.Log onto the VM and run ipconfig to check this.

  • Compruebe que las tablas de búsqueda están correctamente configuradas en las máquinas virtuales principal y de réplica.Check that lookup tables are correctly set on the primary and replica . Para ello, ejecute el siguiente comando en cada servidor y asegúrese de que hay una entrada que corresponde a la dirección IP devuelta anterior: Get-NetVirtualizationLookupRecordTo do this, run the following command on each server, and ensure that there is an entry that corresponds to the IP address returned above: Get-NetVirtualizationLookupRecord
  • Compruebe que la dirección IP es IPv4 y no IPv6.Check that the IP address is IPv4, and not IPv6
  • Asegúrese de que ambas máquinas virtuales están desactivadas antes de ejecutar los scripts.Make sure both VMs are turned off before you run the scripts.
  • Asegúrese de que el estado de replicación está habilitado en ambas máquinas virtuales.Make sure that the replication state is enabled on both VMs.

Ejecución del script de conmutación por error planeadaRun the planned failover script

A continuación se explica el comportamiento de este script:Here's what this script does:

  1. Para cada adaptador de red de una máquina virtual principal, almacena la dirección IP, la red de máquina virtual y el grupo de direcciones IP.For each network adapter on a primary VM, it stores the IP address, VM network, and IP pool.
  2. Revoca todas las direcciones IP para cada adaptador de red en las máquinas virtuales principales y secundarias.Revokes all of the IP addresses for each network adapter on the primary and secondary VMs.
  3. Desconecta todos los adaptadores de red.Disconnects all network adapters.
  4. Conmuta por error las máquinas virtuales principales y secundarias.Fails over the primary and secondary VMs.
  5. Opcionalmente, inicia la replicación inversa.Optionally starts reverse replication.
  6. Proporciona la misma dirección IP (para cada adaptador de red) a la máquina virtual de réplica.Gives the same IP address (for each network adapter) to the replica VM.
  7. Asocia cada adaptador de red de la máquina virtual de réplica a las redes de máquina virtual almacenadas en el paso 1.Attaches each network adapter on the replicate VM to the VM networks that were stored in step 1.

Ejecución del scriptRun the script

El script admite dos argumentos:The script takes two arguments:

  • $VMName: nombre de la máquina virtual$VMName – Name of the virtual machine
  • $ReverseRep: argumento booleano para especificar si se debe realizar la replicación inversa o no.$ReverseRep – Boolean argument to specify whether reverse replication should be performed or not.
    • Si se acepta $true, la replicación inversa se inicia inmediatamente y no se permite cancelar la conmutación por error más adelante.If $true is passed then the reverse replication is started immediately, and you can't cancel failover later.
    • Después de que este script se ha completado correctamente con $ReverseRep como $true:After this script is completed successfully with $ReverseRep as $true:
      • La máquina virtual principal debe tener un estado de replicación Preparado para conmutación por error planeada.The primary VM should be in a Prepared for planned failover replication state.
      • La máquina virtual de réplica debe tener un estado de replicación Conmutación por error completada.The replica VM should be in a Failover complete replication state
    • Si se acepta $false, no realizará la replicación inversa.If $false is passed, then reverse replication won't be performed. ReverseRepORCancelFO.ps1 puede utilizarse para realizar la replicación inversa o cancelar la conmutación por error.ReverseRepORCancelFO.ps1 can be used to either perform reverse replication or cancel the failover
    • Después de que este script se ha completado correctamente con $ReverseRep como $false:After the script is completed successfully with $ReverseRep as $false:
      • La máquina virtual principal debe tener un estado de replicación Preparado para conmutación por error planeada.The primary VM should be in a Prepared for planned failover replication state
      • La máquina virtual de réplica debe tener un estado de replicación Conmutación por error completada.The replica VM should be in a Failover complete replication state.

Si el script no completa cualquiera de los pasos, debe realizar manualmente los pasos con errores y después volver a la ventana de PowerShell.If the script doesn't complete any of the steps, you need to manually complete the failed steps, and then return to the PowerShell window. Los pasos de script incluyen la conmutación por error de la máquina virtual principal, la conmutación por error de la máquina virtual de réplica y, opcionalmente, la replicación inversa.The script steps include failover of the primary VM, failover of the replica VM, and optionally, reverse replication.

Ejecute el script:Run the script:

 Param(
 [Parameter(Mandatory=$True)]
   [string]$VMName,
 [Parameter(Mandatory=$true)]
   [boolean]$ReverseRep
)

# the script running on system with SCVMM Console/PowerShell installed. Also, requires Hyper-V powershell module.``

Import-Module hyper-v

## Refresh VM configuration and initialize
Write-Host -ForegroundColor Green (Get-Date) ".....Refreshing the VMs..."
Get-SCVirtualMachine -Name $VMName | Read-SCVirtualMachine

$VMOnPD = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $true}
$VMOnDR = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $false}

if ($VMOnPD.StatusString -ne "Stopped")
{
    write-host -ForegroundColor Red (Get-Date) "....VM is not in stopped state. Actual State " $VMOnPD.StatusString
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$error.Clear()
$VMRepConfig = Get-VMReplication -ComputerName $VMOnPD.HostName -VMName $VMOnPD.Name
$VMRepConfig = Get-VMReplication -ComputerName $VMOnDR.HostName -VMName $VMOnPD.Name

if ($error -ne 0)
{
    $temp = $VMOnPD.HostName.Split(".")
    $primaryHostName = $temp[0]

    $temp = $VMOnDR.HostName.Split(".")
    $recoveryHostName = $temp[0]

    write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using FQDN, switching to Hostname"
    write-host -ForegroundColor Yellow (Get-Date) "....Primary Hostname: " $primaryHostName " Replica Hostname: " $recoveryHostName

    $error.Clear()
    $VMRepConfig = Get-VMReplication -ComputerName $primaryHostName -VMName $VMOnPD.Name
    $VMRepConfig = Get-VMReplication -ComputerName $recoveryHostName -VMName $VMOnPD.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using Hostname"
        write-host -ForegroundColor Red (Get-Date) "....Exiting"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) "....Successful in getting VM Replication state using Hostname"
}
else
{
    $primaryHostName = $VMOnPD.HostName
    $recoveryHostName = $VMOnDR.HostName
}

$VMOnPDAdapter = Get-SCVirtualNetworkAdapter -VM $VMonPD
$VMOnDRAdapter = Get-SCVirtualNetworkAdapter -VM $VMonDR

$fileName = $VMName + (Get-Date).ToString() + ".txt"
$fileName = $fileName.Replace("/","_")
$fileName = $fileName.Replace(":","_")

Write-Host -ForegroundColor Yellow (Get-Date) "....Dumping network information for $VMName to file $fileName"
Write-Host -ForegroundColor Yellow (Get-Date) "....Number of Network adapters found: " $VMOnPDAdapter.count

$VMNetwork = @()
$VMSubnet = @()
$Pools = @()

$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{
    if ($vmAdapter.VMNetwork -eq $null)
    {
        $VMNetwork = $VMNetwork + $null
        $VMSubnet = $VMSubnet + $null
        $Pools = $Pools + $null
        $counter = $counter + 1
        continue
    }

    $VMNetwork = $VMNetwork + (Get-SCVMNetwork -Name $vmAdapter.VMNetwork.Name -ID $vmAdapter.VMNetwork.ID)
    $VMSubnet = $VMSubnet + (Get-SCVMSubnet -Name $vmAdapter.VMSubnet.Name | where {$_.VMNetwork.ID -eq $vmAdapter.VMNetwork.ID})
    #$PortClassification = Get-SCPortClassification | where {$_.Name -eq "Guest Dynamic IP"}
    $Pools = $Pools + (Get-SCStaticIPAddressPool -IPv4 | where {$_.VMsubnet.name -eq $vmAdapter.VMSubnet.Name})

    Out-File -FilePath $fileName -InputObject $VMNetwork[$counter] -Append
    Out-File -FilePath $fileName -InputObject $VMSubnet[$counter] -Append
    Out-File -FilePath $fileName -InputObject $Pools[$counter] -Append

    $counter = $counter + 1
}

if ($error.Count -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) "....Error is gathering information for $VMName. No changes made"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$IP = @()
$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $IP = $IP + $null
        $counter = $counter + 1
        continue
    }

    ## Revoke IP
    $error.Clear()
    $IP = $IP +(Get-SCIPAddress –GrantToObjectId $VMOnPD.VirtualNetworkAdapters[$counter].ID)
    Write-Host -ForegroundColor Yellow (Get-Date) "....Revoking IP " $IP[$counter] "from Primary VM"
    Revoke-SCIPAddress $IP[$counter]
    if ($error.count -eq 0)
    {
        Write-Host -ForegroundColor Green (Get-Date) "....." $IP[$counter] "revoke completed"
    }

    ## Disconnect Primary VM
    Write-Host -ForegroundColor Yellow (Get-Date) "....Disconnecting Primary VM from Network " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnPD.VirtualNetworkAdapters[$counter] -NoLogicalNetwork -NoConnection -NoPortClassification
    Write-Host -ForegroundColor Green (Get-Date) "....Network Adapter '" $counter "' of Primary VM Disconnected"

    $counter = $counter + 1
}

## Start failover
Write-Host -ForegroundColor Yellow (Get-Date) ".....We are going to Failover " $VMName " from " $primaryHostName " to " $recoveryHostName

$error.Clear()
Start-VMFailover -ComputerName $primaryHostName -VMName $VMOnPD.Name -Prepare -Confirm:$false

start-sleep 5

Write-Host -ForegroundColor Yellow (Get-Date) ".....Completing Failover on Replica site..."
Start-VMFailover -ComputerName $recoveryHostName -VMName $VMOnDR.Name -Confirm:$false
if ($ReverseRep)
{
    write-host -ForegroundColor Green (Get-Date) ".....Starting Reverse Replication..."
    Set-VMReplication -ComputerName $recoveryHostName -reverse -VMName $VMOnDR.Name
}

if ($error -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) ".....Error occured during Planned Failover for VM $VMName"
    write-host -ForegroundColor Red (Get-Date) ".....Please manually complete Failover before continuing"
    Write-Host -ForegroundColor Red (Get-Date) ".....Press any key to continue..."
    $ignoreKey = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Write-Host -ForegroundColor Green (Get-Date) ".....Connecting Network(s) to Failed-over VM"

$counter = 0
foreach($vmAdapter in $VMOnPDAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $counter = $counter + 1
        continue
    }

    Write-Host -ForegroundColor Yellow (Get-Date) "Granting " $IP[$counter] "to Failed-over VM"
    Grant-SCIPAddress -GrantToObjectType "VirtualNetworkAdapter" -GrantToObjectID $VMOnDRAdapter[$counter].ID -StaticIPAddressPool $Pools[$counter] –IPAddress $IP[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Granting IP completed"

    Write-Host -ForegroundColor Yellow (Get-Date) "Connecting Replica VM to " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnDRAdapter[$counter] -IPv4AddressType static -VMNetwork $VMNetwork[$counter] -VMSubnet $VMSubnet[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Network Adapter '" $counter "' of Failed-over VM connected to " $VMNetwork[$counter]

    $counter = $counter + 1
}

Ejecución de la replicación inversa o cancelación del scriptRun the reverse replication/cancel script

A continuación se explica el comportamiento de este script:Here's what this script does:

  1. Si no ejecuta la replicación inversa en el script de conmutación por error, puede utilizar este script para la replicación inversa o cancelar la conmutación por error.If you didn't run reverse replication in the failover script, you can use this script for reverse replication, or to cancel the failover.
  2. Si realiza la cancelación, el script invierte los pasos de la red y restaura las conexiones de la máquina virtual principal, después de desconectar las redes de la máquina virtual de réplica.If you cancel, the script reverses the networking steps, and restores the primary VM connections, after disconnecting the replica VM networks.

Ejecución del scriptRun the script

Este script debe ejecutarse para el script de conmutación por error con $ReverseRep establecido en $false. Este script adopta tres argumentos:This script should be run for the failover script with $ReverseRep set to $false .This script takes three arguments:

  • $VMName: nombre de la máquina virtual.$VMName: VM name
  • $ReverseRep: argumento booleano para especificar si se debe realizar la replicación inversa.$ReverseRep: Boolean argument to specify whether reverse replication should be performed. $true indica que la replicación inversa se ejecuta.$true indicates that reverse replication runs.
  • $CancelFO: argumento booleano para especificar si se cancela la conmutación por error.$CancelFO - Boolean argument to specify whether the failover is cancelled. $true indica la cancelación en los sitios principal y de recuperación.$true indicates cancellation on primary and recovery sites.

Solo uno de los argumentos $ReverseRep y $CancelFO puede aceptar $true al mismo tiempo.One and only one of $ReverseRep and $CancelFO can be passed $true at a time. Después de que el script se ejecuta correctamente, el estado de ambas máquinas virtuales debe ser Replicación habilitada.After the script runs successfully, the state on both VMs should be Replication enabled’.

Ejecute el script:Run the script:

Param(
 [Parameter(Mandatory=$True)]
   [string]$VMName,
 [Parameter(Mandatory=$true)]
   [boolean]$ReverseRep,
 [Parameter(Mandatory=$true)]
   [boolean]$CancelFO
)

# the script running on system with SCVMM Console/PowerShell installed. Also, requires Hyper-V powershell module.

Import-Module hyper-v

if ($ReverseRep -eq $CancelFO)
{
    write-host -ForegroundColor Red (Get-Date) "....Please ensure that one and only one of the parameters -ReverseRep and -CancelFO is passed as $True"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

## Refresh VM configuration and initialize
Write-Host -ForegroundColor Green (Get-Date) ".....Refreshing the VMs..."
Get-SCVirtualMachine -Name $VMName | Read-SCVirtualMachine

$VMOnPD = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $true}
$VMOnDR = Get-SCVirtualMachine -Name $VMName | where {$_.IsPrimaryVM -eq $false}

$error.Clear()
$VMRepConfig = Get-VMReplication -ComputerName $VMOnPD.HostName -VMName $VMOnPD.Name
$VMRepConfig = Get-VMReplication -ComputerName $VMOnDR.HostName -VMName $VMOnPD.Name

if ($error -ne 0)
{
    $temp = $VMOnPD.HostName.Split(".")
    $primaryHostName = $temp[0]

    $temp = $VMOnDR.HostName.Split(".")
    $recoveryHostName = $temp[0]

    write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using FQDN, switching to Hostname"
    write-host -ForegroundColor Yellow (Get-Date) "....Primary Hostname: " $primaryHostName " Replica Hostname: " $recoveryHostName

    $error.Clear()
    $VMRepConfig = Get-VMReplication -ComputerName $primaryHostName -VMName $VMOnPD.Name
    $VMRepConfig = Get-VMReplication -ComputerName $recoveryHostName -VMName $VMOnPD.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) "....Error in getting VM Replication state using Hostname"
        write-host -ForegroundColor Red (Get-Date) "....Exiting"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) "....Successful in getting VM Replication state using Hostname"
}
else
{
    $primaryHostName = $VMOnPD.HostName
    $recoveryHostName = $VMOnDR.HostName
}

if ($VMOnDR.ReplicationStatus.ReplicationState -ne "Recovered")
{
    write-host -ForegroundColor Red (Get-Date) "....Replica VM is not in Failed over state. Actual State " $VMOnDR.ReplicationStatus.ReplicationState
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$error.Clear()

if ($ReverseRep -eq $true)
{
    write-host -ForegroundColor Green (Get-Date) ".....Starting Reverse Replication..."
    Set-VMReplication -ComputerName $recoveryHostName -reverse -VMName $VMOnDR.Name

    if ($error -ne 0)
    {
        write-host -ForegroundColor Red (Get-Date) ".....Error occured during Reverse Replication for VM $VMName"
        write-host -ForegroundColor Red (Get-Date) ".....Please manually complete Reverse replication"
        exit 1
    }

    write-host -ForegroundColor Green (Get-Date) ".....Reverse Replication completed..."
    exit 0
}

if ($VMOnDR.StatusString -ne "Stopped")
{
    write-host -ForegroundColor Red (Get-Date) "....VM is not in stopped state. Actual State " $VMOnDR.StatusString
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$VMOnPDAdapter = Get-SCVirtualNetworkAdapter -VM $VMonPD
$VMOnDRAdapter = Get-SCVirtualNetworkAdapter -VM $VMonDR

$fileName = $VMName + (Get-Date).ToString() + ".txt"
$fileName = $fileName.Replace("/","_")
$fileName = $fileName.Replace(":","_")

Write-Host -ForegroundColor Yellow (Get-Date) "....Dumping network information for $VMName to file $fileName"
Write-Host -ForegroundColor Yellow (Get-Date) "....Number of Network adapters found on Failed-over VM: " $VMOnDRAdapter.count

$VMNetwork = @()
$VMSubnet = @()
$Pools = @()

$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{
    if ($vmAdapter.VMNetwork -eq $null)
    {
        $VMNetwork = $VMNetwork + $null
        $VMSubnet = $VMSubnet + $null
        $Pools = $Pools + $null
        $counter = $counter + 1
        continue
    }

    $VMNetwork = $VMNetwork + (Get-SCVMNetwork -Name $vmAdapter.VMNetwork.Name -ID $vmAdapter.VMNetwork.ID)
    $VMSubnet = $VMSubnet + (Get-SCVMSubnet -Name $vmAdapter.VMSubnet.Name | where {$_.VMNetwork.ID -eq $vmAdapter.VMNetwork.ID})
    #$PortClassification = Get-SCPortClassification | where {$_.Name -eq "Guest Dynamic IP"}
    $Pools = $Pools + (Get-SCStaticIPAddressPool -IPv4 | where {$_.VMsubnet.name -eq $vmAdapter.VMSubnet.Name})

    Out-File -FilePath $fileName -InputObject $VMNetwork[$counter] -Append
    Out-File -FilePath $fileName -InputObject $VMSubnet[$counter] -Append
    Out-File -FilePath $fileName -InputObject $Pools[$counter] -Append

    $counter = $counter + 1
}

if ($error.Count -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) "....Error is gathering information for $VMName. No changes made"
    write-host -ForegroundColor Red (Get-Date) "....Exiting"
    exit 1
}

$IP = @()
$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $IP = $IP + $null
        $counter = $counter + 1
        continue
    }

    ## Revoke IP
    $error.Clear()
    $IP = $IP +(Get-SCIPAddress –GrantToObjectId $VMOnDR.VirtualNetworkAdapters[$counter].ID)
    Write-Host -ForegroundColor Yellow (Get-Date) "....Revoking IP " $IP[$counter] "from Replica VM"
    Revoke-SCIPAddress $IP[$counter]
    if ($error.count -eq 0)
    {
        Write-Host -ForegroundColor Green (Get-Date) "....." $IP[$counter] "revoke completed"
    }

    ## Disconnect Replica VM
    Write-Host -ForegroundColor Yellow (Get-Date) "....Disconnecting Replica VM from Network " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnDR.VirtualNetworkAdapters[$counter] -NoLogicalNetwork -NoConnection -NoPortClassification
    Write-Host -ForegroundColor Green (Get-Date) "....Network Adapter '" $counter "' of Replica VM Disconnected"

    $counter = $counter + 1
}

## Cancel failover
Write-Host -ForegroundColor Yellow (Get-Date) ".....We are going to Cancel Failover " $VMName " on both " $primaryHostName " and " $recoveryHostName

$error.Clear()
Stop-VMFailover -ComputerName $recoveryHostName -VMName $VMName
Start-Sleep -Seconds 10
Stop-VMFailover -ComputerName $primaryHostName -VMName $VMName

if ($error -ne 0)
{
    write-host -ForegroundColor Red (Get-Date) ".....Error occured during Cancel Failover for VM $VMName"
    write-host -ForegroundColor Red (Get-Date) ".....Please manually Cancel Failover on both Primary and Recovery Server"
    Write-Host -ForegroundColor Red (Get-Date) ".....Press any key to continue..."
    $ignoreKey = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}

Write-Host -ForegroundColor Yellow (Get-Date) ".....Connecting Network(s) back to the Primary VM"

$counter = 0
foreach($vmAdapter in $VMOnDRAdapter)
{

    if ($VMNetwork[$counter] -eq $null)
    {
        Write-Host -ForegroundColor Yellow (Get-Date) ".....Network Adapter '" $counter "' not connected"
        $counter = $counter + 1
        continue
    }

    Write-Host -ForegroundColor Yellow (Get-Date) "Granting " $IP[$counter] "to Primary VM"
    Grant-SCIPAddress -GrantToObjectType "VirtualNetworkAdapter" -GrantToObjectID $VMOnPDAdapter[$counter].ID -StaticIPAddressPool $Pools[$counter] –IPAddress $IP[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Granting IP completed"

    Write-Host -ForegroundColor Yellow (Get-Date) "Connecting Primary VM to " $VMNetwork[$counter]
    Set-SCVirtualNetworkAdapter -VirtualNetworkAdapter $VMOnPDAdapter[$counter] -IPv4AddressType static -VMNetwork $VMNetwork[$counter] -VMSubnet $VMSubnet[$counter]
    Write-Host -ForegroundColor Green (Get-Date) "Network Adapter '" $counter "' of Primary VM connected to " $VMNetwork[$counter]

    $counter = $counter + 1
}