
Last night I had to troubleshoot a low free space alert on a cluster. While everything looked normal and no Checkpoints or backups of Virtual Machines were on the Clustered Shared Volume a problem with the backup software flew under the radar and created a massive problem! There was an offending VM that was backed up normally and no checkpoints existed but it’s VHD files were differencing disks. A situation most of the IT Pro’s out there are familiar with.
So how do you rectify this? This VM had grown to a TB of data and while no checkpoints existed it had over 300 differencing VHD files. Restoring such a VM would take hours so that’s not a great option. Well I first started with copying over the XML configuration files of the virtual machine and importing this VM again with NO-VHD drives on it. So far so good we have a working VM with the same GUID and the same network adapters but with no drives.
{Powershell} to the rescue!
Powershell is capable of doing amazing things so I wrote this little script that actually gets each drive and then merges all of the VHD files into the parent so that you can attach it again on the virtual machine. Enjoy!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#Get the virtual Machine $Name=read-host "Please enter VM Name" #Get all of the Virtual Machine Hard Drives $Disks=get-vm -name $name |Get-VMHardDiskDrive #Start enumerating through each foreach($disk in $disks){ #OKay, Hard part 1 is finding the full path to the disk , Split-Path to the rescue! $DiskFullPath=Split-Path -Path $disk.Path #Hard part 2 is finding the name , we will use split path again but with a -Leaf switch to get the file name of the VHDX $DiskName=Split-Path -Path $disk.Path -Leaf #Super hard part #3 is finding the right order. For this part we select all of the objects in the folder (aka checkpoint avhd or avhdx files) and we trim the extension + guid out of it which is 44 letters. Then #we simply select the first one based on the last write time. $VHDs=Get-ChildItem -Path $DiskFullPath |Where-Object{$_.Name -match $DiskName.Substring(0,($DiskName.Length -44))} |Sort-Object lastwritetime -Descending |select FullName -ExpandProperty fullname #Some counters are never bad! $i=-1 $number=-1 #Okay start working with these! do{ #First increment the counters, we started with -1 as most Powershell stuff starts on Zero (0) $number=$number+1 $i++ #Go through each VHD in the array and figure out the path and for each its parent path, if you need to merge remove /select Path,ParentPath/ and replace with merge-vhd -force $vhds[$number]| Get-VHD |select Path,ParentPath #Keep doing it until the VHD count reaches the last item in the array minus one as getting the first (Parent) vhd makes no sense }until($i -ge ($vhds.Count-1)) #That's all folks! } |