Skip to content

Instantly share code, notes, and snippets.

@caseywatson
Last active January 5, 2024 21:28
Show Gist options
  • Save caseywatson/74620282b57d73bc49617bf741220c85 to your computer and use it in GitHub Desktop.
Save caseywatson/74620282b57d73bc49617bf741220c85 to your computer and use it in GitHub Desktop.
PS script for moving Azure VMs into availability zones
param (
[string]$subscriptionId = "",
[string]$bootDiagnosticsResourceGroup = "",
[string]$bootDiagnosticsStorageAccountName = "",
[string]$vmssId = "",
[switch]$keep,
[Parameter(Mandatory = $true)][string]$resourceGroup,
[Parameter(Mandatory = $true)][string]$vmName,
[Parameter(Mandatory = $true)][Int32]$zone
)
# $ErrorActionPreference = 'Stop' # Stop the script if anything goes wrong
$resourcesToDelete = [System.Collections.ArrayList]@() # If the keep flag wasn't set, these resources will be deleted after successful script completion.
$snapshotSku = "Standard_LRS" # To be used with all disk snapshots regardless of disk SKU
Login-AzAccount
if ($subscriptionId) {
Set-AzContext -Subscription $subscriptionId
}
$sourceVM = Get-AzVM `
-ResourceGroupName $resourceGroup `
-Name $vmName
$location = $sourceVM.Location
Write-Host "Stopping VM $($sourceVM.Name)..."
Stop-AzVM `
-ResourceGroupName $resourceGroup `
-Name $vmName `
-Force
Write-Host "VM '$($sourceVM.Name)' stopped. Snapshotting and creating new disks in zone '$zone'..."
$osDiskName = $sourceVM.StorageProfile.OsDisk.Name
$osDiskSnapshotName = ($osDiskName + "-snap")
Write-Host "Snapshotting OS disk '$osDiskName': '$osDiskSnapshotName'..."
$osDisk = (Get-AzDisk `
-DiskName $sourceVM.StorageProfile.OsDisk.Name `
-ResourceGroupName $resourceGroup)
$osDiskSku = $osDisk.Sku.Name
$resourcesToDelete.Add($osDisk.Id) # Delete the original OS disk on successful completion
$osDiskSnapshotConfig = New-AzSnapshotConfig `
-SourceUri $sourceVM.StorageProfile.OsDisk.ManagedDisk.Id `
-Location $location `
-CreateOption Copy `
-SkuName $snapshotSku
$osDiskSnapshot = New-AzSnapshot `
-Snapshot $osDiskSnapshotConfig `
-SnapshotName $osDiskSnapshotName `
-ResourceGroupName $resourceGroup
$resourcesToDelete.Add($osDiskSnapshot.Id) # Delete the original OS disk snapshot on successful completion
# Create a new OS disk in the specified zone based on the OS disk snapshot just taken...
$newOsDiskName = ($osDiskName + "-zone-" + $zone)
Write-Host "Creating new OS disk '$newOsDiskName' from snapshot '$osDiskSnapshotName' in zone '$zone'..."
$newOsDiskConfig = New-AzDiskConfig `
-Location $osDiskSnapshot.Location `
-SourceResourceId $osDiskSnapshot.Id `
-CreateOption Copy `
-SkuName $osDiskSku `
-Zone $zone
$newOsDisk = New-AzDisk `
-Disk $newOsDiskConfig `
-ResourceGroupName $resourceGroup `
-DiskName $newOsDiskName `
Write-Host "New OS disk '$newOsDiskName' created in zone '$zone'."
foreach ($dataDisk in $sourceVM.StorageProfile.DataDisks) {
$dataDiskSnapshotName = ($dataDisk.Name + "-snap")
Write-Host "Snapshotting data disk '$($dataDisk.Name)': '$dataDiskSnapshotName'..."
$dataDiskResource = (Get-AzDisk `
-DiskName $dataDisk.Name `
-ResourceGroupName $resourceGroup)
$dataDiskSku = $dataDiskResource.Sku.Name
$dataDiskSnapshotConfig = New-AzSnapshotConfig `
-SourceUri $dataDisk.ManagedDisk.Id `
-Location $location `
-CreateOption Copy `
-SkuName $snapshotSku
$dataDiskSnapshot = New-AzSnapshot `
-Snapshot $dataDiskSnapshotConfig `
-SnapshotName $dataDiskSnapshotName `
-ResourceGroupName $resourceGroup
$resourcesToDelete.Add($dataDiskResource.Id) # Delete the original data disk on successful completion
$resourcesToDelete.Add($dataDiskSnapshot.Id) # Delete the original data disk snapshot on successful completion
$newDataDiskName = ($dataDisk.Name + "-zone-" + $zone)
Write-Host "Creating new data disk '$newDataDiskName' from snapshot '$dataDiskSnapshotName' in zone '$zone'..."
$newDataDiskConfig = New-AzDiskConfig `
-Location $dataDiskSnapshot.Location `
-SourceResourceId $dataDiskSnapshot.Id `
-CreateOption Copy `
-SkuName $dataDiskSku `
-Zone $zone
$newDataDisk = New-AzDisk `
-Disk $newDataDiskConfig `
-ResourceGroupName $resourceGroup `
-DiskName $newDataDiskName
Write-Host "New data disk '$newDataDiskName' created in zone '$zone'."
}
Write-Host "Configuring new VM '$($sourceVM.Name)'..."
$newVMConfig = If ($vmssId) `
{ New-AzVMConfig -VMName $sourceVM.Name -VMSize $sourceVM.HardwareProfile.VmSize -VmssId $vmssId -Zone $zone } Else `
{ New-AzVMConfig -VMName $sourceVM.Name -VMSize $sourceVM.HardwareProfile.VmSize -Zone $zone }
Write-Host "Connecting new OS disk '$newOsDiskName' to VM '$($sourceVM.Name)'..."
Set-AzVMOSDisk `
-VM $newVMConfig `
-CreateOption Attach `
-ManagedDiskId $newOsDisk.Id `
-Name $newOsDiskName `
-Windows
Write-Host "Connecting new data disks to VM '$($sourceVM.Name)'..."
foreach ($dataDisk in $sourceVM.StorageProfile.DataDisks) {
$newDataDiskName = ($dataDisk.Name + "-zone-" + $zone)
$newDataDisk = Get-AzDisk `
-ResourceGroupName $resourceGroup `
-DiskName $newDataDiskName
Add-AzVMDataDisk `
-VM $newVMConfig `
-Name $newDataDisk.Name `
-ManagedDiskId $newDataDisk.Id `
-Caching $dataDisk.Caching `
-Lun $dataDisk.Lun `
-DiskSizeInGB $dataDisk.DiskSizeGB `
-CreateOption Attach
}
Write-Host "Connecting network interface(s) to source VM '$($sourceVM.Name)'..."
foreach ($nic in $sourceVM.NetworkProfile.NetworkInterfaces) {
if ($nic.Primary -eq "True") {
Add-AzVMNetworkInterface -VM $newVMConfig -Id $nic.Id -Primary
}
else {
Add-AzVMNetworkInterface -VM $newVMConfig -Id $nic.Id
}
}
if ($bootDiagnosticsResourceGroup -and $bootDiagnosticsStorageAccountName) {
Write-Host "Configuring VM '$($sourceVM.Name)' to use external boot diagnostics storage account '$bootDiagnosticsStorageAccountName'..."
Set-AzVMBootDiagnostic `
-VM $newVMConfig `
-Enable `
-ResourceGroupName $bootDiagnosticsResourceGroup `
-StorageAccountName $bootDiagnosticsStorageAccountName
}
Write-Host "Deleting original VM '$($sourceVM.Name)'..."
Remove-AzVM `
-ResourceGroupName $resourceGroup `
-Name $sourceVM.Name `
-Force
Write-Host "Starting VM '$($sourceVM.Name)' in zone '$zone'..."
$stopCreateVmLoop = $false
[int]$createVmLoopRetryCt = 0
do {
try {
New-AzVM `
-ResourceGroupName $resourceGroup `
-Location $location `
-VM $newVMConfig `
-DisableBginfoExtension
Write-Host "New VM '$($sourceVM.Name)' in zone '$zone' created."
$stopCreateVmLoop = $true
}
catch {
if ($createVmLoopRetryCt -gt 3) {
Write-Host "Tried to create VM '$($sourceVM.Name)' 3 times and failed. Check activity log for more details."
$stopCreateVmLoop = $true
} else {
$createVmLoopSleepFor = [Math]::Pow($createVmLoopRetryCt, 2)
Write-Host "Failed to create new VM '$($sourceVM.Name)'. Trying again in '$($createVmLoopSleepFor)' seconds..."
Start-Sleep -Seconds $createVmLoopSleepFor
$createVmLoopRetryCt++
}
}
}
while ($stopCreateVmLoop -eq $false)
if (-not $keep) {
Write-Host "Cleaning up resources..."
foreach ($resourceToDelete in $resourcesToDelete) {
Write-Host "Removing resource '$resourceToDelete'..."
Remove-AzResource `
-ResourceId $resourceToDelete `
-Force
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment