Performing Storage Migrations With Move-VMStorage

With all of the new PowerShell cmdlet’s in available for Hyper-V in Window’s Server 2012 the one I personally had the hardest time with was Move-VMStorage.  The Move-VMStorage cmdlet is used to perform storage migrations for Hyper-V virtual machines i.e. moving a virtual hard disk from one drive to another or from one SMB share to another.  The tricky part about this cmdlet comes in when you want to move only a part of the VM’s storage (so only the VHDs or only a single VHD) – in that case you need to provide an array of hastables?  What goes in that hashtable?  How do you create it?  Here’s how.



There are effectively two way’s to call Move-VMStorage each with two minor variations (a VM object vs. a VM name).  The first one is pretty straight forward – you call Move-VMStorage with either a VM object or name and provide a Destination Storage Path, this will move all on the VM’s virtual hard disks, it’s configuration, any snapshots etc… You can see an example of that below.  The second method allows you to specify only parts of the VM to move – i.e. a specific virtual hard disk, in many cases this is very helpful if for example you want to balance IO load or increase redundancy or if your VM’s are configured with different storage paths upfront and you want to move one of those VHD’s.

PS C:\> Get-Help Move-VMStorage

    Move-VMStorage [-VMName] <string> [-DestinationStoragePath] <string> [-ComputerName <string[]>]
    [-ResourcePoolName <string>] [-RetainVhdCopiesOnSource] [-AllowUnverifiedPaths] [-AsJob]
    [-WhatIf] [-Confirm]  [<CommonParameters>]
    Move-VMStorage [-VMName] <string> [-ComputerName <string[]>] [-VirtualMachinePath <string>]
    [-SnapshotFilePath <string>] [-SmartPagingFilePath <string>] [-Vhds <hashtable[]>]
    [-ResourcePoolName <string>] [-RetainVhdCopiesOnSource] [-AllowUnverifiedPaths] [-AsJob]
    [-WhatIf] [-Confirm]  [<CommonParameters>]

    Move-VMStorage [-VM] <VirtualMachine> [-VirtualMachinePath <string>] [-SnapshotFilePath
    <string>] [-SmartPagingFilePath <string>]
[-Vhds <hashtable[]>] [-ResourcePoolName <string>]
    [-RetainVhdCopiesOnSource] [-AllowUnverifiedPaths] [-AsJob] [-WhatIf] [-Confirm] 

    Move-VMStorage [-VM] <VirtualMachine> [-DestinationStoragePath] <string> [-ResourcePoolName
    <string>] [-RetainVhdCopiesOnSource] [-AllowUnverifiedPaths] [-AsJob] [-WhatIf] [-Confirm] 

The Basic Storage Migration

A basic migration operation where you want to migrate all of the VM’s storage is pretty straight forward – let’s look at an example with a VM that has three VHDs all located in the same folder as it’s configuration and the migrate them to another location.

Storage Configuration Prior Migration

PS C:\> (Get-VM Demo).ConfigurationLocation    

PS C:\> Get-VMHardDiskDrive -VMName Demo    

VMName ControllerType ControllerNumber ControllerLocation DiskNumber Path
—— ————– —————- —————— ———- —-
Demo IDE 0 0   V:\Share\VMs\Demo\OS.vhdx
Demo SCSI 0 0   v:\Share\VMs\Demo\SCSIDisk1.vhdx
Demo SCSI 0 1   v:\Share\VMs\Demo\SCSIDisk2.vhdx

Migrating The VM’s Storage

PS C:\> Move-VMStorage -VMName Demo -DestinationStoragePath T:\Share2\VMs\Demo

Storage Configuration Post Migration

PS C:\> (Get-VM Demo).ConfigurationLocation

PS C:\> Get-VMHardDiskDrive -VMName Demo    

VMName ControllerType ControllerNumber ControllerLocation DiskNumber Path
—— ————– —————- —————— ———- —-
Demo IDE 0 0   T:\Share2\VMs\Demo\OS.vhdx
Demo SCSI 0 0   T:\Share2\VMs\Demo\SCSIDisk1.vhdx
Demo SCSI 0 1   T:\Share2\VMs\Demo\SCSIDisk2.vhdx

Migrating a Single Virtual Hard Disk

Migrating a single virtual hard disk is a slightly more involved operation – once you see how it’s done the process is pretty simple.  We’ll use the same VM as before but in this case we will only migrate the virtual hard disk attached to the SCSI controller at location 1.  The first operation thing we need to do is retrieve the path of the virtual hard disk as it is today and we will use the Get-VMHardDiskDrive cmdlet for this.  Then we need to create a hash table with two key value pairs, the first pair has a key name of “SourceFilePath” with a value of the location of the virtual disk, the second pair has a key name of “DestinationFilePath” and a value of the location where the virtual hard disk will be migrated to.  We then take that hashtable and put it into an array that is then passed to Move-VMStorage. 

Here is the complete example:

$vhdToMove = Get-VMHardDiskDrive -VMName Demo -ControllerLocation 1
$vhdMoveMap = New-Object -TypeName System.Collections.Hashtable
$vhdMoveMap.Add("SourceFilePath", $vhdToMove.Path)
$vhdMoveMap.Add("DestinationFilePath", "T:\Share2\VMs\Demo\SCSIDisk2.vhdx")
$vhds = @($vhdMoveMap)
Move-VMStorage -VMName Demo -Vhds $vhds

Migrating Multiple Virtual Hard Disks

In similar fashion to the first single virtual hard disk migration we can move multiple disks at the same time by just adding more hashtable’s to the array.

For example if we wanted to move two virtual hard disks:

$vhdToMove = Get-VMHardDiskDrive -VMName Demo -ControllerLocation 0 -ControllerType SCSI
$vhdMoveMap = New-Object -TypeName System.Collections.Hashtable
$vhdMoveMap.Add("SourceFilePath", $vhdToMove.Path)
$vhdMoveMap.Add("DestinationFilePath", "T:\Share2\VMs\Demo\SCSIDisk1.vhdx")
$vhds = @($vhdMoveMap)

$vhdToMove = Get-VMHardDiskDrive -VMName Demo -ControllerLocation 1 -ControllerType SCSI
$vhdMoveMap = New-Object -TypeName System.Collections.Hashtable
$vhdMoveMap.Add("SourceFilePath", $vhdToMove.Path)
$vhdMoveMap.Add("DestinationFilePath", "S:\Share3\VMs\Demo\SCSIDisk2.vhdx")
$vhds += @($vhdMoveMap)

Move-VMStorage -VMName Demo -Vhds $vhds

Or if we wanted to move all virtual hard disks for any VM starting with “D”:

Get-VM -Name "D*" | % {
$vhds = $null
    foreach ($VHD in (Get-VMHardDiskDrive -VM $_))
        $vhdMap = New-Object -TypeName System.Collections.Hashtable
        $vhdMap.Add("SourceFilePath", $VHD.Path)
        $vhdMap.Add("DestinationFilePath", ($VHD.Path.Replace("V:\Share\VMs\", "T:\Share2\VMs\")))
        $vhds += @($vhdMap)
    Move-VMStorage -VM $_ -Vhds $vhds


I hope this helps better explain the Move-VMStorage cmdlet…  Happy migrating.


Comments (2)

  1. SCBuzz says:

    I found this blog after the fact, but yes, this required some serious headscratching to figure it out.

    But I also ran into another problem and I wonder if you saw it? One of the things I needed to do in addition to migrating the storage was to leave the Source untouched. So I specified the "-RetainVhdCopiesOnSource"  option.  Funny thing is that it deleted the first VHD that was migrated, but not the 2nd one.

  2. Yusuf Ozturk says:

    Thats correct. -RetainVhdCopiesOnSource is not working as it explained. That keeps only source disks like Snapshot parent files etc. If you snapshot the VM first and move it via Move-VMStorage, that will keep main VHD file of disk.