Hello Friends,
Welcome back to Part 2. In Part 1 we covered why Premium SSD creates the oversizing problem, how Premium SSD v2 changes the pricing model, the constraints that shape migration planning, and a PowerShell script to identify candidate disks across a subscription. If you have not read that yet, start there because the zone requirement in particular will come up again here.
In this part we go through the actual migration. The good news is that the process is more direct than most people expect: you deallocate the VM, update the disk SKU in place, and restart. There is no new disk to create, no data to copy, and no disk swap. The existing disk is converted to Premium SSD v2 with the same name and resource ID it always had.
How the conversion actually works
The migration is a property update on the existing managed disk. You change the SKU from Premium_LRS to PremiumV2_LRS, set the IOPS and throughput values at the same time, and Azure handles the conversion in the background. You can monitor progress as it happens. When it reaches 100 percent, you restart the VM and that is it.

Before you start
A few things to confirm before touching any disk:
- The target region supports Premium SSD v2 (covered in Part 1)
- The VM has an availability zone assignment. Premium SSD v2 requires a zone, and the disk zone must match the VM zone. If the VM has no zone, resolve that first before attempting the conversion
- Host caching on the disk attachment is set to None. Premium SSD v2 does not support host caching. This must be cleared from the VM resource before the conversion, not the disk resource itself
- You have a maintenance window. The VM must be deallocated for the duration of the SKU change
- IaC update is planned and assigned. The conversion is not done until the code matches the live state
The migration script
The script below handles the full workflow. It confirms the disk is on Premium_LRS, deallocates the VM, updates the disk SKU and provisioned values in a single operation, monitors conversion progress, and restarts the VM.
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$resourceGroupName,
[Parameter(Mandatory = $true)]
[string]$vmName,
[Parameter(Mandatory = $true)]
[string]$diskName,
[Parameter(Mandatory = $false)]
[int]$provisionedIops = 3000,
[Parameter(Mandatory = $false)]
[int]$provisionedThroughputMBps = 125,
[switch]$whatIf
)
$ErrorActionPreference = 'Stop'
$disk = Get-AzDisk -ResourceGroupName $resourceGroupName -DiskName $diskName
if ($disk.Sku.Name -ne 'Premium_LRS') {
throw "Disk '$diskName' is not a Premium SSD disk. Current SKU: $($disk.Sku.Name)"
}
Write-Host "Disk : $diskName" -ForegroundColor Cyan
Write-Host "Current SKU : $($disk.Sku.Name)" -ForegroundColor White
Write-Host 'Target SKU : PremiumV2_LRS' -ForegroundColor White
Write-Host "IOPS : $provisionedIops" -ForegroundColor White
Write-Host "Throughput : $provisionedThroughputMBps MB/s" -ForegroundColor White
if ($whatIf) {
Write-Host '[WhatIf] No changes made.' -ForegroundColor Yellow
return
}
Write-Host "Deallocating VM: $vmName" -ForegroundColor Cyan
Stop-AzVM -ResourceGroupName $resourceGroupName -Name $vmName -Force | Out-Null
Write-Host 'Converting disk to Premium SSD v2...' -ForegroundColor Cyan
$diskUpdate = New-AzDiskUpdateConfig `
-SkuName PremiumV2_LRS `
-DiskIOPSReadWrite $provisionedIops `
-DiskMBpsReadWrite $provisionedThroughputMBps
Update-AzDisk `
-ResourceGroupName $resourceGroupName `
-DiskName $diskName `
-DiskUpdate $diskUpdate | Out-Null
Write-Host 'Monitoring conversion...' -ForegroundColor Cyan
do {
Start-Sleep -Seconds 15
$pct = (Get-AzDisk -ResourceGroupName $resourceGroupName -DiskName $diskName).CompletionPercent
Write-Host " Progress: $pct%" -ForegroundColor White
} while ($pct -lt 100)
Write-Host 'Conversion complete.' -ForegroundColor Green
Write-Host "Starting VM: $vmName" -ForegroundColor Cyan
Start-AzVM -ResourceGroupName $resourceGroupName -Name $vmName | Out-Null
Write-Host ''
Write-Host 'Done. Validate the workload and update your IaC.' -ForegroundColor Green
Write-Host " Disk : $diskName" -ForegroundColor White
Write-Host ' SKU : PremiumV2_LRS' -ForegroundColor White
Write-Host " IOPS : $provisionedIops" -ForegroundColor White
Write-Host " MB/s : $provisionedThroughputMBps" -ForegroundColor White
Run with -WhatIf first to confirm the parameters before the VM goes down. The disk keeps the same name and resource ID throughout. No disk swap, no data movement between resources.
The default values in the script are the free baseline: 3,000 IOPS and 125 MB/s. These are what you pay nothing above. If you set them to match the old Premium SSD tier without looking at what the workload actually uses, you carry the same over-provisioning across to the new SKU. Part 3 of this series covers how to pull utilization data from Azure Monitor and size these values correctly.
Monitoring during conversion
Disk performance is reduced while the conversion is running. The script above polls CompletionPercent every 15 seconds. You can also check it directly from the CLI if you need to query from outside the script:
az disk show \
--name $diskName \
--resource-group $resourceGroupName \
--query "[completionPercent]" \
--output tsv
Do not rotate or change customer-managed encryption keys while completionPercent is below 100. Do not restart the VM until the value reaches 100. Both of these are easy to overlook when you are working through a batch of disks under time pressure.
Bicep: updating an existing disk
If you are using Bicep to manage your disks, the in-place update is just a property change on the existing disk resource. The disk keeps the same name so Bicep treats it as an update to the existing resource rather than a new deployment.
param diskName string
param location string = resourceGroup().location
param diskIops int = 3000
param diskMbps int = 125
resource dataDisk 'Microsoft.Compute/disks@2022-07-02' = {
name : diskName
location : location
sku: {
name: 'PremiumV2_LRS'
}
properties: {
diskIOPSReadWrite : diskIops
diskMBpsReadWrite : diskMbps
}
}
Deploy after the VM is deallocated:
az deployment group create \
--resource-group <rg> \
--template-file disk.bicep \
--parameters diskName=<name> diskIops=3000 diskMbps=125
Restart the VM once the deployment confirms complete and completionPercent reaches 100.
Bicep: new workloads on Premium SSD v2
For greenfield deployments, the key thing to get right is zone alignment between the VM and the disk. Both must be in the same availability zone. Drive the zone value from a parameter at the deployment root and pass it to both resources so they always stay in sync.
param location string = resourceGroup().location
param zone string = '1'
param diskName string
param diskSizeGiB int = 256
param diskIops int = 3000
param diskThroughputMBps int = 125
resource premiumV2Disk 'Microsoft.Compute/disks@2023-10-02' = {
name : diskName
location : location
zones : [zone]
sku: {
name: 'PremiumV2_LRS'
}
properties: {
diskSizeGB : diskSizeGiB
creationData : {
createOption: 'Empty'
}
diskIOPSReadWrite : diskIops
diskMBpsReadWrite : diskThroughputMBps
}
}
Start conservatively on IOPS and throughput. You can increase them at any time without redeployment.
IaC update after migration
This step is non-negotiable. If your Terraform, Bicep, or ARM templates still define the disk as Premium_LRS after the migration, the next pipeline run will attempt to reconcile the live state back to what the code says. Depending on your pipeline behaviour, that can revert the disk type.
Update the IaC before the next pipeline run, not as a follow-up task. The migration is not complete until the code reflects the live state:
- Change
storage_account_typetoPremiumV2_LRSin Terraform (orsku.namein Bicep/ARM) - Add
disk_iops_read_writeanddisk_mbps_read_writewith the values you provisioned - Remove any
cachingarguments from disk attachment resources - Raise a pull request and peer-review before merging
What is coming in Part 3
In Part 3 we focus on what happens after migration: how to right-size provisioned IOPS and throughput using real utilization data from Azure Monitor, KQL queries for disk performance, and alert patterns that surface problems before the workload feels them.