ConvertTo-SPOT.ps1
Function ConvertTo-SPOT { Param ( # Set variables to your specifics $resourceGroup, $vmName, $subscriptionName, [switch]$force ) $asciiArt = @' ____ _ __ ______ _ ____ ____ ___ _____ | _ \ / \\ \ / / ___| | |_ ___ / ___|| _ \ / _ \_ _| | |_) / _ \\ V / | _ | __/ _ \ \___ \| |_) | | | || | | __/ ___ \| || |_| | | || (_) | ___) | __/| |_| || | |_| /_/ \_\_| \____| \__\___/ |____/|_| \___/ |_| Designed and managed by Aamir '@ Write-Output "`e[5;32m$asciiArt`e[0m"; $WarningPreference = 'SilentlyContinue' $randomNumber = Get-Random -Maximum 1000 $context = Set-AzContext $subscriptionName Write-Output "`e[36mSetting context to $($subscriptionName)" Try { # Get the details of the VM to be moved to the Availability Set $originalVM = Get-AzVM ` -ResourceGroupName $resourceGroup ` -Name $vmName -ErrorAction Stop } Catch { Write-Output "`e[31mIncorrect details provided. Please check the resource name." Write-Output "`e[31mMentioned VM $($vmName) not found." Start-Sleep -Seconds 10 } if ($originalVM) { Write-Output "`e[32mFound below VM in $($subscriptionName)" $originalVM.Id } try { # Check whether already logged-in # $token = (Get-AzAccessToken).token # $login = (az login --federated-token $token --tenant 'a33c6ac4-a52e-45c5-af07-b972df9bd004') # $login = (az login --identity) # for managed identity $setAzContext = (az account set --name $subscriptionName) $updateresource = (az resource update --resource-group $resourceGroup --name $vmName --resource-type virtualMachines --namespace Microsoft.Compute --set properties.storageProfile.osDisk.deleteOption=detach) if (((Get-AzVM -ResourceGroupName $resourceGroup -Name $vmName).SecurityProfile.SecurityType) -eq 'TrustedLaunch') { Write-Error 'Unfortunately, You cannot convert [TrustedLaunch] SecurityProfile VM to SPOT - these are not simple configuration items you can just change, these determine what hardware the server is bound to, how encryption is configured etc. SPOT conversion is only applicable for [STANDARD] SecurityProfile VMs' break; } else { New-AzVM ` -ResourceGroupName $resourceGroup ` -Location $originalVM.Location ` -VM $originalVM ` -DisableBginfoExtension -WhatIf Write-Warning 'VM eligible for conversion. Please use -Force with same command, If you are fine with the above results.' } } catch { Write-Output "`e[31mYou need to login. Or incorrect resource details provided." Start-Sleep -Seconds 10 if ($LASTEXITCODE -ne 0) { exit 1 } } # checking for dependencies $vm = Get-AzVM -ResourceGroupName $resourceGroup -VMName $vmName $extensionExist = ($vm.Extensions | Select-Object Publisher, VirtualMachineExtensionType, TypeHandlerVersion) if ($extensionExist) { Write-Output "`e[36mFound below VM extensions, make sure you get them installed after conversion." foreach ($currExtension in $extensionExist) { Write-Output "`e[33m|-- $($currExtension.VirtualMachineExtensionType)" } Write-Output "`e[36mMake sure you note the installed extensions, that you can configure later on the converted VM." ('-' * 75) Write-Output "`e[36mPlease re-run the automation with the -force switch to initiate the conversion, due to extension dependencies." ('-' * 75) } $isSPOT = (Get-AzVM -ResourceGroupName $resourceGroup -VMName $vmName | Select-Object Name, Location, ProvisioningState, Priority, @{Name = 'maxPrice'; Expression = { $_.BillingProfile.MaxPrice } }) $isSPOT if (((Get-AzVM -ResourceGroupName $resourceGroup -Name $vmName).SecurityProfile.SecurityType) -eq 'TrustedLaunch') { Write-Error 'Unfortunately, You cannot convert [TrustedLaunch] SecurityProfile VM to SPOT - these are not simple configuration items you can just change, these determine what hardware the server is bound to, how encryption is configured etc. SPOT conversion is only applicable for [STANDARD] SecurityProfile VMs' } else { if (($isSPOT.Priority -eq $null) -and ($isSPOT.ProvisioningState -eq 'Succeeded')) { if ($force) { Write-Output "`e[37mInitiating the conversion..." # Create the basic configuration for the replacement VM. $newVM = New-AzVMConfig ` -VMName $originalVM.Name ` -VMSize $originalVM.HardwareProfile.VmSize ` -Priority 'Spot' -MaxPrice -1 # You can set the maxPrice to -1 to indicate that the Azure Spot VM/VMSS should not be evicted for price reasons. Also, the default max price is -1 if it is not provided by you. Write-Verbose $newVM # Taking backup of OSdisks $osDiskid = $originalVM.StorageProfile.OsDisk.ManagedDisk.Id $osDiskName = $originalVM.StorageProfile.OsDisk.Name # Create Disk Snapshot $snapshot = New-AzSnapshotConfig -SourceUri $osDiskid -Location $originalVM.Location -CreateOption copy $newsnap = New-AzSnapshot ` -Snapshot $snapshot ` -SnapshotName "$($osDiskName)_$($randomNumber)" ` -ResourceGroupName $resourceGroup Write-Output "`e[32m`tStep 1 : Taking OS disk backup / snapshot." Write-Verbose $newsnap # Confgure OS Disk $attachOSDisk = Set-AzVMOSDisk ` -VM $newVM -CreateOption Attach ` -ManagedDiskId $originalVM.StorageProfile.OsDisk.ManagedDisk.Id ` -Name $originalVM.StorageProfile.OsDisk.Name if ($attachOSDisk) { Write-Verbose $attachOSDisk Write-Output "`e[32m`tStep 2 : OS Disk marked for attachment." } if ($originalVM.OSProfile.WindowsConfiguration) { $newVM.StorageProfile.OsDisk.OsType = 'Windows' Write-Output "`e[32m`tStep 3 : Identified WINDOWS host OS profile." } else { $newVM.StorageProfile.OsDisk.OsType = 'Linux' Write-Output "`e[32m`tStep 3 : Identified LINUX host OS profile." } if (($originalVM.StorageProfile.DataDisks).count -gt 0) { foreach ($disk in $originalVM.StorageProfile.DataDisks) { # Taking backup of disks # Get Current VM Data Disk metadata. $dataDiskid = $disk.ManagedDisk.Id $dataDiskName = ($disk.Name).ToLower() # Create Disk Snapshot $snapshot = New-AzSnapshotConfig -SourceUri $dataDiskid -Location $originalVM.Location -CreateOption copy $newsnap = New-AzSnapshot ` -Snapshot $snapshot ` -SnapshotName "$($dataDiskName)_$($randomNumber)" ` -ResourceGroupName $resourceGroup Write-Verbose $newsnap Write-Output "`e[32m`tStep 4 : Taking backup of Data disk - $($dataDiskName)." } } else { Write-Output "`e[32m`tStep 4 : No Data disk found for backup.[SKIPED]" } # Add NIC(s) and keep the same NIC as primary Write-Output "`e[32m`tStep 5 : Configuring NIC for the VM." foreach ($nic in $originalVM.NetworkProfile.NetworkInterfaces) { if ($nic.Primary -eq 'True') { $addExistingNIC = Add-AzVMNetworkInterface ` -VM $newVM ` -Id $nic.Id -Primary } else { $addExistingNIC = Add-AzVMNetworkInterface ` -VM $newVM ` -Id $nic.Id } } if ($originalVM.AvailabilitySetReference.Id) { #$newVM.AvailabilitySetReference=$originalVM.AvailabilitySetReference.Id Write-Output "`e[31mWarning: VM $originalVM.Name is in an availability set. Spot VMs cannot run in availability sets." } # Remove the original VM # Detach all dependent resources Write-Output "`e[37mDetaching attached resources for reusabelity." if (((Get-AzVM -ResourceGroupName $resourceGroup -Name $vmName).StorageProfile.Osdisk.DeleteOption) -eq 'Delete') { $vmConfig = Get-AzVM -ResourceGroupName $resourceGroup -Name $vmName $vmConfig.StorageProfile.OsDisk.DeleteOption = 'Detach' $vmConfig.StorageProfile.DataDisks | ForEach-Object { $_.DeleteOption = 'Detach' } $vmConfig.NetworkProfile.NetworkInterfaces | ForEach-Object { $_.DeleteOption = 'Detach' } $vmConfig | Update-AzVM } Write-Output "`e[37mOverwriting existing VM with SPOT conversion - $($originalVM.Name)." $removeExistingVM = Remove-AzVM -ResourceGroupName $resourceGroup -Name $vmName -Force Write-Verbose $removeExistingVM # # Creating disks from snapshots # $osDisk = New-AzDisk -DiskName $osDiskName -Disk ` # (New-AzDiskConfig -Location $originalVM.Location -CreateOption Copy ` # -SourceResourceId $newsnap.Id) ` # -ResourceGroupName $resourceGroup # Recreate the VM Write-Output "`e[32m`tStep 6 : Converting to NEW SPOT VM." $NewVMCreation = New-AzVM ` -ResourceGroupName $resourceGroup ` -Location $originalVM.Location ` -VM $newVM ` -DisableBginfoExtension if ($NewVMCreation) { Write-Verbose $NewVMCreation Write-Output "`e[32mSUCCESS : Completed conversion succesfully." } foreach ($attachDisk in $originalVM.StorageProfile.DataDisks) { $dataDiskid = ($attachDisk.ManagedDisk.Id).ToLower() $dataDiskName = ($attachDisk.Name).ToLower() Write-Output "`e[32m`tStep 7 : Attaching $($dataDiskName)" $vm = Get-AzVM -Name $vmName -ResourceGroupName $resourceGroup $vm = Add-AzVMDataDisk -VM $vm ` -Name $dataDiskName ` -ManagedDiskId $dataDiskid ` -Lun $attachDisk.Lun ` -CreateOption Attach Write-Output " --- Adding data disk $($dataDiskName) to $($vmName)" Update-AzVM -ResourceGroupName $resourceGroup -VM $vm } } } else { ('-' * 45) Write-Output " `e[35 $($vmName) is already running as SPOT." ('-' * 45) } } if ($NewVMCreation -and $extensionExist) { Write-Output 'To install extensions on the updated VM run the below command' foreach ($currentItemName in $extensionExist) { ('-' * 25) Write-Output "Set-AzVMExtension -Publisher $($currentItemName.Publisher) -ExtensionType $($currentItemName.Publisher) -Name $($currentItemName.VirtualMachineExtensionType) -ResourceGroupName $($resourceGroup) -VMName $($vmName)" } } } |