Настройка отработки отказа виртуальной машины между виртуальными сетямиConfigure VM failover between virtual networks

Важно!

Поддержка этой версии Virtual Machine Manager (VMM) прекращена. Рекомендуем перейти на VMM 2019.This version of Virtual Machine Manager (VMM) has reached the end of support, we recommend you to upgrade to VMM 2019.

В этой статье описываются способы репликации и отработки отказа виртуальных машин в System Center Virtual Machine Manager (VMM) между виртуальными сетями, если вы не используете службу Azure Site Recovery для управления процессами аварийного восстановления.This article describes how to handle replication and failover of VMs in System Center - Virtual Machine Manager (VMM), between virtual networks when you're not using the Azure Site Recovery service to manage disaster recovery.

  • Для репликации виртуальных машин мы рекомендуем использовать Azure Site Recovery.We recommend that you use Azure Site Recovery for replicating VMs. VMM не поддерживает управление репликами Hyper-V без Site Recovery, и для автоматизации операций c репликой Hyper-V вам нужно использовать командлеты PowerShell для реплики 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.
  • Для аварийного восстановления мы рекомендуем разделить основную и дополнительную виртуальные сети.For disaster recovery, we recommend that you use separate primary and secondary virtual networks. Основные виртуальные машины следует подключить к основной сети, а виртуальные машины реплики — к дополнительной.Primary VMs connect to the primary network, and replica VMs to the secondary network. Так вы обеспечите возможность одновременного подключения к сети двух типов виртуальных машин.This ensures that both VMs can be connected to a network at the same time.
  • Если у вас только одна виртуальная сеть, используйте Site Recovery для автоматизации управления сетью с помощью функции сетевого сопоставления.If you have a single virtual network, use Site Recovery to automate network management using the network mapping feature. Если вы не используете Site Recovery, тщательно проверьте все предварительные требования и порядок подключения виртуальных машин к сети.If you don't use Site Recovery, you need to carefully check prerequisites, and the order in which VMs are attached to the network. В частности, виртуальная машина реплики и основная виртуальная машина не должны подключаться к одной виртуальной сети одновременно.In particular, the replica VM and primary VM mustn't be connected to the single virtual network at the same time. В противном случае в VMM могут быть удалены записи CA-PA, что приведет к потере сетевого подключения.Otherwise, CA-PA records might get deleted in VMM, and cause network connectivity loss.

Пример решенияSample solution

В этом примере решения описана следующая среда.This sample solution describes the following environment:

  • Один сервер VMM управляет основным и дополнительным сайтами.A single VMM server manages both primary and secondary sites.
  • Основная виртуальная машина и виртуальная машина реплики размещаются в одной виртуальной сети Hyper-V.Primary and replica VMs are hosted on a single Hyper-V virtual network.
  • Вам нужно выполнить плановую отработку отказа и сохранить IP-адрес виртуальной машины после отработки отказа.You want to run a planned failover, and retain the IP address of the VM after the failover.
  • Виртуальные машины имеют адреса IPv4.VMs have IPv4 addresses.

Перед началом работыBefore you start

  • Убедитесь, что настройки виртуального коммутатора и логического коммутатора в структуре VMM совпадают.Make sure that the virtual switch and logical switch settings are valid and match in the VMM fabric. В противном случае операция подключения к сети после отработки отказа может завершиться неудачей.If they don't, network attach operations might not succeed after failover.

  • Основная виртуальная машина должна быть подключена к виртуальной сети.The primary VM should be connected to a virtual network

  • Виртуальная машина реплики не должна быть подключена к сети.The replica VM shouldn't be connected to a network

  • Каждому сетевому адаптеру основной виртуальной машины должен быть назначен только один IP-адрес.Only one IP address should be assigned to each network adapter of the primary VM. Выполните следующую команду, чтобы проверить это.Run this command to ensure this. Если к виртуальной машине подключено больше одного сетевого адаптера, запустите команду для каждого адаптера, изменяя индекс массива.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``
    
  • Убедитесь, что операционная система назначила виртуальной машине тот же IP-адрес, который указан выше.Make sure that the IP address assigned to the VM by the operating system is the same as the IP address shown above. Войдите на виртуальную машину и выполните ipconfig, чтобы убедиться в этом.Log onto the VM and run ipconfig to check this.

  • Убедитесь, что таблицы подстановки правильно установлены на основной виртуальной машине и виртуальной машине реплики.Check that lookup tables are correctly set on the primary and replica . Для этого выполните на каждом сервере следующую команду и убедитесь в том, что существует запись для полученного выше IP-адреса: 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

  • Убедитесь, что IP-адрес имеет формат IPv4, а не IPv6.Check that the IP address is IPv4, and not IPv6

  • Перед выполнением скриптов убедитесь, что обе виртуальные машины отключены.Make sure both VMs are turned off before you run the scripts.

  • Убедитесь, что на обеих виртуальных машинах включена репликация.Make sure that the replication state is enabled on both VMs.

Запустите скрипт плановой отработки отказа.Run the planned failover script

Этот скрипт выполняет следующее.Here's what this script does:

  1. Сохраняет IP-адрес, сеть и пул IP-адресов для каждого сетевого адаптера на основной виртуальной машине.For each network adapter on a primary VM, it stores the IP address, VM network, and IP pool.
  2. Отменяет все IP-адреса для каждого сетевого адаптера на основной и дополнительной виртуальных машинах.Revokes all of the IP addresses for each network adapter on the primary and secondary VMs.
  3. Отключает все сетевые адаптеры.Disconnects all network adapters.
  4. Выполняет отработку отказа для основной и дополнительной виртуальных машин.Fails over the primary and secondary VMs.
  5. При необходимости запускает обратную репликацию.Optionally starts reverse replication.
  6. Присваивает виртуальной машине реплики те же IP-адреса, которые использовались на каждом из сетевых адаптеров.Gives the same IP address (for each network adapter) to the replica VM.
  7. Присоединяет каждый сетевой адаптер виртуальной машины реплики к тем сетям, которые были сохранены на шаге 1.Attaches each network adapter on the replicate VM to the VM networks that were stored in step 1.

Выполнение скриптаRun the script

Скрипт принимает два параметра.The script takes two arguments:

  • $VMName — это имя виртуальной машины.$VMName – Name of the virtual machine
  • $ReverseRep — логический аргумент, который указывает, нужно ли выполнять обратную репликацию.$ReverseRep – Boolean argument to specify whether reverse replication should be performed or not.
    • Если передается значение $true, обратная репликация запускается немедленно, и тогда отработку отказа невозможно будет позже отменить.If $true is passed then the reverse replication is started immediately, and you can't cancel failover later.
    • После успешного выполнение скрипта со значением $true для параметра $ReverseRep:After this script is completed successfully with $ReverseRep as $true:
      • основная виртуальная машина будет иметь статус репликации Подготовлено к плановой отработке отказа;The primary VM should be in a Prepared for planned failover replication state.
      • виртуальная машина реплики будет иметь статус репликации Отработка отказа завершена.The replica VM should be in a Failover complete replication state
    • Если передается значение $false, обратная репликация не выполняется.If $false is passed, then reverse replication won't be performed. После этого вы сможете применить ReverseRepORCancelFO.ps1 для выполнения обратной репликации или отмены отработки отказа.ReverseRepORCancelFO.ps1 can be used to either perform reverse replication or cancel the failover
    • После успешного выполнение скрипта со значением $false для параметра $ReverseRep:After the script is completed successfully with $ReverseRep as $false:
      • основная виртуальная машина будет иметь статус репликации Подготовлено к плановой отработке отказа;The primary VM should be in a Prepared for planned failover replication state
      • виртуальная машина реплики будет иметь статус репликации Отработка отказа завершена.The replica VM should be in a Failover complete replication state.

Если скрипт не выполнит одно из действий, необходимо выполнить это действие вручную, а затем вернуться в окно 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. Шаги скрипта включают отработку отказа для основной виртуальной машины, отработку отказа для виртуальной машины реплики и (необязательно) обратную репликацию.The script steps include failover of the primary VM, failover of the replica VM, and optionally, reverse replication.

Выполните скрипт: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
}

Запуск скрипта для обратной репликации или отменыRun the reverse replication/cancel script

Этот скрипт выполняет следующее.Here's what this script does:

  1. Если скрипт перехода на другой ресурс выполнялся без применения обратной репликации, вы можете использовать этот скрипт для выполнения обратной репликации или отмены отработки отказа.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. Если вы выполняете отмену, скрипт применяет все сетевые действия в обратном порядке и восстанавливает подключения основной виртуальной машины, предварительно отключив все сети для виртуальной машины реплики.If you cancel, the script reverses the networking steps, and restores the primary VM connections, after disconnecting the replica VM networks.

Выполнение скриптаRun the script

Этот скрипт должен выполняться, если скрипт отработки отказа запущен со значением $false для параметра $ReverseRep. Этот скрипт принимает три аргумента:This script should be run for the failover script with $ReverseRep set to $false .This script takes three arguments:

  • $VMName — имя виртуальной машины.$VMName: VM name
  • $ReverseRep — логический аргумент, который указывает, нужно ли выполнять обратную репликацию.$ReverseRep: Boolean argument to specify whether reverse replication should be performed. Значение $true обозначает, что обратная репликация выполняется.$true indicates that reverse replication runs.
  • $CancelFO — логический аргумент, который указывает, нужно ли отменять отработку отказа.$CancelFO - Boolean argument to specify whether the failover is cancelled. Значение $true обозначает, что выполняется отмена на основном и резервном сайтах.$true indicates cancellation on primary and recovery sites.

Параметры $ReverseRep и $CancelFO не могут одновременно иметь значения $true.One and only one of $ReverseRep and $CancelFO can be passed $true at a time. После успешного выполнения скрипта на обеих виртуальных машинах должно быть установлено состояние Репликация включена.After the script runs successfully, the state on both VMs should be Replication enabled’.

Выполните скрипт: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
}