Getting to grips with Hyper-V's API

It's only 7 months since I first installed Powershell. Hard to believe that last week I saw a copy of the OCS res kit with my name on the cover as a result of the PowerShell scripts I wrote, and worth remembering before criticizing other people's scripts.

Over on Ben Armstrong's blog he has applied his virtualization expertise to showing how to use the newly published WMI management interface, posting samples using VB and "VB in powershell syntax" for example

 $VMs = gwmi -class "MSVM_ComputerSystem" -namespace "root\virtualization" -computername "."
foreach ($VM in $VMs){
   if ($VM.Caption -match "Microsoft Virtual Computer System"){
      write-host "=================================="
      write-host "VM Name:  " $VM.ElementName
      write-host "VM GUID:  " $VM.Name
      write-host "VM State: " $VM.EnabledState
   }
}

Ben says he finds it easier to read that way, but I thought I'd show how we could do it in Powershell style. An If inside a for loop ? filter before looping 

 VMs = (get-wmiobject -class "MSVM_ComputerSystem" -namespace "root\virtualization") | 
             where {$_.caption -match "Virtual"}

Or better, use the WMI Query Syntax to only return the objects you want

 $VMs = get-wmiobject -namespace "root\virtualization" -query `
"SELECT * FROM Msvm_ComputerSystem WHERE Caption Like '*virtual*' "

The bit of Ben's code which says he's not been immersed in PowerShell is the for loop with write statements inside. It shouts "FORMAT-Table"

 $VMs | Format-Table -autosize ElementName, Name, Enabledstate

If you prefer a list format you can always use Format-list, and if you want more helpful heading names you can use  @{Label="VM GUID"; expression={$_.Name}} as a field

 $VMs | Format-Table -autosize @{Label="VM Name"; expression={$_.ElementName}} ,
@{Label="VM GUID"; expression={$_.Name}} , Enabledstate

In fact you could go step futher and expand the value in enabledState to text

 @{Label="State"; expression={switch ($_.EnabledState) { 2 {"Running"} 3 {"Stopped"}
   32768 {"Paused"} 32769 {"Suspended"} 32770 {"Starting"} 
   32771 {"Snapshotting"} 32773 {"Saving"} 32774 {"Stopping"} } }}

Of course I'll often take the approach "Variables! We don't need no stinking variables" and pipe the Get-WMIObject into Format-Table. But it's useful sometimes - like in choose functions I tend to write for many objects ...

 Function Choose-VM
{$global:Counter=-1
 $VMs = get-wmiobject -namespace "root\virtualization" -query `
          "SELECT * FROM Msvm_ComputerSystem WHERE Caption Like '%virtual%' "
 Format-Table -inputobject $VMS @{ Label = "ID"; Expression={($global:counter++) }} ,
                                @{Label="VM Name"; expression={$_.ElementName}} ,
                                @{Label="VM GUID"; expression={$_.Name}} ,
                                @{Label="State"; expression={switch ($_.EnabledState) { 2 {"Running"} 3 {"Stopped"} 
                                                  32768 {"Paused"} 32769 {"Suspended"} 32770 {"Starting"} 
                                                  32771 {"Snapshotting"} 32773 {"Saving"} 32774 {"Stopping"} } }} | out-host 
 $VMs[ [int[]](Read-Host "Which ones? ").Split(",")]}

Ben did a second post to show starting a virtual machine  - to do this I can just use

 (choose-vm ) | foreach-object {$_.requestStateChange(2)} 

You could use anything which returns Msvm_ComputerSystem WMI objects and you can stop or pause one a machine just by requesting a different state to change to.  Shutting down a machine just by setting it's state to 3 for stopped is the virtual equivalent of hitting the power switch. The hyper-v integration components include one to trigger a clean shutdown and Ben shows how that can be used; here's my version

 (choose-vm ) | foreach-object { (get-wmiobject -namespace "root\virtualization" -query `
               "SELECT * FROM Msvm_ShutdownComponent WHERE SystemName='$($_.name)' ").InitiateShutdown($true,"Because I said so") } 

 

Technorati tags: Microsoft, Virtualization, hyper-V, powershell, WMI