Find all virtual hard disks associated with a virtual machine [script]

This is something that sounds really simple to start with – but gets more complicated the more you think about it. 

The question is: how do you identify all virtual hard disks that are associated with a given virtual machine?

Your first response might be “you open the virtual machine settings and have a look there!”  But what if your virtual machine uses differencing disks?  How do you identify all of the parent disks involved?  How about if it has snapshots?  What if those snapshots use a different set of virtual hard disks than the currently active virtual machine (this is a valid – though odd – configuration)?

To deal with all of these permutations and issues – I came up with the following PowerShell script:

 # Prompt for the Hyper-V Server to use
 $HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
  
 # Prompt for the virtual machine to use
 $VMName = Read-Host "Specify the name of the virtual machine"
  
 # Get Storage management service
 $ImgService = gwmi Msvm_ImageManagementService -namespace "root\virtualization" -computername $HyperVServer -locale MS_409
  
 # Get the virtual machine object
 $VM = gwmi MSVM_ComputerSystem -namespace "root\virtualization" -computername $HyperVServer -locale MS_409 | where {$_.ElementName -eq $VMName}
  
 # Create an empty hashtable
 $table = @{}
  
 # Go over each of the virtual machines "system setting data" objects.  There will be a system setting data
 # for each snapshot associated with the virtual machine, and one for the active virtual machine.
 foreach ($VSSD in $VM.getRelated("Msvm_VirtualSystemSettingData")) 
   {
    # Get all the VHDs associated with the current system setting data
    $VHDs = [array]($VSSD.getRelated("Msvm_ResourceAllocationSettingData") | where {$_.ResourceType -eq 21} | where {$_.ResourceSubType -eq "Microsoft Virtual Hard Disk"})
  
    # Only continue if the system setting data actually had virtual hard disks
    if ($VHDs)
       {
       # Intialize index, and get the VHD and VHDPath for the first virtual hard disk      
       $index = 0
       $VHD = $VHDs[$index]
       $VHDPath = $VHD.Connection | select -first 1
  
       # loop through all virtual hard disks
       do      
          {
          # Get the detailed information for the current virtual hard disk
          $xml = [xml]($ImgService.GetVirtualHardDiskInfo($VHDPath)).info
       
          # Only continue if the current virtual hard disk is not in the hashtable
          if (!$table.ContainsKey($VHDPath)) 
             {
             
             # Add the virtual hard disk to the hashtable - including the type of the virtual hard disk
             switch ([uint64]($xml.Instance.Property | ?{$_.Name -eq "Type"}).value)
                {
                   2 {$table[$VHDPath] = "Fixed"}
                   3 {$table[$VHDPath] = "Dynamic"}
                   4 {$table[$VHDPath] = "Differencing"}
                }
             
             # Check to see if the current virtual hard disk has a parent virtual hard disk
             if (($xml.Instance.Property | ?{$_.Name -eq "ParentPath"}).value)
                {
                # if it does - make the parent the next virtual hard disk that we look at
                $VHDPath = ($xml.Instance.Property | ?{$_.Name -eq "ParentPath"}).value
                }
             # If the current virtual hard disk does not have a parent - just move onto the next virtual
             # hard disk in the system settings data object
             else
                {
                # Increase the index - and make sure that we have not gone past the last virtual hard disk
                $index = $index + 1
                if ($index -ne $VHDs.count)
                   {
                   # Setup new values for next time through
                   $VHD = $VHDs[$index]
                   $VHDPath = $VHD.Connection | select -first 1}
                   }
          
                }
  
          # If the current virtual hard disk is already in the hashtable - move onto the next virtual
          # hard disk in the system settings data object         
          else
             {
             # Increase the index - and make sure that we have not gone past the last virtual hard disk
             $index = $index + 1
             if ($index -ne $VHDs.count)
                {
                # Setup new values for next time through
                $VHD = $VHDs[$index]
                $VHDPath = $VHD.Connection | select -first 1
                }
             }
          }
       
       # loop until we have covered all virtual hard disks
       until ($index -eq $VHDs.count)
       }
    }
  
 # Display the results in a nicely formated and sorted manner
 $table.GetEnumerator() | Sort-Object Name | Format-Table -Autosize

This script will find every virtual hard disk in every snapshot associated with a virtual machine – as well as tracking down any parent disks of differencing disks.  The neat trick in this script is that it uses a hash table so that it does not look at the same virtual hard disk twice (because the script goes through all snapshots of a virtual machine – you can end up looking at the same virtual hard disk over and over again if you do not do this).

At the end of the script it provides a list of all virtual hard disks that are associated with the virtual machine.

Cheers,

Ben

FindVHDs.zip