MSEndpointMgr

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.

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 :

$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
        }
    }

Maurice Daly

Maurice has been working in the IT industry for the past 20 years and currently working in the role of Senior Cloud Architect with CloudWay. With a focus on OS deployment through SCCM/MDT, group policies, active directory, virtualisation and office 365, Maurice has been a Windows Server MCSE since 2008 and was awarded Enterprise Mobility MVP in March 2017. Most recently his focus has been on automation of deployment tasks, creating and sharing PowerShell scripts and other content to help others streamline their deployment processes.

3 comments

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

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

Sponsors