Craig Forrester

The Pleasure of Finding Things Out

github linkedin email rss
Resize Azure Virtual Machine Disks with Azure PowerShell
August 1, 2018
7 minutes read

In this post, I’m going to walk through how to expand a managed OS disk on an Azure virtual machine with PowerShell. In the previous post in this series, we covered how to achieve this with Azure CLI in Bash, and in later posts, we will cover how to convert these commands into scripts.

Log In and Verify Subscription

Before we do anything, as always, we want to be certain we have PowerShell connected to Azure Resource Manager, and that we are on the correct subscription. So, we call Get-AzureRmContext and, treating it as an object, reference the name of the context Subscription, as follows:

(Get-AzureRmContext).Subscription.Name

Example output:

Visual Studio Enterprise

If you see no output, you are not connected to any Subscription. If you want to set up your own login function or script, you could always test this explicitly with if ((Get-AzureRmContext).Subscription.Name -eq $null) { echo "Not logged in." ; return $false } or something like it.

Deallocate the Virtual Machine

As with Azure CLI, in order to resize virtual hard disks, we have to deallocate the virtual machine first, because as of this writing resizing them while the VM is running is not possible.

Get-AzureRmVM -ResourceGroupName $rg -Name $vm | 
    Stop-AzureRmVM -Force -AsJob

Throughout the post I refer to variables instead of the literal names, like the above example. We will be able to convert this to a script later much more easily as a result, and it will allow you to use the examples on whatever specific instances in your environment with minimal change.

We are doing our work in PowerShell, so to use these variables names we simply assign them values, as in this example:

$rg = 'lab1-rg'
$vm = 'ws2016server1'

To verify our virtual machine is deallocated, we have a couple of options. If we used the -AsJob switch, like I did above, then all we need to do is pull that job up and check its status, like this:

Get-Job -Newest 1 | select Name,JobStateInfo

Example Output:

Name                                        JobStateInfo
----                                        ------------
Long Running Operation for 'Stop-AzureRmVM' Completed

Otherwise, we can use the Get-AzureRmVM cmdlet again, with the virtual machine name and the -Status switch, which will show us detailed status by returning what’s called an Instance View of the virtual machine:

Get-AzureRmVM -ResourceGroupName $rg -Name $vm -Status

Example output:


ResourceGroupName : lab-rg
Name              : ws2016server1
Disks[0]          :
  Name            : ws2016server1-disk-os
  Statuses[0]     :
    Code          : ProvisioningState/succeeded
    Level         : Info
    DisplayStatus : Provisioning succeeded
    Time          : <Date and Time>
Statuses[0]       :
  Code            : ProvisioningState/succeeded
  Level           : Info
  DisplayStatus   : Provisioning succeeded
  Time            : <Date and Time>
Statuses[1]       :
  Code            : PowerState/deallocated
  Level           : Info
  DisplayStatus   : VM deallocated

Note the PowerState under Statuses[1]. If we want to access this particular status by name later, in our script, PowerShell provides us a number of ways. Here’s one…

$vm_state = Get-AzureRmVM -ResourceGroupName $rg -Name $vm -Status
$vm_state.Statuses.Code -match 'Power'

Example Output:

PowerState/deallocated

We could then further extend our script functionality based on that PowerState status.

Get the Current Disk Size

Next we should determine the current size of the disk, so we can compare it against the size we want to expand it to. The first step is to locate the disk ID and assign it to a variable, with the following query:

$vm_current = Get-AzureRmVM -ResourceGroupName $rg -Name $vm

$disk_name = $vm_current.StorageProfile.OsDisk.Name

(We could have done this in one line, but it’s a bit easier to read this way.)

Once we have the OS disk ID assigned to $disk_id, we can use it to lookup the disk:

Get-AzureRmDisk $rg $disk_name | 
    select Name,DiskSizeGB,@{N='Tier';E={$_.Sku.Name}}

(If you’re wondering what the last bit is with the @{N='Tier'..., it’s just a way for us to map the Storage SKU Name into a custom column of our output object, instead of having $_.Sku.Name as the column header.)

Example output:

Name                  DiskSizeGB Tier
----                  ---------- ----
ws2016server1-disk-os 127        StandardLRS

This is good output because it confirms both the current size of the disk, and reminds us what storage tier it’s using, in case that’s a concern.

Aside: You may be wondering why we didn’t just look up the disk directly, by name. We could have. The reason we didn’t is that most of the time we need to expand a disk, we are doing so based on some request that refers to the virtual machine by name, not the disk. Additionally, in some environments disk names are “square” with the virtual machine names, and in others, they aren’t. So, it’s a good idea to start at the source and programmatically move to the destination.

Compare Current Disk Size to Requested Size

Next, we need to store the current disk size to compare it against the requested size:

$disk_size = (Get-AzureRmDisk $rg $disk_name).DiskSizeGB

We have the current disk size assigned to $disk_size now, so we can verify that the requested size is greater than the current size:

$disk_size_new = 150

if (-not ($disk_size_new -gt $disk_size)) {
    echo "ERROR: Current disk size $disk_size GB is equal to `
          or larger than the requested size of $disk_size_new GB."
} else {
    echo "Make it so."
}

This is pretty standard PowerShell stuff here — we’re just comparing two numbers. The -not indicates exactly that, so we’re check if the requested size is not greater than the current size. We could have written this with a less-than (-lt) and omitted the -not, but putting it this way, it reads more naturally left-to-right and says exactly what we want to do: “If not disk size new greater than disk size… Do this thing.” So, the first condition under the if is going to be used if the requested size is not larger than the existing. And of course, where I’ve got echo statements here, you can do whatever you want with the conditions — exit the script, write an error, send an alert, etc.

Another check we might want to make against the input is the maximum size. According to the documentation, we don’t want to go over 2048 GB (2 TB), so we can add a condition for that also, like this:

# For Maximum Virual Disk Size, refer to the following documentation:
# https://docs.microsoft.com/en-us/azure/virtual-machines/windows/expand-os-disk 
$disk_size_max = 2048

if (-not ($disk_size_new -gt $disk_size)) {
    echo "I'm sorry, Dave. I'm afraid I can't do that."
} elseif ($disk_size_new -gt $disk_size_max) {
    echo "Are you out of your Vulcan mind?!"
} else {
    echo "Make it so."
}

It’s a good idea to make these kinds of input validations where it makes sense, because you don’t want someone to put an extra digit on the request and end up with a 20 TB disk (if that were possible). The flip-side of that coin is that you have to be vigilant in keeping up with the limits — in this case, the maximum size. It’s best to put these kinds of fixed variables (constants, in programming terms) at the top of your script so that they can be easily seen and updated when necessary. It’s best to put a reference to the relevant documentation in a comment by this as well, so that you know where to go for the information.

Update the Disk Size

Now, without any further ado… let’s expand the disk:

$disk = Get-AzureRmDisk -ResourceGroupName $rg -DiskName $disk_name

$disk.DiskSizeGB = $disk_size_new

Update-AzureRmDisk -ResourceGroupName $rg `
    -Disk $disk -DiskName $disk_name

Example Output:

ResourceGroupName  : lab-rg
ManagedBy          : /subscriptions/X/resourceGroups/lab-rg/providers/Microsoft.Compute/virtualMachines/ws2016server1
Sku                : Microsoft.Azure.Management.Compute.Models.DiskSku
Zones              :
TimeCreated        : <Date and Time>
OsType             : Windows
CreationData       : Microsoft.Azure.Management.Compute.Models.CreationData
DiskSizeGB         : 150
EncryptionSettings :
ProvisioningState  : Succeeded
Id                 : /subscriptions/X/resourceGroups/lab-rg/providers/Microsoft.Compute/disks/ws2016server1-disk-os
Name               : ws2016server1-disk-os
Type               : Microsoft.Compute/disks
Location           : northcentralus
Tags               : {[environment, test], [stack, lab]}

Notice that the output of Update-AzureRmDisk shows us the DiskSizeGB (Here: 150), and the ProvisioningState (Here: Succeeded). To capture these and use them as verification, we can assign the output to a variable and then run some checks, as follows:

$disk_update_status = Update-AzureRmDisk -ResourceGroupName $rg `
                        -Disk $disk -DiskName $disk_name

if ($disk_update_status.ProvisioningState -eq 'Succeeded') {
    # Everything is OK
} else {
    # Report an error, take some action
}

Verify Final Disk Size

To manually verify our results, we simply get the disk status again:

Get-AzureRmDisk -ResourceGroupName $rg -DiskName $disk_name | 
    select Name,DiskSizeGB

Example Output:

Name                DiskSizeGB
----                ----------
ws2016test1-disk-os        150

Start Up the Virtual Machine

All that remains is to boot up the VM:

Start-AzureRmVM -ResourceGroupName $rg -Name $vm

Hopefully, this gives you another tool in your toolbox. In future posts, we’ll work through take these commands and converting them into a script that we can then use both interactively and in a toolchain.

Additional Reading


Back to posts