Add Scripts/Powershell/Hyper-V/Failover Cluster/Collapse-DifferencingDisk-Chains.md
This commit is contained in:
@ -0,0 +1,161 @@
|
||||
## Purpose
|
||||
Sometimes things go awry with backup servers and Hyper-V and a bunch of extra `.avhdx` virtual differencing disks are created, taking up a ton of space. This can be problematic because if you run out of space, the virtual machines running on that underlying storage will stop working. Sometimes this can involve dozens or even hundreds of disk in rare cases that need to be manually merged or "collapsed" down to reclaim the lost space.
|
||||
|
||||
## Powershell Script
|
||||
You need to copy the contents of the following somewhere on your computer and save it as `Get-HyperVParentDisks.ps1`.
|
||||
``` powershell
|
||||
param (
|
||||
[Parameter(Mandatory=$true, HelpMessage="Specify the path to the AVHDX file.")]
|
||||
[string]$AVHDXPath,
|
||||
|
||||
[Parameter(Mandatory=$false, HelpMessage="Specify this flag to merge disks into their parents.")]
|
||||
[switch]$MergeIntoParents,
|
||||
|
||||
[Parameter(Mandatory=$false, HelpMessage="Specify this flag to simulate the merging process without actually performing it.")]
|
||||
[switch]$DryRun
|
||||
)
|
||||
|
||||
function Get-ParentDisk {
|
||||
param (
|
||||
[string]$ChildDisk
|
||||
)
|
||||
$diskInfo = Get-VHD -Path $ChildDisk
|
||||
return $diskInfo.ParentPath
|
||||
}
|
||||
|
||||
function Get-AllParentDisksChain {
|
||||
param ([string]$CurrentDisk)
|
||||
$parentDiskChain = @()
|
||||
while ($CurrentDisk) {
|
||||
$parentDisk = Get-ParentDisk -ChildDisk $CurrentDisk
|
||||
if ($parentDisk) {
|
||||
$parentDiskChain += $CurrentDisk # Add the current disk to the chain before moving to the parent
|
||||
$CurrentDisk = $parentDisk
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
$parentDiskChain += $CurrentDisk # Add the base disk at the end of the chain
|
||||
return $parentDiskChain
|
||||
}
|
||||
|
||||
function Merge-DiskIntoParent {
|
||||
param ([string]$ChildDisk, [string]$ParentDisk, [int]$DiskNumber, [int]$TotalDisks)
|
||||
|
||||
if ($DryRun) {
|
||||
Write-Output "[Differential Disk $DiskNumber of $TotalDisks]"
|
||||
Write-Output "Child: $ChildDisk"
|
||||
Write-Output "Parent: $ParentDisk"
|
||||
Write-Output "[Dry Run] Would merge child into parent"
|
||||
} else {
|
||||
Write-Output "[Differential Disk $DiskNumber of $TotalDisks]"
|
||||
Write-Output "Child: $ChildDisk"
|
||||
Write-Output "Parent: $ParentDisk"
|
||||
try {
|
||||
$childDiskInfo = Get-VHD -Path $ChildDisk
|
||||
if ($childDiskInfo.VhdFormat -ne 'VHDX' -or $childDiskInfo.VhdType -ne 'Differencing') {
|
||||
Write-Output "Error: $ChildDisk is not a valid differencing disk (AVHDX) and cannot be merged."
|
||||
throw "Invalid disk type for merging."
|
||||
}
|
||||
Merge-VHD -Path $ChildDisk -DestinationPath $ParentDisk -Confirm:$false
|
||||
Write-Output "Successfully Merged Child into Parent"
|
||||
} catch {
|
||||
Write-Output "Failed to merge ${ChildDisk} into ${ParentDisk}: $_"
|
||||
Restart-Service -Name vmms
|
||||
throw "Merge failed. Halting script."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output "Starting parent disk search for: $AVHDXPath"
|
||||
$parentDiskChain = Get-AllParentDisksChain -CurrentDisk $AVHDXPath
|
||||
$totalDisks = $parentDiskChain.Count
|
||||
Write-Output "Total parent disks found: $totalDisks"
|
||||
|
||||
if ($MergeIntoParents) {
|
||||
Write-Output "`nStarting merge process..."
|
||||
for ($i = 0; $i -lt ($totalDisks - 1); $i++) {
|
||||
$currentDisk = $parentDiskChain[$i]
|
||||
$nextDisk = $parentDiskChain[$i + 1]
|
||||
Merge-DiskIntoParent -ChildDisk $currentDisk -ParentDisk $nextDisk -DiskNumber ($i + 1) -TotalDisks $totalDisks
|
||||
}
|
||||
Write-Output "Merge process completed."
|
||||
} elseif ($DryRun) {
|
||||
Write-Output "[Dry Run] Merge simulation completed."
|
||||
}
|
||||
```
|
||||
|
||||
## Script Usage Syntax
|
||||
You can run the script in a few different ways, seen below:
|
||||
|
||||
Merge Disks:
|
||||
`.\Get-HyperVParentDisks.ps1 -MergeIntoParents -AVHDXPath "Z:\Example\Virtual Hard Disks\Example.avhdx"`
|
||||
|
||||
!!! info "Example Output"
|
||||
```
|
||||
Starting parent disk search for: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_E5F78673-3DAD-4211-AC0A-A3BDEB763B63.avhdx
|
||||
Total parent disks found: 6
|
||||
|
||||
Starting merge process...
|
||||
[Differential Disk 1 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_E5F78673-3DAD-4211-AC0A-A3BDEB763B63.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_8B9EDF27-6B7D-4766-AE60-ED67BF3055AE.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 2 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_8B9EDF27-6B7D-4766-AE60-ED67BF3055AE.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_6607B03C-E3F8-49CC-A69B-68BA3DACE81F.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 3 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_6607B03C-E3F8-49CC-A69B-68BA3DACE81F.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_BB68092D-626C-47AA-A20D-93DB0FEB4167.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 4 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_BB68092D-626C-47AA-A20D-93DB0FEB4167.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_2E6147A8-1C6E-4A07-ABA8-6DE3AFB79974.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 5 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_2E6147A8-1C6E-4A07-ABA8-6DE3AFB79974.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER.vhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
Merge process completed
|
||||
```
|
||||
|
||||
Dry Run (Non-Destructive):
|
||||
`.\Get-HyperVParentDisks.ps1 -MergeIntoParents -DryRun -AVHDXPath "Z:\Example\Virtual Hard Disks\Example.avhdx"`
|
||||
|
||||
!!! info "Example Output"
|
||||
```
|
||||
Starting parent disk search for: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_E5F78673-3DAD-4211-AC0A-A3BDEB763B63.avhdx
|
||||
Total parent disks found: 6
|
||||
|
||||
Starting merge process...
|
||||
[Differential Disk 1 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_E5F78673-3DAD-4211-AC0A-A3BDEB763B63.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_8B9EDF27-6B7D-4766-AE60-ED67BF3055AE.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 2 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_8B9EDF27-6B7D-4766-AE60-ED67BF3055AE.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_6607B03C-E3F8-49CC-A69B-68BA3DACE81F.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 3 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_6607B03C-E3F8-49CC-A69B-68BA3DACE81F.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_BB68092D-626C-47AA-A20D-93DB0FEB4167.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 4 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_BB68092D-626C-47AA-A20D-93DB0FEB4167.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_2E6147A8-1C6E-4A07-ABA8-6DE3AFB79974.avhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
|
||||
[Differential Disk 5 of 6]
|
||||
Child: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER_2E6147A8-1C6E-4A07-ABA8-6DE3AFB79974.avhdx
|
||||
Parent: Z:\DISK-MERGE-TESTER\Virtual Hard Disks\DISK-MERGE-TESTER.vhdx
|
||||
[Dry Run] Would merge child into parent
|
||||
Merge process completed.
|
||||
```
|
Reference in New Issue
Block a user