The Deployment Bunny

OS Deployment, Virtualization, Microsoft based Infrastructure…

  • Archives

  • Meta

PowerShell is King – Compress, Compact, Optimize VHD and VHDX files

Posted by Mikael Nystrom on June 15, 2015

I was reading a thread at Facebook in our user group, someone was trying to compress/compact/optimize a VHD(VHDX files but the result was depressing, the amount of reclaimed space was zero and that is kind of low… Now there is a good explanation for that, when using the UI there is no way to configure it and the UI also don’t inform you that the ONLY way to get it small is to mount it in Read Only mode. It really has to be mounted, otherwise there is now way to see “inside” the disk and to get rid of all the Zero blocks. It does not hurt if you also run defrag and cleanup inside the VM before, but the most important thing is that it needs to be mounted.

The documentation for the PowerShell command says:


The Script:

So, to make things easy for my friends in the User Group I created a simple script that will find the disks for the VM, check if the VM is running and if not mount each file in read only mode with no drive letter, optimize the VDH and dismount it. So the output of the script looks like this when running using –verbose


and here is how the script looks like:


and you can download the script here…


13 Responses to “PowerShell is King – Compress, Compact, Optimize VHD and VHDX files”

  1. This doesn’t seem to work if the VM has a snapshot.

  2. NoFloorPlans said

    Hi Mike,

    Nice PowerShell Script, very much needed.

    Works nice but have two questions:

    “It does not hurt if you also run defrag and cleanup inside the VM before”
    This seems to be a must to be able to shrink volumes.

    1. Is there a way to perform a defrag as well from Powershell when the VM is shutdown (in this script), or how is this solved in the real life?
    2. I have modified the script so I have a TXT file that have all the VMnames in it, but is there a way to put the “-Verbose” parameter from the “command line” into the script instead?

  3. I know this is now bit of an old article but I have issues with a lot of the objects not existing.
    I made a few changes so this can also be run from the System Center host for a clustered environment.
    Hope it is able to help someone else out.

    #Dont put any code above this Commandlet

    Import-Module hyper-v
    Import-Module virtualmachinemanager

    foreach($VMname in $VMnames){
    #Check if VM is running
    Write-host “Checking $VMname”
    if((Get-VM -Name $VMname).VirtualMachineState -eq “PowerOff” -and (Get-VMCheckpoint -VM $VMname) -eq $null){

    $VMHost = (Get-VM -Name $VMname).HostName

    #Find the disks
    foreach($VHD in ((Get-SCVirtualHardDisk -VM $VMname).Location)){
    $arguments = @($VHD)
    Write-host “Working on $VHD, please wait”
    $VHDFileSize = Invoke-Command -ComputerName $VMHost -ArgumentList $arguments -ScriptBlock {Param($VHD) (Get-VHD -Path $VHD).FileSize/ 1GB}
    Write-host “Current size $([math]::truncate($($VHDFileSize))) GB”
    Invoke-Command -ComputerName $VMHost -ArgumentList $arguments -ScriptBlock {Param($VHD) Mount-VHD -Path $VHD -NoDriveLetter -ReadOnly}
    Invoke-Command -ComputerName $VMHost -ArgumentList $arguments -ScriptBlock {Param($VHD) Optimize-VHD -Path $VHD -Mode Full}
    $VHDFileSize = Invoke-Command -ComputerName $VMHost -ArgumentList $arguments -ScriptBlock {Param($VHD) (Get-VHD -Path $VHD).FileSize/ 1GB}
    Write-host “Optimize size $([math]::truncate($($VHDFileSize))) GB”
    Invoke-Command -ComputerName $VMHost -ArgumentList $arguments -ScriptBlock {Param($VHD) Dismount-VHD -Path $VHD}
    Write-host “If size has not changed please run a disk defrag”
    Write-host “”
    else{Write-Warning “$VMname is not turned off or has a snapshot, will not be fixed”
    Write-host “”}

  4. Gary said

    How can I achieve this without shutting the VM down, if I move the VM to another Cluster Shared Volume it releases the white space… if this cant be achieved without shutting down, anyone got a good script to report on white space (the difference between used space in a VM and VHD size)?

    • Visit this site again, at 2.01 PM and you will have a very shiny script that grabs data about every VHD file in your SCVMM environment, let me know if you don’t use SCVMM and I’ll write a new function that does the same thing but directly for Hyper-V

  5. Gary said

    We do use SCVMM so would greatly appreciate!

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: