Scripts to Inventory Virtual Machines [Hyper-V]

Someone asked me if it was possible to make a script that would tell you what guest operating systems you had running on a Hyper-V server.  To answer them I whipped up two sample PowerShell scripts.

The first one will generate a table of detected guest operating systems – and a count for each type – as well as showing you how many offline virtual machines exist on the computer:

# Prompt for the Hyper-V Server to use
$HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
 
# Get all virtual machine objects on the server in question
$VMs = gwmi -namespace root\virtualization Msvm_ComputerSystem -computername $HyperVServer -filter "Caption = 'Virtual Machine'" 
 
# Create an empty hashtable
$table = @{}
 
# Go over each of the virtual machines
foreach ($VM in [array] $VMs) 
  {
   # Get the KVP Object
   $query = "Associators of {$VM} Where AssocClass=Msvm_SystemDevice ResultClass=Msvm_KvpExchangeComponent"
   $Kvp = gwmi -namespace root\virtualization -query $query -computername $HyperVServer
 
   # Get the OSName value out of the guest KVP data
   $xml = [xml]($Kvp.GuestIntrinsicExchangeItems | ? {$_ -match "OSName"})
   $entry = $xml.Instance.Property | ?{$_.Name -eq "Data"}
 
   # Filter out offline virtual machines and virtual machines which did not return KVP data
   if ($entry.Value) 
      {$value = $entry.Value}
   elseif ($VM.EnabledState -ne 2)
      {$value = "Offline"}
   else {$value = "Unknown"}
 
   # Count up the values and store it in a hashtable
   if ($table.ContainsKey($value)) 
      {$table[$value] = $table[$value] + 1 }
   else
      {$table[$value] = 1}
   }
 
# Display the results in a nicely formated and sorted manner
$table.GetEnumerator() | Sort-Object Name | Format-Table -Autosize

This script will products an output like this:

image

Then I made a simpler, more “light weight”, script that will return the same information – but does not include offline virtual machines:

# Prompt for the Hyper-V Server to use
$HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
 
# Get all guest KVP data objects on the server in question
$Kvps = gwmi -namespace root\virtualization Msvm_KvpExchangeComponent -computername $HyperVServer
 
# Create an empty hashtable
$table = @{}
 
# Go over each of the guest KVP data objects
foreach ($Kvp in [array] $Kvps) 
  {
   # Get the OSName value out of the guest KVP data
   $xml = [xml]($Kvp.GuestIntrinsicExchangeItems | ? {$_ -match "OSName"})
   $entry = $xml.Instance.Property | ?{$_.Name -eq "Data"}
 
   # Filter out unknown operating systems
   if ($entry.Value) {$value = $entry.Value} else {$value = "Unknown"}
 
   # Count up the values and store it in a hashtable
   if ($table.ContainsKey($value)) 
      {$table[$value] = $table[$value] + 1 }
   else
      {$table[$value] = 1}
   }
 
# Display the results in a nicely formated and sorted manner
$table.GetEnumerator() | Sort-Object Name | Format-Table -Autosize

This script produces an output like this:

image

Both of these scripts use the guest operating system data that is returned through the Hyper-V key-value pair functionality to identify what is present on your system. 

The big difference between the two scripts is that the first one starts by identifying all the virtual machines on the physical computer – and then finding the key-value pair data associated with that virtual machine.  This means that it is able to identify offline virtual machines (which do not return guest key-value pair data).  But it also means that the script will take longer to run on a system with a lot of virtual machines.  Meanwhile, the second script just grabs all the guest key-value pair objects on the system and does not attempt to associate them with virtual machines.  This approach is much quicker – but will not account for offline virtual machines.

Cheers,

Ben