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.

 

Overview

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

NAME
    Move-VMStorage
   
SYNTAX
    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] 
    [<CommonParameters>]

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

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    
V:\Share\VMs\Demo

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
T:\Share2\VMs\Demo

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.

-taylorb