PowerShell Management Library for Hyper-V

March 25th, 2009

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.

Alex Mueller

  • http://www.alvinashcraft.com/2009/03/26/dew-drop-march-26-2009/ Dew Drop – March 26, 2009 | Alvin Ashcraft’s Morning Dew

    [...] PowerShell Management Library for Hyper-V (Alex Mueller) [...]

  • http://profiles.google.com/jakson0900 jakson robert

    For a little while now, I’ve been trying to find MS best practices
    for managing a Hyper-V library of VMs (be they VHD or pass-through
    disks). I’ve only managed to come up with a few minor things.

    Anyone have some credible links with more information on generalized library management best practices? i have gotten many solutions from http://elegantcode.com and another one http://www.hypervhd.com about hyperv
     

  • http://www.vpswebserver.com windows vps

    I have been trying to use a script I found to shutdown and backup my hyperv vms.  I get an error everytime I try and run it.
    The script below is one easily found online, I have tried many
    variations but always seem to get the same error.  Any help is
    appreciated!
    You cannot call a method on a null-valued expression.

    At C:scriptsVmBackuptest2.ps1:30 char:35

    + $result = $vmshut.InitiateShutdown <<<< ("$true","no comment")

        + CategoryInfo          : InvalidOperation: (InitiateShutdown:String) [],

       RuntimeException

        + FullyQualifiedErrorId : InvokeMethodOnNull
    This is line 30 of the script-$result = $vmshut.InitiateShutdown("$true","no comment")
    $waitstart = 200

    $waitshutdown = 120
    if ($args[1] -match "0") {

    $inputfile=get-content $args[0]

    foreach ($guest in $inputfile) {

    write-host "Starting $guest"

    $vm = gwmi -namespace rootvirtualization -query "select * from msvm_computersystem where elementname='$guest'"

    $result = $vm.requeststatechange(2)

    if ($result.returnvalue -match "0") {

    start-sleep -s $waitstart

    write-host ""

    write-host "$guest is started" -foregroundcolor green

    write-host ""

    }

    else {

    write-host ""

    write-host "unable to start $guest" -foregroundcolor red

    write-host ""

    }}}
    if ($args[1] -match "1") {

    $inputfile=get-content $args[0]

    foreach ($guest in $inputfile) {

    write-host "shutting down $guest"

    $vm = gwmi -namespace rootvirtualization -query "select * from msvm_computersystem where elementname='$guest'"

    $vmname = $vm.name

    $vmshut = gwmi -namespace rootvirtualization -query "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$vmname'"

    $result = $vmshut.InitiateShutdown("$true","no comment")

    if ($result.returnvalue -match "0") {

    start-sleep -s $waitshutdown

    write-host ""

    write-host "no error while shutting down $guest"

    write-host "shutdown of $guest completed" -foregroundcolor green

    write-host ""}
    else {

    write-host ""

    write-host "unable to shutdown $guest" -foregroundcolor red

    write-host ""

    }}}
    else {
    write-host "USAGE: to shutdown VMs," -nonewline; write-host ".managehyperV.ps1 c:hosts.txt 1" -foregroundcolor yellow
    write-host "USAGE: to start VMs," -nonewline; write-host ".managehyperV.ps1 c:hosts.txt 0" -foregroundcolor yellow
    }

blog comments powered by Disqus