I am always looking for tools to allow me to do more by doing less. Administering Hyper-V with PowerShell can be tedious. James O’Neill’s PowerShell Management Library for Hyper-V is a great tool to improve automation of Hyper-V management using PowerShell.
As a tester, I am constantly creating new environments, installing our product, taking snapshots, ect. Doing this by hand is a waste of time in my opinion. Doing this with a script makes life easier and frees up my time.
Typically, I prefer to create is to create one VM and to have other VMs inherit from that base image. My second VM (the first child to inherit from the base), I will install daily builds of our product. I will then create a third child, which I use a sandbox. I can easily delete it and recreate as needed without having to install our product over again. I have not consistently found success with using snapshots, so I prefer to use differencing disks. The examples below use differencing disks.
This first code block shows how I am using the PowerShell management library to simply the creation of a base VM. There is nothing magic about it. It’s pretty strait-forward. I create a VM, give it a name, set the CPU count, memory size, network adapters, hard drive, and DVD drive.
$server = "my-dev-server"
$vmName = "TestParentVM"
$vmVirtualSwitch = "My Virtual Network"
# create a new virtual machine
$vm = New-VM $vmName -server $server
# set cpu count
Set-VMCPUCount $vm 2 -server $server
# set memory size
Set-VMMemory $vm 2 -memory 4GB -server $server
# add a legacy network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch -legacy
# add a default VMBus (non-legacy) network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch
# add the hard drive to the VM
Add-VMNewHardDisk –vm $vm -controllerID 0 -lun 0 -vhdpath "$(get-VHDdefaultPath)\$vmName.vhd" -size 20GB
# add the DVD with bootup ISO
Add-VMDRIVE –vm $vm 1 1 -server $server -DVD
Write-Host "Operation complete."
The next code block will create a differencing disk pointing to the parent created in the previous example. I create the usual, VM, CPU count, ect. What makes this a differencing disk is how the VHD is created by specifying the parent. I do some cleanup in this routine by deleting any previously created VMs or VHDs, and I loop through the creation a failure preventative.
$server = "my-dev-server"
$vmName = "TestChildVM"
$vmParent = "TestParentVM"
$vmVirtualSwitch = "My Virtual Network"
#region functions - feel free to move to a separate file
Function DeleteVhd
{
Param ($pathToVhd)
# delete the disk that was added if it exists
if (test-path -path $pathToVhd)
{
Remove-Item -Path $pathToVhd -Force
}
}
#endregion
# delete the VHD if it already exists
DeleteVhd "$(get-VHDdefaultPath)\$vmName.vhd"
# delete the VM if it already exists
Remove-VM -vm $vmName -server $server
# create a new virtual machine
$vm = New-VM $vmName -server $server
# get the vm
$vm = Get-VM $vmName
# set cpu count
Set-VMCPUCount $vm 2 -server $server
# set memory size
Set-VMMemory $vm 2 -memory 4GB -server $server
# get VM Nics available
$vmNics = Get-VMNic -server $server -vmbus -legacy
$vmNicSwitchList = New-Object System.Collections.ArrayList
# get VM Nic Switch available for each VM Nic and add it to our list
foreach ($vmNic in $vmNics)
{
$vmSwitchElementName = (Get-VMNicSwitch $vmNic).ElementName
if ($vmNicSwitchList.Contains($vmSwitchElementName) -ne $true)
{
$vmNicSwitchList.Add($vmSwitchElementName)
}
}
# Use the first available VM Nic Switch - this assumes
# I do not create more than on per each environment.
if ($vmNicSwitchList.Count -gt 0)
{
$vmVirtualSwitch = $vmNicSwitchList[0]
}
# add a legacy network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch -legacy
# add a default VMBus (non-legacy) network adapter
Add-VMNIC -vm $vm -VirtualSwitch $vmVirtualSwitch
# add the hard drive to the VM
$parent = "$(get-VHDdefaultPath)\$vmParent.vhd"
$vhdPath = "$(get-VHDdefaultPath)\$vmName.vhd"
Add-VMNewHardDisk –vm $vm -controllerID 0 -lun 0 -vhdpath $vhdPath -parent $parent
# get the VM to see if a disk is attached to it, if it is not, then it failed
$disks = Get-VMDisk -vm $vm
$count = 1
# if the disk was not successfully added, try and add it again
while ($disks -eq $null -and $count -le 5)
{
Write-Host "RETRY $count - The disk was not properly added. Attempting to retry."
# delete the disk that was added
DeleteVhd "$(get-VHDdefaultPath)\$vmName.vhd"
Add-VMNewHardDisk –vm $vm -controllerID 0 -lun 0 -vhdpath $vhdPath -parent $parent
$disks = Get-VMDisk -vm $vm
$count++
}
if ($disks -eq $null)
{
Write-Host "Operation failed to create this virtual machine. Contact an admin."
}
else
{
Write-Host "Operation completed successfully. Attempting to start the VM."
Start-VM -vm $vm -wait
}
I have found that the VM creation is not consistently successful on the first pass, so I have added the while loop. Since adding that, I have not had any issues.
I hope these help. They should be easy to change if you prefer snapshots versus differencing disks. Thanks to James O’Neill for creating this library. It makes administering Hyper-V much easier. It is available via CodePlex, where the latest release, updates, and forum support can be found.