Script To Convert Hyper-V Virtual Machine From VHD To VHDX

Last year I wrote a script that would allow you to specify a virtual machine, and the script would:

  1. Shut down the VM if running
  2. Seek out any VHD files attached to any of the VM’s controllers
  3. Create VHDX files from those VHD files
  4. Replace the VHD files by attaching the VHDX files to the same controllers and locations in the VM settings
  5. Delete the VHD files

In my tests, the script had some issues. But that was nearly a year ago and it was on WS2012 in my lab. The script remained untouched until yesterday. I was chatting with my fellow Hyper-V MVP, Didier Van Hoye (aka @workinghardinit). He told me he was in the process of migrating VMs from an old W2008 R2 cluster to WS2012 and was going to be converting VHD files. Aha! This might be a time for a solution to speed up the process.

I sent the script over to Didier to have a look-see. Would it work. Well, Didier ran a series of tests this morning with guest OSs including W2003 R2 and WS2012. The tests ran flawlessly.

So … here is the script. FYI there are few things to note:

  • You might consider putting in a delay loop to test if the VM is actually shut down if you need to shut it down. Put a timeout of 3 minutes in that. The stop-vm cmdlet is async so it shouldn’t cause an issue as it is below, but you might want to take the extra step, just in case.
  • You might want to comment out the line Remove-VMHardDiskDrive $VHD for your test or pilot runs.
  • I do not support this script 🙂
  • Run the script and specify the VM name as a parameter.

CREDIT: A big thank you to Didier Van Hoye (aka @workinghardinit) for checking my work.

#—-

[CmdletBinding ()]
Param   (
        [Parameter(Mandatory=$True)]
        [string]$VMName
        )

#Disable error reporting – comment out the following line if you need to troubleshoot the script
$ErrorActionPreference = "SilentlyContinue"

cls

$VM = Get-VM $VMName
$VMStatus = $VM.State

if ($VM.VMid -ne $NULL)
{
    if ($VMStatus -eq "Running")
    {  
        #Shut down the VM if it is running
        Write-Host "Shutting down" $VMName
        Stop-VM $VMName  
    }

    #Get the disks in the VM
    $AllVHD = Get-VMHardDiskDrive $VMName

    if ($AllVHD -eq $NULL)
        {
        Write-Host "There are no virtual hard disks to convert"
        Exit
        }

    foreach ($VHD in $AllVHD)
    {
        #Get the VM path and create a VHDX file path
        [string]$VHDFile = Get-Item $VHD.Path
        $VHDFormat = (Get-VHD $VHDFile).VhdFormat
        if ($VHDFormat -eq "VHD")
            {
            [string]$VHDXFile = $VHDFile + "x"

            [string]$ControllerType = $VHD.ControllerType
            [string]$ControllerNumber = $VHD.ControllerNumber
            [string]$ControllerLocation = $VHD.ControllerLocation

            Write-Host "Converting: " $VHDFile "to" $VHDXFile
            Convert-VHD –Path $VHDFile –DestinationPath $VHDXFile
            Sleep 10

            #Reconfigure the Physical Sector Size of the VHDX file to 4 K
            Set-VHD -Path $VHDXFile -PhysicalSectorSizeBytes 4096
            Sleep 10

            #Remove the old VHD
            Write-Host "Removing $VHDFile from $VMName"
            Remove-VMHardDiskDrive $VHD
            Sleep 10
            #Replace the VHD with the VHDX
            Write-Host "Adding $VHDXFile to $VMName"
            Add-VMHardDiskDrive -VMName $VMName -Path $VHDXFile -ControllerType $ControllerType -ControllerNumber $ControllerNumber -ControllerLocation $ControllerLocation

            #Danger Will Robinson – we are going to delete the original VHD – we hope you have a tested VM backup!
            Write-Host "Deleting $VHDFile"
            Remove-Item $VHDFile -Force
            }
        else
            {
            Write-Host "$VHDFile is already a VHDX file: skipping"
            }
    }

    if ($VMStatus -eq "Running")
    {  
        #Restart the VM if it was running before the conversion
        Write-Host "Starting" $VMName
        Start-VM $VMName  
        #Wait for 10 seconds
        Write-Host "Waiting for 10 seconds to verify the virtual machine …"
        Sleep 10
        $VMStatus = $VM.State
        if ($VMStatus -ne "Running")
        {
            #Something went wrong
            Write-Host "$VMName could not reboot – please restore the VM from backup"     
        }
    }

}
else
{
    Write-Host $VMName "does not exist on this host"
    Exit
}

Write-Host "Processing of $VMName has completed"

2 thoughts on “Script To Convert Hyper-V Virtual Machine From VHD To VHDX”

  1. Consider a particular case, in a Windows 8 environment, when you can’t attach a .vhd file to a Hyper-V machine. It just won’t find any .vhds to work, so a version to offline vhds would be handy.
    Anyway thank you, great work!

Leave a Reply

Your email address will not be published. Required fields are marked *

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