Save to My DOJO
We’ve had quite a few posts about Hyper-V checkpoints lately (formerly snapshots). We also spend a fair bit of time warning people not to tinker with them manually. There are still those people that are going to tinker despite any warnings, and there will always be those people who don’t even find the warnings until they’re too late to be of any value. Worse, there will always be those unfortunate few that do everything right and still find themselves in a mess. The least I can do is provide a tool that can be of use to anyone that’s stuck working on a complicated tree of differencing disks.
As a refresher, a differencing virtual hard disk contains all the changes that would have been made to a fixed or dynamically expanding virtual hard disk. These disks have a number of uses. The most common is with checkpoints, which automatically create differencing disks (and prepend an A to the extension so that it becomes an AVHD or an AVHDX). They are also used by Remote Desktop Services (RDS) to deploy an individual machine in a pool from a single parent. We can create them manually with New-VHD to attach one or more dependent child virtual machines to a single master.
Once a differencing disk has been created, any virtual machine that is assigned to that differencing disk will only write changes into the differencing disk. When it reads from the differencing disk, any blocks that it does not contain are retrieved from the parent. It is possible for a differencing disk to be a parent of a differencing disk, but they must eventually trace back to a root fixed or dynamically expanding disk.
To put it mildly, problems arise when changes are made to a VHD that is the parent of a differencing disk. So that there is no misunderstanding, these problems are catastrophic to the differencing disk. When a parent-child relationship exists between a virtual disk and a differencing virtual disk, all changes must occur in the child. If any change, however minor, is made to the parent, the data in the child is invalidated. The parent is still fully usable.
So, if you’re going to tinker with a disk in a differencing relationship, you must have a clear idea of what that relationship is. Unfortunately, there’s really no way to look at a VHD and determine if it has children. It’s easiest with checkpoints, because Hyper-V will always create the AVHD/X files in the same folder as the parent. Even if you can’t visually confirm which is the newest or oldest, at least you know that none have wandered off into a neighboring pasture. For RDS, you should only be using the provided modification tools because they have their own methods of protecting the differencing children. If you’re out creating differencing disks on your own, hopefully you paid attention to where you placed them.
How to see all disks upward of a VHD file
While I can’t be of much help in determining what the newest disk in a differencing chain is, I can show you a very quick way to see all the disks in a chain upward of a VHD file that you provide. Just use the following script:
function Get-VHDDifferencingChain { <# .SYNOPSIS Finds all the parents of a Hyper-V virtual hard disk (VHD or VHDX). .DESCRIPTION Finds all the parents of a Hyper-V virtual hard disk (VHD or VHDX). Files are retrieved from newest (immediate parent of the submitted VHD/X) to oldest (root VHD/X in the chain). .EXAMPLE Get-VHDDifferencingChain -Path 'C:ClusterStorageVirtual Hard Disksvmone-child4.vhdx' Shows all of the parents of differencing disk vmone-child4.vhdx back to the root. .NOTES v1.0 Author: Eric Siron Authored date: December 13, 2015 Copyright 2015 Altaro Software #> #requires -Modules Hyper-V param([Parameter(Mandatory=$true)][String]$Path) while($Path = (Get-VHD -Path $Path).ParentPath) { $Path } }
The above script is one of those fun ones where the introductory comments dramatically outnumber the functional parts of the script. The only problem is that it runs the risk of being a bit too clever. I like my scripts to be easy to read. If this were any longer, or was part of a bigger script, I wouldn’t do it this way. This is because one line of script does three things, and some of the functionality is obscured. Look specifically at this line:
while($Path = (Get-VHD -Path $Path).ParentPath)
The first thing this line does is run Get-VHD against $Path.
The second thing that it does is assign one property of the output of Get-VHD to $Path. There are two confusion points in that piece alone. First, remember that in PowerShell, the = sign is the assignment operator; -eq is the equality operator. We are not checking if $Path is equal to its own ParentPath. The second confusion point is using $Path as a parameter to a cmdlet whose output is being assigned back into $Path. It works because as soon as Get-VHD completes, PowerShell moves on to evaluating the value of its ParenPath, then performs the assignment. The original value of $Path does not collide in PowerShell because it stops caring about the value of $Path before its even done with Get-VHD. It’s not a problem for us to reuse $Path because we don’t need its original value anymore.
The third thing that this line does is verify the outcome of that assignment for the while. while is a deceptively simple language construct that expands to: if the condition is not zero or empty, loop until the condition is zero or empty. Pay attention to the “condition is zero or empty” part, as this is not intuitive for newcomers. It would be logical to expect it to expand to: if the condition is true, loop until the condition is not true, because that is how humans use if in natural language. The only computer language that I am familiar with that implements If in the natural language fashion is Visual Basic. If this same line of script were converted to VB, it would refuse to compile because $Path = (Get-VHD -Path $Path).ParentPath cannot be evaluated as a simple true/false condition. Because PowerShell will continue operating the while loop as long as the condition inside parentheses produces something, it will loop until it encounters a VHD that doesn’t have a parent.
There is only one remaining line of script, and that is the $Path item on a line all by itself inside the while loop. This cause the value of $Path to be placed into the pipeline. If you run this script as-is, it will just emit the text to the screen, line by line. If you pipe into something that accepts a String object or an array of Strings, it will handle them appropriately.
Most of the programming and scripting best practices recommendations that I’ve seen will tell you to avoid writing code that doesn’t follow natural language conventions because of the potential confusion. That means that a friendlier script would look like this:
param([Parameter(Mandatory=$true)][String]$Path) $ParentPath = (Get-VHD -Path $Path -ErrorAction Stop).ParentPath while(-not [String]::IsNullOrEmpty($ParentPath)) { $ParentPath $ParentPath = (Get-VHD -Path $Path).ParentPath }
While friendlier, it duplicates script and, in my opinion, has other issues that make it harder to read than the method that I chose. Of course, there are other ways to script this to wind up with the same output, but this is one of those few cases where increased readability introduces increased complexity.
Bonus: Re-using a Get-VHD Script
I wrote a script that looks for orphaned virtual machine files a while back, and that same post I included a script that could use only built-in PowerShell components to check a VHD/X file for parents. The purpose of doing so is that you need the Hyper-V PowerShell module loaded in order to use Get-VHD, even though virtual disk files can be used on systems without Hyper-V, such as Windows 7. If you wanted to use that script with this one, then making two simple changes to produce the following script will do the trick (assuming that you loaded Get-VHDDifferencingParent as a dot-sourced function):
function Get-VHDDifferencingChain { <# .SYNOPSIS Finds all the parents of a Hyper-V virtual hard disk (VHD or VHDX). .DESCRIPTION Finds all the parents of a Hyper-V virtual hard disk (VHD or VHDX). Files are retrieved from newest (immediate parent of the submitted VHD/X) to oldest (root VHD/X in the chain). .EXAMPLE Get-VHDDifferencingChain -Path 'C:ClusterStorageVirtual Hard Disksvmone-child4.vhdx' Shows all of the parents of differencing disk vmone-child4.vhdx back to the root. .NOTES v1.0 -- Alternate Edition Author: Eric Siron Authored date: December 13, 2015 Copyright 2015 Altaro Software #> param([Parameter(Mandatory=$true)][String]$Path) while($Path = (Get-VHDDifferencingParent -Path $Path).ParentPath) { $Path } }
The referenced script wasn’t included in the PowerShell and Hyper-V series because it’s far more complex than what I had in mind for this teaching series. You shouldn’t let that stop you from tearing into it to see what you can learn (or if you can find something I did that could be fixed).
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!
8 thoughts on "PowerShell & Hyper-V: Finding the Parents of a Differencing VHD"
Cool article! Thank you, Eric!
Overall, excellent articles and very interesting and practical ebooks.
Thanks very much !!! (All writers )
Overall, excellent articles and very interesting and practical ebooks.
Thanks very much !!! (All writers )
You may miss a parameter in this line:
$ParentPath = (Get-VHD -Path -ErrorAction Stop).ParentPath
It should be like this
$ParentPath = (Get-VHD -Path $Path -ErrorAction Stop).ParentPath
Maybe a browser display problem? That’s exactly what I see on my screen.
Oh nevermind, I found the lines that you referenced. Fixed now. Thanks!