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