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

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…http://1drv.ms/1IES3pB


Categories: Hyper-V, PowerShell

Tagged as: , ,

13 replies »

  1. 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?

  2. 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 “”}

  3. 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…..so 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

Leave a Reply to Mikael Nystrom Cancel reply

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

WordPress.com Logo

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

Facebook photo

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

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.