Changing Azure disk SKUs using Azure Resource Graph and the Azure Python SDK

I was recently working with a customer who needed to make mass changes to Azure disks (we’re talking couple 100 disks). The disks were part of a demo environment that was very infrequently used, but when it was used it needed to perform well. When the demo was spun up, the disks needed to be running as premium disks, but when spun down, standard disks were fine. We settled on standard SSDs just in case they needed to spin up without having the option to make the change back to premium.

In terms of requirements, there were three requirements for the script:

  • Ability to mass change disks from premium to standard SSD
  • Ability to exclude certain VMs
  • Work on multiple subscriptions

I published the script itself to my GitHub repo if you ever need something similar. Let me walk you through it here.

Using the Azure Resource Graph to get the list of disk IDs to change

If you ever need to interact with more than one Azure resource, and potentially with hundreds, you will be lot happier if you leverage the Azure resource graph. The resource graph is an optimized way to query large amounts of Azure objects. It avoids you hitting ARM throttles, and is super fast in returning specific queries on your resources.

In our use case for the disks, we needed to get all premium disks attached to VMs that aren’t running (you can’t change disk SKU on a running VM) – and exclude a named list of VMs. I did this by executing two queries: first query to get the list the IDs of VMs to ignore (aka running VMs and static list.

resources |         
where type == "microsoft.compute/virtualmachines" |         
where name in {} or  parse_json(properties.extended.instanceView.powerState.code) != "PowerState/deallocated" |        
project id

Where the {} in the query get changed by python by the list of VMs to ignore. Then we can use those IDs to get a list of disks:

resources |
where type == "microsoft.compute/disks" |
where sku.tier == "Premium" |
where managedBy !in {}

Where in this query, the {} gets substituted by the list of IDs of the VMs that we need to ignore.

And that final list can be fed into a while loop of disks to change:

Changing disk SKUs in Python

Changing the disk SKU in Python took me a while to figure out, but the end code result is pretty straightforward. In essence I loop over the subscriptions and then change the SKUs of the disks in each subscription:

def resizeDisksToStandardSSD(diskIDs):
    #credentials = get_credentials()
    cred = get_azure_cli_credentials()
    newSKU = DiskSku(name="StandardSSD_LRS")
    for sub in subscriptionIds:
        compute_client = ComputeManagementClient(cred[0],sub)
        logging.warning("Changing level in subscription {}".format(sub))
        logging.warning("Resource group \t diskname")
        for disk in diskIDs:
            if (disk['id'].split('/')[2] == sub):
                rgName = disk['id'].split('/')[4]
                diskName = disk['id'].split('/')[8]
                logging.warning("{}\t{}".format(rgName,diskName))
                disk = compute_client.disks.get(rgName,diskName)
                disk.sku = newSKU
                async_disk_update = compute_client.disks.begin_create_or_update(rgName,diskName,disk)
                async_disk_update.wait()
    return True

I could do things a bit more optimized by sorting the disks per subscription rather than looping over the list of disks for each subscription, but since we won’t be running this often, I figure this is OK for now.

Summary

If you want to run this, check out the end-to-end script on Github. The biggest lessons learnt here is

Leave a Reply