How to Convert VHD to VHDX Using PowerShell

To convert a VHD to VHDX in PowerShell, shut down the VM and run Convert-VHD on the Hyper-V host. The new format comes from the file extension you give the destination:

Convert-VHD -Path 'D:\VMs\web-01.vhd' -DestinationPath 'D:\VMs\web-01.vhdx'

VHDX supports disks up to 64 TB instead of 2 TB and handles power failures better, so it’s worth converting older disks. Below I cover the requirements, a safe step-by-step conversion, a folder dry run and how to spot disks with the wrong extension.

What you need first

Convert-VHD is part of the Hyper-V module, which comes with the Hyper-V role. On my laptop, which doesn’t run Hyper-V, PowerShell can’t find it:

try { Get-Command -Name Convert-VHD -ErrorAction Stop }
catch { "Failed. $($_.Exception.Message)" }
"Hyper-V module installed: $([bool](Get-Module -ListAvailable -Name Hyper-V))"

Output:

Failed. The term 'Convert-VHD' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Hyper-V module installed: False
PowerShell Convert-VHD is not recognized
No Hyper-V role, so no Convert-VHD (PowerShell 7)

Windows PowerShell 5.1 words the error slightly differently, but it means the same thing. Run the conversion on a Hyper-V host from an elevated PowerShell window.

To add Hyper-V on a server or on Windows 11 Pro or Enterprise, run one of these as administrator and restart. To see which features are already on, list the Windows features first:

# Windows Server
Install-WindowsFeature -Name Hyper-V -IncludeManagementTools -Restart

# Windows 11 Pro or Enterprise
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All

Convert one disk step by step

Conversion is an offline operation, so the disk can’t be attached to a running VM. This is the order I follow on a production host:

$vm  = 'WEB-01'
$vhd = 'D:\VMs\web-01.vhd'
$new = 'D:\VMs\web-01.vhdx'

Stop-VM -Name $vm
Convert-VHD -Path $vhd -DestinationPath $new -VHDType Dynamic

$drive = Get-VMHardDiskDrive -VMName $vm | Where-Object Path -EQ $vhd
Set-VMHardDiskDrive -VMHardDiskDrive $drive -Path $new
Start-VM -Name $vm

Convert-VHD copies the data into a new file, so you need free space for both disks. Without -VHDType, the new disk keeps the source type, fixed or dynamic. Check the host’s free space first, as shown in checking hard drive space.

Check the result with Get-VHD -Path $new. The VhdFormat property should say VHDX.

Remove the old VHD

Once the VM boots and works, delete the old file. I keep it for a day or two first. If you’re sure, -DeleteSource removes it as part of the conversion:

Convert-VHD -Path $vhd -DestinationPath $new -DeleteSource

Don’t convert a disk that has checkpoints. Delete or merge the checkpoints first, because the differencing files point at the original VHD.

Plan a whole folder first

Before converting many disks, list what would change. I used small test files in a VMs folder, two .vhd and two .vhdx. First, a trap that caught me in testing:

(Get-ChildItem -Path .\VMs -Filter *.vhd).Name

Output:

sql-01.vhd
web-01.vhd
PowerShell Filter *.vhd also matches vhdx files
-Filter *.vhd returns the .vhdx files too (Windows PowerShell 5.1)

The output above is from PowerShell 7. In Windows PowerShell 5.1, -Filter *.vhd also matched both .vhdx files, because Windows matches the short 8.3 file names. Filter on the Extension property instead, which works the same in both. More filter quirks are in Get-ChildItem -Filter:

Get-ChildItem -Path .\VMs -File | Where-Object Extension -EQ '.vhd' | ForEach-Object {
    $target = [System.IO.Path]::ChangeExtension($_.FullName, '.vhdx')
    [pscustomobject]@{
        Source   = $_.Name
        Target   = Split-Path -Path $target -Leaf
        SizeMB   = $_.Length / 1MB
        Conflict = Test-Path -Path $target
    }
}

Output:

Source     Target      SizeMB Conflict
------     ------      ------ --------
sql-01.vhd sql-01.vhdx     12    False
web-01.vhd web-01.vhdx      8    False

The dry run builds each target name and checks that it doesn’t exist yet. When the list looks right, loop over the same files with Convert-VHD on the host.

Check a disk’s real format

Convert-VHD trusts the file extension, but files get renamed. The format is written inside the file: a VHDX starts with the text vhdxfile, and a VHD has conectix in its last 512 bytes.

The function below reads both. I ran it on four test files that hold only those signatures, including one VHD that was renamed to .vhdx:

function Get-VirtualDiskFormat ([string]$Path) {
    $fs = [System.IO.File]::OpenRead($Path)
    try {
        $buffer = [byte[]]::new(8)
        [void]$fs.Read($buffer, 0, 8)
        if ([System.Text.Encoding]::ASCII.GetString($buffer) -eq 'vhdxfile') { return 'VHDX' }
        $fs.Position = $fs.Length - 512                       # VHD footer
        [void]$fs.Read($buffer, 0, 8)
        if ([System.Text.Encoding]::ASCII.GetString($buffer) -eq 'conectix') { return 'VHD' }
        'Unknown'
    }
    finally { $fs.Dispose() }
}

Get-ChildItem -Path .\VMs -Include *.vhd, *.vhdx -Recurse | ForEach-Object {
    [pscustomobject]@{
        Name       = $_.Name
        Extension  = $_.Extension
        RealFormat = Get-VirtualDiskFormat $_.FullName
    }
}

Output:

Name        Extension RealFormat
----        --------- ----------
app-01.vhdx .vhdx     VHDX
old-dc.vhdx .vhdx     VHD
sql-01.vhd  .vhd      VHD
web-01.vhd  .vhd      VHD
PowerShell detect VHD or VHDX file format
old-dc.vhdx is really a VHD (PowerShell 7)

This only reads the files, so it works without Hyper-V. On a host, Get-VHD shows the same VhdFormat for real disks. To get the file sizes too, see getting the size of a file.

Frequently Asked Questions

How do I convert VHD to VHDX in PowerShell?

Stop the VM, then run Convert-VHD -Path disk.vhd -DestinationPath disk.vhdx on the Hyper-V host. Every parameter is in Microsoft’s Convert-VHD reference.

Can I convert a VHD while the VM is running?

No. Conversion is offline, so the disk must not be attached to a running VM. Shut the VM down first.

Why is Convert-VHD not recognized?

It comes with the Hyper-V role. Install Hyper-V as Microsoft’s Install Hyper-V guide shows, or run the command on a Hyper-V host.