Skip to main content

Azure Managed Disks: Moving to Premium SSD v2 - Part 2

· 7 min read
Hasan Gural

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.

conversion

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.

Set IOPS and throughput from baseline data, not from the old tier

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_type to PremiumV2_LRS in Terraform (or sku.name in Bicep/ARM)
  • Add disk_iops_read_write and disk_mbps_read_write with the values you provisioned
  • Remove any caching arguments 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.

References