5 minute read

As discussed in one of my previous blog posts I’m now working on a migration project to move virtual machines from one datacenter to another datacenter. I’m using Hyper-V Replica to replicate all the virtual machines with as less down-time as possible. Now the issue with Hyper-V replica is that you can only replicate to one and only one storage location. In the settings as seen below you can only define one location:

Hyper-V-Replica location

My advise for Hyper-V replica (if you are using it like me as a temporary solution to migrate from one datacenter to another) is to have a temporary migration LUN to replicate the virtual machines. After the migration you can then just delete the temporary LUN.

Now this customer has a policy of separating LUNS per operating system for several technical (like speed of disks, different backup procedures, etc) and phylosophic (they don’t like to put all their eggs in the same basket) reasons. So this means I have 1 lun for Windows Clients, 1 lun for Windows server 2012, 1 lun for Windows server 2008, 1 for SQL servers, etc. Sadly enough after each planned failover from one datacenter to the other I need to do a manual storage migration of the virtual machine in VMM to the corresponding LUN on the new Hyper-V Cluster based  on the operating system. So I need to perform the following manual steps for each machine after the failover:

  1. Connect to the new hyperV cluster
  2. Verify planned fail over was succesfull
  3. Remove replication for the machine in Failover Clustermanager
  4. Connect to the VMM console
  5. Identify the operating system version of the migrated VM
  6. Perform a quick storage migration to the corresponding LUN based on the operating system of the virtual machine.
  7. and wait….

This are a lot of manual steps and a lot of waiting time as well!!!

So I created a PowerShell script based on the VM storage Migration script from Niklas Akerlund. The script will:

  1. Connect to VMM
  2. Get all running Virtual machines
  3. Identify virtual machines that have a disk on the temporary LUN
  4. Connect to the host of the VM and remove Hyper-V replica for the machine
  5. Start a Quick storage migration asynchronous job in VMM (you need to use the –asynchronous option otherwise the script will not continue and not launch another job until the first one is finished)

Now let’s discuss the parameters needed for the script to be able to run:


$lun2012 = “C:\ClusterStorage\sata_w2k12r2_OS_ds1”
$lun2008 = “C:\ClusterStorage\sata_w2k8r2_os_ds3”
$lun2003 = “C:\ClusterStorage\sata_w2k3_os_ds1”
$lunmig = “C:\ClusterStorage\sata_migration_ds1”
$lunclients = “C:\ClusterStorage\sata_clients_ds1”
$VMHostGroup = “All Hosts”
$VMHostCluster = “cluster1”
$VMHost = $null
$maxjobs = 10


  • $lun2012 etc are variables that define my Cluster Shared volumes
  • $Lunmig is the location of my temporary LUN that Hyper-V replica is using to replicate the VM’s. Please note the double \ for the lunmig variable
  • $MaxJobs is used to defined how many simultaneous storage migration your Hyper-V can take. If you want to know more about what this number should be have a look here.  So if in Hyper-V you have configured a limit of 2 set the $MaxJobs to 2 as well.



Please find below the script:


Storage Migration of VMM VM and also remove hyperv-replication if -removereplica is used.

More information can be found here: http://mscloud.be/bulk-migrate-virtual-machines-after-hyper-v-replication

Author: Alexandre Verkinderen
#region Variables

#define CSV locations
$lun2012 = “C:\ClusterStorage\sata_w2k12r2_OS_ds1”
$lun2008 = “C:\ClusterStorage\sata_w2k8r2_os_ds1”
$lun2003 = “C:\ClusterStorage\sata_w2k3_os_ds1”
$lunmig = “C:\ClusterStorage\sata_migration_ds1”
$lunclients = “C:\ClusterStorage\sata_clients_ds1”
#define LOG location
$LogFile = “C:\Users\verkinderenal\Downloads\log.csv”
#define host group in vmm and hyperv clustername
$VMHostGroup = “All Hosts”
$VMHostCluster = “srvcluster1”
$VMHost = $null
#define max simultaneous Storage migrations
$maxjobs = 10
$VMHosts = (Get-SCVMHostCluster -Name $VMHostCluster).Nodes
#get all running vms, we don’t want any vm’s that are still being replicated
$VMs = $VMHosts | Get-SCVirtualMachine | where {$_.Status -eq ‘Running’}

function Move-SCVirtualMachineStorage
[boolean]$HighAvailable = $true,

Generate a GUID for the JobGroupID variable.

$JobGroupID = [Guid]::NewGuid().ToString()
$VMHost = Get-SCVMHost -ComputerName $VMHost
if ($VMName -ne $null){
$VMs = @()
foreach ($VMNamed in $VMName){
$VMs += Get-SCVirtualMachine -Name $VMNamed
$VMs = $VM
foreach($VM in $VMs){

#remove replica for the VM on the new host
$hostname = $VM.HostName
$machine = $vm.Name
$script = {param($hostname,$machine); Remove-VMReplication -VMName $machine -ComputerName $hostname -ErrorAction SilentlyContinue}
Invoke-Command -ComputerName $VM.HostName -ScriptBlock $script -ArgumentList $hostname,$machine
#end scriptblock
} #endif removereplica
Start-sleep -Seconds 60
Move-SCVirtualMachine -VM $VM -VMHost $VM.HostName -UseDiffDiskOptimization -HighlyAvailable $HighAvailable -Path $Path -JobGroup $JobGroupID -RunAsynchronously
} #end foreach vm
foreach ($VM in $VMs) {

#run through all the vms but only continue if we didnt reach the max concurent jobs
While((Get-SCJob | where { $_.Status -eq “Running” }).Count -ge $maxjobs)
Add-Content $LogFile “waiting”
Start-Sleep -seconds 60
} #end while

$VHDs = $VM | Get-SCVirtualDiskDrive
foreach ($VHDconf in $VHDs){
if ($VHDconf.VirtualHardDisk.HostVolume -match $lunmig ){
if($VM.OperatingSystem -LIKE “Unknown”){
Add-Content $LogFile “Migrating $VM.Name not possible as OS is Unknown”
Elseif($VM.OperatingSystem -like “Windows 7”){
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lunclients “
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lunclients -RemoveReplica
Elseif($VM.OperatingSystem -like “_Windows Server 2012
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lun2012”
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lun2012 -RemoveReplica
Elseif($VM.OperatingSystem -like “Windows Server 2008”){
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lun2008”
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lun2008 -RemoveReplica
Elseif($VM.OperatingSystem -like “Windows 8”){
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lunclients “
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lunclients -RemoveReplica
Elseif($VM.OperatingSystem -like “Windows XP”){
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lunclients “
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lunclients -RemoveReplica
Elseif($VM.OperatingSystem -like “Windows Vista”){
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lunclients “
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lunclients -RemoveReplica
Elseif($VM.OperatingSystem -like “_Windows Server 2003*”){
Add-Content $LogFile “Migrating $VM.Name from $VHDconf.VirtualHardDisk.HostVolume.Name to $lun2003”
Move-SCvirtualMachineStorage -VMName $VM.Name -VMHost $vm.HostName -Path $lun2003 -RemoveReplica
}#endif lunmig
else {
Add-Content $LogFile “No need to migrate $VM.Name”

} #end foreach



Launch the script on your VMM server and that’s it!


This is what I call:  
nailed it 

You can download the script here https://gallery.technet.microsoft.com/VMM-bulk-storage-migration-ad2184c0 

Hope this helps,

Alexandre Verkinderen

Leave a comment