Defragment a VHDX

While staying out of the debate on fixed versus dynamic VHDX files, the dynamic flavor will end up being fragmented on your drive.  While trying to diagnose some performance issues on a VM, we took a look at the VHDX file with the Get-VHD PowerShell command.

vhdx_defrag_before

What we saw was a fragmentation level of 100%.  That was high enough that we wanted to correct it and rule it out as a factor.  We tried what we saw suggested elsewhere to copy the VHDX to another machine or drive, but whether we let VMM do it, or did it ourselves, the file reported the same thing afterwards.

What ended up working was to use the Convert-VHD command to write out a new file.  This is pretty simple, just run:

Convert-VHD -DestinationPath newfile.vhdx -Path oldfile.vhdx -VHDType Dynamic

That gives us a much better report when we check the VHDX.

vhdx_defrag_after
You’d think that you could just rename the two files, and fire up your VM and be back in business.  Unfortunately if you do this you just end up with an error.  This is because there is security applied to the VHDX file that ties it to the VM.  Without that security the VM can’t access the file.  Since you still have the original file, you could look up the security permissions and then put them back with icacls, but we have PowerShell and PowerShell has a great pair of commands get-acl and set-acl.

You’d think that you could just run:

$acl = Get-Acl oldfile.vhdx
Set-Acl newfile.vhdx -AclObject $acl

Unfortunately you just get an error

set-acl : The security identifier is not allowed to be the owner of this object.

This is because the Get-Acl command is pulling in more data that we want (specifically the owner data).  However you can just get the part we want like this:

$acl = (Get-Item oldfile.vhdx).GetAccessControl(‘Access’)

Then you can use the store acl to update your new file and start your VM.

We can combine all of these into a little script as follows:

$vhdtofix = “oldfile.vhdx”
$newvhdx = “newfile.vhdx”
$acl = (Get-Item $vhdtofix).GetAccessControl(‘Access’)
Convert-VHD -DestinationPath $newvhdx -Path $vhdtofix -VHDType Dynamic
Set-Acl $newvhdx -AclObject $acl

Now you just need to change the names or point the VM to the new file and your freshly defragmented version will be used.

Advertisements

Leave a 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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s