Quickly migrate multiple virtual machines to Standard SSD
I wanted to get a script to convert multiple virtual machines from Standard HHD to Standard SSD if they were ready for it.
By ready I mean; a virtual machine that I have prepared a scheduled maintenance window for.
There are many ways, but I took the choice of using a specific tag that have to be present on the virtual machines that will be converted in bulk. This approach give me the opportunity to ask a coworker or a customer to determine which virtual machine can be converted doing the scheduled maintenance window, without having to deal with things like an input list in .CSV format or other ways of agreeing to which virtual machines can be converted. One could argue that it would be nice to have support for multiple scheduled maintenance windows – if needed, this could easily be done by changing the input tag to a read-host.
I asked Copilot for GitHub to write me the whole script (this would have taken a few seconds and then I was done). But I think my scripting skills outrun my prompting skills, because the PowerShell script I got, would take all VMs with the tag, and shut them down before looking if the VMs OS disk was actually Standard HHD. So I decided to take the output and rewrite and add output feature so I could monitor if disk conversion failed. I also added a tagging to each completed VM.
The script will:
- Get all virtual machines in an Azure subscription with the tag “ready-for-disk-conversion” if value of that tag is “true”
- Loop over each eligible VM
- Check if the VM’s OS disk is Standard_HHD
- Deallocate the VM
- Convert the OS disk to StandardSSD
- Set tag “disk-conversion-status” to either completed or “failed” on the VM
- Boot the VM and monitor if success
- Output results to .CSV file: “.\VM_Disk_Migration_Results-$Today.csv”
# Define variables
$TagName = "ready-for-disk-conversion"
$TagValue = "true"
$Today = Get-Date -Format "yyyy-MM-dd"
# Load Azure modules
Import-Module Az.Compute
Import-Module Az.Resources
Import-Module Az.Accounts
# Login to Azure account
Connect-AzAccount
# Select subscription (interactively)
$subscription = Get-AzSubscription | Out-GridView -Title "Select an Azure Subscription" -PassThru
Set-AzContext -SubscriptionId $subscription.Id
# Get all VMs with the specified tag
$vmList = Get-AzVM -Status | Where-Object {
$_.Tags.ContainsKey($TagName) -and $_.Tags[$TagName] -eq $TagValue
}
$vmListResults = @()
foreach ($vm in $vmList) {
write-information "Processing VM: $($vm.Name)" -ForegroundColor Cyan
$resourceGroup = $vm.ResourceGroupName
# Get OS disk
$osDiskName = $vm.StorageProfile.OsDisk.Name
$osDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName $osDiskName
if ($osDisk.Sku.Name -eq "StandardHDD_LRS") {
$Status = "N/A"
$VMBoot = "N/A"
# Deallocate the VM
write-information "Deallocating VM: $($vm.Name)..."
Stop-AzVM -Name $vm.Name -ResourceGroupName $resourceGroup -Force -NoWait
# Wait for deallocation to complete
do {
Start-Sleep -Seconds 5
$vmStatus = (Get-AzVM -ResourceGroupName $resourceGroup -Name $vm.Name -Status).Statuses |
Where-Object { $_.Code -like 'PowerState/*' }
} while ($vmStatus.DisplayStatus -ne "VM deallocated")
try {
write-information "Converting OS disk to StandardSSD_LRS..."
$osDisk.Sku.Name = "StandardSSD_LRS"
Update-AzDisk -ResourceGroupName $resourceGroup -DiskName $osDiskName -Disk $osDisk
$Status = "Success"
# Remove the migration tag
write-information "Removing tag '$TagName' from VM..."
$vm.Tags.Remove($TagName)
Set-AzVM -VM $vm
# Set failed tag 'disk-migration-status=completed'
$vm.Tags["disk-conversion-status"] = "completed"
Set-AzVM -VM $vm
}
catch {
$Status = "Failed"
# Set failed tag 'disk-migration-status=failed'
$vm.Tags["disk-conversion-status"] = "failed"
Set-AzVM -VM $vm
}
# Start the VM
write-information "Starting VM: $($vm.Name)..."
try {
Start-AzVM -Name $vm.Name -ResourceGroupName $resourceGroup -ErrorAction Continue
Write-Host "VM '$($vm.Name)' started successfully."
$VMBoot = "Success"
}
catch {
Write-Error "Failed to start VM '$($vm.Name)'"
$VMBoot = "Failed"
}
write-information "Disk conversion completed for VM: $($vm.Name)" -ForegroundColor Green
$vmListResults += [PSCustomObject]@{
VMName = $vm.Name
ResourceGroup = $resourceGroup
Status = $Status
VMBoot = $VMBoot
NewDiskSku = $osDisk.Sku.Name
}
}
}
$vmListResults | Export-CSV -Path ".\VM_Disk_Migration_Results-$Today.csv" -NTI
Use this script to get all virtual machines in the Azure subscription that failed to convert, if you prefer this method over reading the outout .CSV file:
# Define variables
$TagName = "disk-conversion-status"
$TagValue = "failed"
# Load Azure modules
Import-Module Az.Compute
Import-Module Az.Resources
Import-Module Az.Accounts
# Login to Azure account
Connect-AzAccount
# Select subscription (interactively)
$subscription = Get-AzSubscription | Out-GridView -Title "Select an Azure Subscription" -PassThru
Set-AzContext -SubscriptionId $subscription.Id
# Get all VMs with the specified tag
Get-AzVM -Status | Where-Object {$_.Tags.ContainsKey($TagName) -and $_.Tags[$TagName] -eq $TagValue}