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
- Documentation: Expand virtual hard disks on a Linux VM in Azure
- Documentation: Expand the OS drive of a Windows VM in an Azure
- Documentation: Virtual Machines - Instance View (Azure Compute)