Last active
January 5, 2024 21:28
-
-
Save caseywatson/74620282b57d73bc49617bf741220c85 to your computer and use it in GitHub Desktop.
PS script for moving Azure VMs into availability zones
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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