PowerShell – Convert all VHD’s on your HV cluster to VHDX


The other morning I was reviewing my hyper-v estate for VM’s running with VHD’s in the legacy VHD format. I thought to myself that a script to convert all of the VHD’s but also taking in several other important steps such as shutting down the VM, optimising the new VHDX, shrink the VHD (where possible), connecting the new VHDX and starting the machine would be nice.

I started down the path of writing the script when I came across a post on Aidan Finn’s blog (http://www.aidanfinn.com/?p=16007) which undertook the majority of what I was trying to achieve with the exception that the script runs on a single host. I decided to press on write my own script to cover off the cluster element.

The result is the below script, which only requires the name of your cluster. Once this is specified the script will connect to each of your cluster hosts in sequence, get a list of VM’s with VHD’s and proceed with the conversion process.

There is a section within the script which you can uncomment (AT YOUR OWN RISK) to remove the legacy VHD disks post conversion. As always make sure you have backup’s, allow for downtime and also make sure you have sufficient space on your LUN’s to facilitate the conversion process.  The scripts are provided as is and without support.

Cluster Script (Updated as per Jeffery Hick’s comment) :


$Cluster = "ENTER CLUSTER NAME IN HERE"
$HVNodes = Get-ClusterNode -Cluster $Cluster
ForEach ($HVNode in $HVNodes) {
    $HVPS = New-PSSession -ComputerName $HVNode
    Write-Host ""
    Write-Host "==========================================================="
    Write-Host "Connecting to $HVNode to read VM settings"
    Write-Host "==========================================================="
    Write-Host ""
    Invoke-Command -Session $HVPS -ScriptBlock {
    ForEach ($VM in (Get-VM)){
    $VMName = $VM.name
    $VMDetails = Get-VM -Name $VMName | Select-Object VMID | get-vhd | Where-Object {$_.vhdFormat -eq "VHD"}
        If($VMStatus.State -eq "Running"){
        Write-Host ""
        Write-Host -ForegroundColor Red "==========================================================="
        Write-Host "Stopping VM $VMName"
        Write-Host -ForegroundColor Red "==========================================================="
        Write-Host ""
        Stop-VM -VMName $VMName
        }
        ForEach ($VHD in $VMDetails){
        $VMVHDDetails = $VHD.Path
        $VMStatus = Get-VM -name $VMName
        Write-Host ""
        Write-Host "==========================================================="
        Write-Host "Beginning VHD conversion process for VM $VMName"
        Write-Host "==========================================================="
        Write-Host ""
            ForEach ($VHD in $VMVHDDetails){
            $NewVHD = ($VHD + "x")
                if(!(Test-Path $NewVHD))
                {
                Write-Host ""
                Write-Host "Step 1."
                Write-Host -ForegroundColor Green "Converting $VHD to fixed VHDX format"
                Convert-VHD -Path $VHD -DestinationPath $NewVHD -VHDType Fixed -verbose
                sleep 10
                Write-Host ""
                Write-Host "Step 2."
                Write-Host -ForegroundColor Green "Changing $NewVHD Sector Size (4096k)"
                Set-VHD -Path $NewVHD -PhysicalSectorSize 4096
                sleep 10
                Write-Host ""
                Write-Host "Step 3."
                Write-Host -ForegroundColor Green "Modifying VM Settings To Use $NewVHD"
                Get-VMHardDiskDrive -VMName $VMName | Where-object {$_.path -eq "$VHD"} | Set-VMHardDiskDrive -Path $NewVHD
                #sleep 10
                Write-Host ""
                Write-Host "Step 4."
                Write-Host -ForegroundColor Cyan "Shrinking VHDX to minimal file size"
                Resize-VHD -Path $NewVHD -ToMinimumSize
                #Uncomment this section if you wish to remove the old VHD drives post conversion. DO SO AT YOUR OWN RISK
                #Write-Host ""
                #Write-Host "Step 5."
                #Write-Host -ForegroundColor Red "Removing old VHD"
                #Remove-VHHardDiskDrive $VHD
                #sleep 10
                Write-Host ""
                }
                Else {
                Write-Host -ForegroundColor Gray "Skipping $VHD as virtual disk already exists in the target location"
                }
                }
        Write-Host ""
        Write-Host -ForegroundColor Green "==========================================================="
        Write-Host "Starting VM $VMName"
        Write-Host -ForegroundColor Green "==========================================================="
        Write-Host ""
        Start-VM -VMName $VMName
        }
    }
    }
}

Cluster Script (With minimal screen feedback):

$Cluster = "Enter your cluster name here"
$HVNodes = Get-ClusterNode -Cluster $Cluster
ForEach ($HVNode in $HVNodes) {
    $HVPS = New-PSSession -ComputerName $HVNode
    Write-Host ""
    Write-Host "Checking $HVNode"
    Invoke-Command -Session $HVPS -ScriptBlock {
    ForEach ($VM in (Get-VM)){
    $VMName = $VM.name
    $VMDetails = Get-VM -Name $VMName | Select-Object VMID | get-vhd | Where-Object {$_.vhdFormat -eq "VHD"}
        If($VMStatus.State -eq "Running"){
        Write-Host "Stopping $VMName"
        Write-Host ""
        Stop-VM -VMName $VMName
        }
        ForEach ($VHD in $VMDetails){
        $VMVHDDetails = $VHD.Path
        $VMStatus = Get-VM -name $VMName
        Write-Host ""
        Write-Host "Beginning VHD conversion process for VM $VMName"
            ForEach ($VHD in $VMVHDDetails){
            $NewVHD = ($VHD + "x")
                if(!(Test-Path $NewVHD))
                {
                Write-Host ""
                Write-Host "Converting $VHD to fixed VHDX format"
                Convert-VHD -Path $VHD -DestinationPath $NewVHD -VHDType Fixed -verbose
                sleep 10
                Write-Host "Changing $NewVHD Sector Size"
                Set-VHD -Path $NewVHD -PhysicalSectorSize 4096
                sleep 10
                Write-Host "Modifying VM Settings To Use $NewVHD"
                Get-VMHardDiskDrive -VMName $VMName| Where-object {$_.path -eq "$VHD"} | Set-VMHardDiskDrive -Path $NewVHD
                sleep 10
                Write-Host "Shrinking VHDX to minimal file size"
  Resize-VHD -Path $NewVHD -ToMinimumSize
                #Uncomment this section if you wish to remove the old VHD drives post conversion. DO SO AT YOUR OWN RISK
                #Write-Host "Removing old VHD"
                #Remove-VHHardDiskDrive $VHD
                #sleep 10
                Write-Host ""
                }
                Else {
                Write-Host "Skipping $VHD as virtual disk already exists in the target location"
                }
                }
Start-VM -VM $VMName
        }

    }
    }
}

Single Host Script:

ForEach ($VM in (Get-VM)){
    $VMName = $VM.name
    $VMDetails = Get-VM -Name $VMName | Select-Object VMID | get-vhd | Where-Object {$_.vhdFormat -eq "VHD"}
    ForEach ($VHD in $VMDetails){
    $VMVHDDetails = $VHD.Path
    $VMStatus = Get-VM -name $VMName
    If($VMStatus.State -eq "Running"){
    Write-Host "Stopping $VMName"
    Stop-VM -VMName $VMName
    }
    ForEach ($VHD in $VMVHDDetails){
            $NewVHD = ($VHD + "x")
                if(!(Test-Path $NewVHD))
                {
                Write-Host ""
                Write-Host "Converting $VHD to fixed VHDX format"
                Convert-VHD -Path $VHD -DestinationPath $NewVHD -VHDType Fixed -verbose
                sleep 10
                Write-Host "Changing $NewVHD Sector Size"
                Set-VHD -Path $NewVHD -PhysicalSectorSize 4096
                sleep 10
                Write-Host "Modifying VM Settings To Use $NewVHD"
                Get-VMHardDiskDrive -VMName $VMName | Where-object {$_.path -eq "$VHD"} | Set-VMHardDiskDrive -Path $NewVHD
                sleep 10
                Write-Host "Shrinking VHDX to minimal file size"
  Resize-VHD -Path $NewVHD -ToMinimumSize
                #Uncomment this section if you wish to remove the old VHD drives post conversion. DO SO AT YOUR OWN RISK
                #Write-Host "Removing old VHD"
                #Remove-VHHardDiskDrive $VHD
                sleep 10
                Write-Host ""
                }
                Else {
                Write-Host "Skipping $VHD as virtual disk already exists in the target location"
                }
             }
 Start-VM -VM $VMName
        }
    }

3 thoughts on “PowerShell – Convert all VHD’s on your HV cluster to VHDX

  1. Jeffery Hicks (@JeffHicks) November 7, 2014 / 1:32 pm

    Nice. If I may make some suggestions, use a Foreground color with Write-Host so it is clear what are messages and what is output. Even better would be to use Write-Progress. You could also use cmdlet binding and support shouldProcess which would let you use -WhatIf with your script.

    • modalyit November 7, 2014 / 1:37 pm

      Thanks for the feedback Jeffery, much appreciated. I did originally include foreground colour outputs on the various stages but I removed them in an attempt to be as clean as possible but I take your point. I’ll throw up a modified version later which provides better feedback to the user.

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