How to compact Hyper-V VHD / VHDX files using PowerShell?

Discussion created by Gregor on Nov 15, 2013

I am running a Hyper-V Server (Windows Server 2012 Standard Edition). When I started working with virtual machines (VM), I quickly realized that todays CPU's are usually strong enough to serve multiple VM instances in parallel. Memory resources can be a more serious issue but I found the main bottleneck usually is the performance of the hard disk(s) hosting the virtual disks. Luckily these days Solid State Disk's are offering pretty good performance and become more and more affordable even capacity is far below of what recent hard disks offer. So I decided adding 2 SSD's in addition to the disk hosting the operating system to my Hyper-V server. The 2 SSD have a capacity of ~250 GB each and are combined as RAID 0. The capacity doesn't allow extensively creating VM snapshots but I am pretty happy about the overall performance.


Since I know about the capacity limitations, I am trying to free up disk space e.g. by deleting setup kits right after using them. Unfortunately this doesn't shrink the virtual hard disks. Hyper-V manager offers compacting disks while the VM is shut down. One needs to open the Settings for a particular VM and edit the virtual hard disk file. I however experienced that this functionality - at least sometimes - behaves like a lame duck. It wasn't clear to me if compacting worked or not; if it just starts its operation late or is doing nothing at all. At this point I started thinking about alternatives and found that VHD / VHDX files can be compacted using PowerShell. For sure the VM must be shut down to allow compacting but there shouldn't be any risk for damage because the script will fail if the VM is running.



# Script requires Administrator privileges
# Requires PowerShell 3.0 or later
# Script execution needs to be enable e.g. by
# Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
# Path and Filename of a virtual hard disk
# Appears strange but file needs to be mounted ReadOnly
Mount-VHD -Path $VHDPath -ReadOnly
# PowerShell should display the process in optimization
Optimize-VHD -Path $VHDPath -Mode Full
Dismount-VHD $VHDPath

Here's the "advanced" version that allows to compact multiple virtual disks at once. 



$Files=get-childitem $Path *.vhdx -rec|where-object {!($_.psiscontainer)}
foreach ($File in $Files) 
    [string]$VHDFile = $Path + "\" + $File.Name
    Mount-VHD -Path $VHDFile -ReadOnly
    Write-Host "Compacting " $VHDFile
    Optimize-VHD -Path $VHDFile -Mode Full
    Dismount-VHD $VHDFile

 Finally a hint: Compacting works optimal if the disk has been defragmented from within the VM OS first.