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 since 1999 and was awarded his first MVP Enterprise Mobility award in 2017. Technology focus includes Active Directory, Group Policy, Hyper-V, Windows Deployment (SCCM & MDT) and Office 365.

(89)

comments
  • Jeffery Hicks (@JeffHicks)
    Posted at 13:32 November 7, 2014
    Jeffery Hicks (@JeffHicks)
    Reply
    Author

    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
      Posted at 13:37 November 7, 2014
      modalyit
      Reply
      Author

      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.

  • Alan Doran
    Posted at 14:40 November 7, 2014
    Alan Doran
    Reply
    Author

    Reblogged this on Alan Doran IT Ramblings.

  • Leave a Reply