Use PowerShell to Determine the Chain of VHD’s for a Virtual Machine on Hyper-V

You want to determine what VHD’s exist for the virtual machines (VM’s) that are virtualized on your Windows 8 or Windows Server 2012 Hyper-V virtualization host.

This may sound simple, but what if you have a base image or template that has been configured and then multiple VM’s have been created using differencing disks that reference that single base image? If that weren’t difficult enough to keep track of, you may also have snapshots of those VM’s which are also classified as differencing disks in HyperV.

How do you easily figure out what the chain is for all of the VHD’s or VHDX files that are necessary for a virtual machine? I couldn’t find a way to determine this easily so I decided to write a PowerShell function to figure this out.

I recently added a second hard drive (an SSD) to my laptop computer and I want to move some of the virtual hard drive files to this drive.

#Requires -Version 3.0
#Requires -Modules Hyper-V
function Get-VHDChain {

    [CmdletBinding()]
    param(
        [string]$ComputerName = $env:COMPUTERNAME,
        [string[]]$Name = '*'
    )

    try {
        $VMs = Get-VM -ComputerName $ComputerName -Name $Name -ErrorAction Stop
    }
    catch {
        Write-Warning $_.Exception.Message
    }

    foreach ($vm in $VMs){
        $VHDs = ($vm).harddrives.path

        foreach ($vhd in $VHDs){
            Clear-Variable VHDType -ErrorAction SilentlyContinue

            try {
                $VHDInfo = $vhd | Get-VHD -ComputerName $ComputerName -ErrorAction Stop
            }
            catch {
                $VHDType = 'Error'
                $VHDPath = $vhd
                Write-Verbose $_.Exception.Message
            }

            $i = 1
            $problem = $false

            while (($VHDInfo.parentpath -or $i -eq 1) -and (-not($problem))){
                If ($VHDType -ne 'Error' -and $i -gt 1){
                    try {
                        $VHDInfo = $VHDInfo.ParentPath | Get-VHD -ComputerName $ComputerName -ErrorAction Stop
                    }
                    catch {
                        $VHDType = 'Error'
                        $VHDPath = $VHDInfo.parentpath
                        Write-Verbose $_.Exception.Message
                    }
                }

                if ($VHDType -ne 'Error'){
                    $VHDType = $VHDInfo.VhdType
                    $VHDPath = $VHDInfo.path
                }
                else {
                    $problem = $true
                }

                [pscustomobject]@{
                    Name = $vm.name
                    VHDNumber = $i
                    VHDType = $VHDType
                    VHD = $VHDPath
                }

                $i++
            }
        }
    }
}

I’ve already moved a couple of the VHDX files as you can see in the results where the VHDType is “Error”:

vhdchain-1.png

µ