Resize Azure VMs in bulk


If you need to do a bulk update of VM sizes from one type to another to take advantage of new technology being released, this script should help you out.

This script will work for Resource Manager VMs, and NOT Classic VMs.
It targets VMs that are deallocated or have failed deployments only.
VMs in a Failed deployment state will automatically start when re-sized.

I've added a select grid at the end to let you toggle the state of the VMs that have been changed.
i.e Selected VMs will be Started if Deallocated and Stopped if in any other state

I've preloaded the SizeMapping table with the mapping to move from Dv2 Promo sizes to their equivilent Dv3 and Ev3 sizes.
You can change this as to your requirements, just set the Old and New elements for each record.
Make sure only select ONE subscription from the Select Grid.

#If you want to stop running VMs, Resize and restart again, set this to $true
$ForceStop = $false
#Prompt to confirm shutdown for each VM
$StopConfirm = $true
#Filter on VM name to restrict the list of VMs returned
$VMFilter = "*"

$AvailSubscriptions = Get-AzureRmSubscription

$ErrorActionPreference = "Stop"
$SelectedSubscription = $AvailSubscriptions | select Name, Id | Out-GridView -Title "Select ONE (only) Subscription" -PassThru 

$SubscriptionGUID = $SelectedSubscription.Id

Select-AzureRmSubscription -Subscription $SubscriptionGUID

#Update this table if you have different mapping size requirements from old to new
$SizeMapping = @()
$SizeMapping += @{Old='Standard_D2_v2_Promo';New='Standard_D2_v3'}
$SizeMapping += @{Old='Standard_D3_v2_Promo';New='Standard_D4_v3'}
$SizeMapping += @{Old='Standard_D4_v2_Promo';New='Standard_D8_v3'}
$SizeMapping += @{Old='Standard_D5_v2_Promo';New='Standard_D16_v3'}
$SizeMapping += @{Old='Standard_D11_v2_Promo';New='Standard_E2_v3'}
$SizeMapping += @{Old='Standard_D12_v2_Promo';New='Standard_E4_v3'}
$SizeMapping += @{Old='Standard_D13_v2_Promo';New='Standard_E8_v3'}
$SizeMapping += @{Old='Standard_D14_v2_Promo';New='Standard_E16_v3'}
$SizeMapping += @{Old='Standard_DS2_v2_Promo';New='Standard_D2s_v3'}
$SizeMapping += @{Old='Standard_DS3_v2_Promo';New='Standard_D4s_v3'}
$SizeMapping += @{Old='Standard_DS4_v2_Promo';New='Standard_D8s_v3'}
$SizeMapping += @{Old='Standard_DS5_v2_Promo';New='Standard_D16s_v3'}
$SizeMapping += @{Old='Standard_DS11_v2_Promo';New='Standard_E2s_v3'}
$SizeMapping += @{Old='Standard_DS12_v2_Promo';New='Standard_E4s_v3'}
$SizeMapping += @{Old='Standard_DS13_v2_Promo';New='Standard_E8s_v3'}
$SizeMapping += @{Old='Standard_DS14_v2_Promo';New='Standard_E16s_v3'}

#You can use a $VMFilter, set above, to limit the script to only add filtered VM names to the array
$VMs = Get-AzureRmVM | Where-Object {$_.Name -like $VMFilter}

$AlteredVMs = @()
foreach ($VM in $VMs) {
    if ($VM.HardwareProfile.VmSize -in $SizeMapping.Old) {
        $Status = Get-AzureRmVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name -Status
        $NewSize = $SizeMapping.New[$SizeMapping.Old.IndexOf($VM.HardwareProfile.VmSize)]

        #If the VM is deallocated, just resize it.        
        if ($Status.Statuses[1].DisplayStatus -eq "VM deallocated") {
            
            write-output "Updating VM: $($VM.Name) from $($VM.HardwareProfile.VmSize) to $($NewSize)"

            $VM.HardwareProfile.VmSize = $NewSize
        
            Update-AzureRmVM -VM $VM -ResourceGroupName $VM.ResourceGroupName -Verbose
        
            $AlteredVM = new-object psobject -Property @{
                Name = $VM.Name
                ResourceGroup = $VM.ResourceGroupName
                Location = $VM.Location
                ResourceID = $VM.Id
                SubscriptionID = $SubscriptionGUID
                OldSize = 
                OldProvisioningStatus = $Status.Statuses[0].DisplayStatus
                OldRunStatus = $Status.Statuses[1].DisplayStatus
                NewProvisioningStatus = $null
                NewRunStatus = $null
            }
            $AlteredVMs += $AlteredVM
        }

        #If the VM has failed provisioning, try and stop the VM firstso it clears the allocation error and doesn't start automatically after changing size
        if ($Status.Statuses[0].DisplayStatus -eq "Provisioning failed") {
            
            $StopAction = $false
            $TryCount = 0
            $ErrorActionPreference = "Stop"
            do {
                try {
                    $TryCount += 1
                    Write-Output "Stopping VM: $($VM.Name)"
                    Stop-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -Force
                    $StopAction=$true
                } catch {
                    sleep -10
                    $StopAction = $false
                }
            } until ($StopAction -eq $true -or $TryCount -ge 5)
            $ErrorActionPreference = "Continue"

            write-output "Updating VM: $($VM.Name) from $($VM.HardwareProfile.VmSize) to $($NewSize)"
            $VM.HardwareProfile.VmSize = $NewSize
        
            Update-AzureRmVM -VM $VM -ResourceGroupName $VM.ResourceGroupName -Verbose
        
            $AlteredVM = new-object psobject -Property @{
                Name = $VM.Name
                ResourceGroup = $VM.ResourceGroupName
                Location = $VM.Location
                ResourceID = $VM.Id
                SubscriptionID = $SubscriptionGUID
                OldProvisioningStatus = $Status.Statuses[0].DisplayStatus
                OldRunStatus = $Status.Statuses[1].DisplayStatus
                NewProvisioningStatus = $null
                NewRunStatus = $null
            }
            $AlteredVMs += $AlteredVM

        }

        #If you have ForceStop set to $true and the VM is running
        if ($Status.Statuses[1].DisplayStatus -eq "VM running" -and $ForceStop) {
            
            if ($StopConfirm) {
                $ConfirmStop = Read-Host -Prompt "Please type 'YES' to confirm you want to stop VM $($VM.Name)"
                if ($ConfirmStop -eq "YES") {
                    $StopAction = $false
                    $TryCount = 0
                    $ErrorActionPreference = "Stop"
                    do {
                        try {
                            $TryCount += 1
                            Write-Output "Stopping VM: $($VM.Name)"
                            Stop-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -Force
                            $StopAction=$true
                        } catch {
                            sleep -10
                            $StopAction = $false
                        }
                    } until ($StopAction -eq $true -or $TryCount -ge 5)
                    $ErrorActionPreference = "Continue"

                    write-output "Updating VM: $($VM.Name) from $($VM.HardwareProfile.VmSize) to $($NewSize)"
                    $VM.HardwareProfile.VmSize = $NewSize
        
                    Update-AzureRmVM -VM $VM -ResourceGroupName $VM.ResourceGroupName -Verbose
                    
                    Write-Output "Starting VM: $($VM.Name) after resize"
                    Start-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName

                    $AlteredVM = new-object psobject -Property @{
                        Name = $VM.Name
                        ResourceGroup = $VM.ResourceGroupName
                        Location = $VM.Location
                        ResourceID = $VM.Id
                        SubscriptionID = $SubscriptionGUID
                        OldProvisioningStatus = $Status.Statuses[0].DisplayStatus
                        OldRunStatus = $Status.Statuses[1].DisplayStatus
                        NewProvisioningStatus = $null
                        NewRunStatus = $null
                    }
                    $AlteredVMs += $AlteredVM
                }
            }
        }
    }
}

$CurrentVMState = @()

foreach ($AlteredVM in $AlteredVMs) {
    $Status = Get-AzureRmVM -ResourceGroupName $AlteredVM.ResourceGroup -Name $AlteredVM.Name -Status
    $AlteredVM.NewProvisioningStatus = $Status.Statuses[0].DisplayStatus
    $AlteredVM.NewRunStatus = $Status.Statuses[1].DisplayStatus
    $CurrentVMState += $AlteredVM
}
$ToggleStates = $null
if ($CurrentVMState -ne $null) {
    $ToggleStates = $CurrentVMState | select Name, ResourceGroup, NewRunStatus | Out-GridView -Title "Select VMs to toggle state (Running to Deallocated; Deallocated to Running); Click Cancel to change none" -PassThru
    foreach ($ToggleState in $ToggleStates) {
        if ($ToggleState.NewRunStatus -eq "VM deallocated") {
            Start-AzureRmVM -ResourceGroupName $ToggleState.ResourceGroup -Name $ToggleState.Name
        } else {
            Stop-AzureRmVM -ResourceGroupName $ToggleState.ResourceGroup -Name $ToggleState.Name -Force
        }
    }
}



Comments (0)

Skip to main content