Importing VMs from one host to another can run into issues due differences in the configuration of the hosts. A common issue is that a virtual switch does not exist on the new Hyper-V host. While it is possible to attempt the import, and then deal with the fallout afterwards, wouldn't it be great to know about such issues before actually starting the import?
That is where the Compare-VM cmdlet comes in. It is able to provide a compatibility report which illustrates issues between the source and destination Hyper-V hosts.
The below is probably one of the most frequent import issues which I run into. The required virtual network does not exist on the host used for the import.
Notes on Compare-VM
Compare-VM can compare the contents of the VM’s configuration file and the host where the VM is to be imported.
Note that the format of this file has changed. As discussed in What's new in Hyper-V on Windows Server 2016, note that the file extension .VMCX is now used. Previously .XML was used.
This necessitates knowing the correct file extension for the virtual machines in question. This post is based off Windows 10 and Windows Server 2016, so the new extension is used.
$report = Compare-VM -Path 'D:VM1Virtual Machines53EAE599-4D3B-4923-B173-6AEA29CB7F42.VMCX’
The issues can then be reviewed using:
$report.Incompatibilities | Format-Table –AutoSize
Review Single VM
To review a single VM, the path to the VM can be passed as a pipeline object or directly in the Compare-VM cmdlet. In this case the former was used.
Get-Item -Path '.*.VMCX' | ForEach {(Compare-VM -Path ($_.FullName)).Incompatibilities}
There is an issue locating a virtual switch called “E15-Cloud”. That needs to be created on the host.
Now that we know that, we can create the required switch[s] and then successfully import the VM.
Review Multiple VMs
The above command can be easily modified to search through a directory of VMs. Typically I have a folder which contains all of the VMs for a given lab. There will be a parent differencing disk to minimise the disk space footprint. Each VM has its own sub folder which contains the child differencing disk and other VM specific files. This allows a recursive search from the parent directory to easily identify all of the required VMs. The –Recurse parameter of Get-ChildItem searches for files of type .VMCX and passes them down the pipeline to the Compare-VM cmdlet.
If you wanted to count the number of VMs returned, simply add the Measure-Object to the end. For example:
Get-ChildItem -Recurse '.2013 HA*.vmcx' | Measure-Object
Reviewing Multiple VMs and Creating New Virtual Switches
The below is aimed at the VMs in most of my labs. Each VM has a single network interface, and since I frequently need to move VMs between different hosts it can be tedious having to create all of the different Virtual Switches.
The below will loop through the list of VMs which is obtained by the recursive search for the .VMCX configuration file. It checks to see that there is an incompatibility for a missing switch, which is a variable called $VMSwitch. This is then leveraged in the new-VMSwitch cmdlet to create the private virtual switch. Again, this is sample code which is tailored to my purposes. Feel free to modify so that it meets your goals. It is not intended to be robust and enterprise grade. For example it assumes that all VMs have one virtual network attached, and you are running it with the correct credentials. It also assumes that the network is what caused the incompatibility message. Feel free to buff if and make it more robust.
Copy the following code into a .PS1 file to execute it.
$VMs = Get-ChildItem -Path '.2013 HA*.VMCX' -Recurse ForEach ($VM in $VMs) {$FullMessage = (Compare-VM -Path ($VM.FullName)).Incompatibilities.Message IF($FullMessage) { $VMSwitch = $FullMessage.Split("'")[1] Write-Host "Network $VMSwitch not present" -Foregroundcolor Magenta $Exists = Get-VMSwitch $VMSwitch -ErrorAction SilentlyContinue IF ($Exists) { Write-Host "$VMSwitch Exists"} ELSE {New-VMSwitch -Name $VMswitch -SwitchType Private} } }
Importing Multiple VMs
Now that the required virtual networks have been created, we can import the VMs using something like the below:
Get-ChildItem -Path '.2013 HA*.VMCX' -Recurse | ForEach { Import-VM -path $_.FullName}
Remove All But Default VMSwitch
This was a line used in testing the post, and was included as it may be useful to some folks. It will remove all VMSwitches that are NOT called “Default Switch”.
Get-VMSwitch | Where {$_.Name -NE "Default Switch"} | Remove-VMSwitch -Confirm:$False
Cheers,
Rhoderick