Explore Azure Resources with Resource Graph

Last weeks Ignite conference came with lots of new Azure announcements if you want to see them all make sure to check out my announcement summary. One of these announcements that seem to go somewhat under the radar was Azure Resource Graph, but I think this could be a handy tool for Azure administrators.

Resource graph as a new service which allows you to explore your Azure resources using a command line tool and a new query language. While it would be possible today to query resources directly using CLI or PowerShell, this is much slower than using the resource graph. CLI/PowerShell also does not allow us to do cross subscription queries, which Resource Graph does by default.

The query language in resource explorer is the Azure Data Explorer Kusto language (although not all methods are available).

Pre-Requisites

The resource graph can be used in both Azure CLI and Azure PowerShell, so you either need to have one of these installed, or you can use Azure Cloud Shell - Launch Cloud Shell

Either way, once you have one of the two running, you then need to enable and install Resource Graph (as it is in preview it is not available by default). You can do this using the commands below.

PowerShell

Install-Module -Name AzureRm.ResourceGraph

CLI

az extension add --name resource-graph

Basic Querying

To query the graph you use the “az graph query” command for CLI or “search-AzureRMGraph” command in PowerShell, and feed this command a query. As an example, lets just query to find the total number of Azure resources we have.

PowerShell

Search-AzureRmGraph -Query "summarize count()"

CLI

az graph query -q "summarize count ()"

This command then returns the result of the query, in this case, a simple count value.

[
  {
    "count_": 185
  }
]

Note that by default graph queries run across all your subscriptions, if you want to scope this to a specific subscription you can use the subscription parameter to scope this, this expects the subscription ID rather than the name.

PowerShell

Search-AzureRmGraph -Query "summarize count()" -subscription "xxxx-xxxx-xxxx-xxxx"

CLI

az graph query -q "summarize count ()" --subscriptions "xxxx-xxxx-xxxx-xxxx-xxxx"

Filters

While knowing how many resources we have is interesting, but it’s not terribly useful. We’ll expand on this and filter this command so we can see how many virtual machines we have. To do this, we add a “where” command to our query and have it look for items of type “Microsoft.Compute/virtualMachines”. Remember that what we are querying here are all ARM resources, so all the types we are used to when writing ARM templates are used here.

PowerShell

Search-AzureRmGraph -Query "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count()"

CLI

az graph query  -q "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count ()"

again, this returns an object with a count value:

[
  {
    "count_": 5
  }
]

We can expand on this query further by breaking this countdown by OS type. We need a field in the object that shows the OS in use, so if we didn’t know this already we can query a VM by name and take a look:

PowerShell

Search-AzureRmGraph -Query "where type =~ 'Microsoft.Compute/virtualMachines' and name == '<myVmName>' "

CLI

az graph query -q "where type =~ 'Microsoft.Compute/virtualMachines' and name == '<myVmName>' "

This returns a VM object, and we can look at all the properties of this. If you are familiar with ARM templates for VMs, you’ll already know that we can get the OS by looking at the oSDisks osType value.

 "storageProfile": {
        "dataDisks": [
          {
            "caching": "None",
            "createOption": "Attach",
            "lun": 0,
            "managedDisk": {
              "id": "/subscriptions/xxxxxx/resourceGroups/BSERIESDEMO/providers/Microsoft.Compute/disks/testdisk",
              "resourceGroup": "BSERIESDEMO"
            },
            "name": "testdisk"
          }
        ],
        "imageReference": {
          "offer": "WindowsServer",
          "publisher": "MicrosoftWindowsServer",
          "sku": "2016-Datacenter",
          "version": "latest"
        },
        "osDisk": {
          "caching": "ReadWrite",
          "createOption": "FromImage",
          "managedDisk": {
            "id": "/subscriptions/xxxxx/resourceGroups/BSERIESDEMO/providers/Microsoft.Compute/disks/BSeriesDemo_OsDisk_1_336623f815a647529a27ad91d79c131c",
            "resourceGroup": "BSERIESDEMO"
          },
          "name": "BSeriesDemo_OsDisk_1_336623f815a647529a27ad91d79c131c",
          "osType": "Windows"
        }
      },

Now we know the property to use we can amend our query to break down our count by OS type. The query language does expect the property to be a string, rather than JSON, so we need to use the “toString” operator provided by the query language.

PowerShell

search-AzureRmGraph -Query "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count() by tostring(properties.storageProfile.osDisk.osType)"

CLI

az graph query -q "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count() by tostring(properties.storageProfile.osDisk.osType)"

This query results in an object that has the count broken down by OS disk type.

[                                                       
  {                                                     
    "count_": 4,                                        
    "properties_storageProfile_osDisk_osType": "Linux"  
  },                                                    
  {                                                     
    "count_": 1,                                        
    "properties_storageProfile_osDisk_osType": "Windows"
  }                                                     
]                                                       

Functions

As mentioned above, the query language in use by Resource Graph is the Kusto language used in Data Explorer and Log Analytics, but only a subset of the commands. The full list of supported commands can be found here. For example, if we want to tidy up the names of the columns in our VM OS query above, we can use the “project” command to rename the columns:

PowerShell

search-AzureRmGraph -Query "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count() by tostring(properties.storageProfile.osDisk.osType) | project OS=properties_storageProfile_osDisk_osType, total=count_"

CLI

az graph query -q "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count() by tostring(properties.storageProfile.osDisk.osType) | project OS=properties_storageProfile_osDisk_osType, total=count_"

This query gives us the following result, with renamed columns.

[
  {
    "OS": "Linux",
    "total": 4
  },
  {
    "OS": "Windows",
    "total": 1
  }
]

We can also filter on missing data. For example, a useful query might be to find any public IP addresses that are not assigned to a resource (VM, VMSS, Load Balancer etc.). When attached to a resource this is indicated in the “ipConfiguration” parameter in the properties section, so we need to check if this is empty. Again we look at the query functions available to us. The isnotempty command returns false if the parameter is missing, so if we wrap that in the “not” function, to give us the inverse we can check if the resource is attached to anything.

Powershell

search-AzureRmGraph -Query "where type =~ 'Microsoft.Network/publicIPAddresses' and not(isnotempty(properties.ipConfiguration))"

CLI

az graph query -q "where type =~ 'Microsoft.Network/publicIPAddresses' and not(isnotempty(properties.ipConfiguration))"

Policy Query

One great use of Resource Graph is if you are looking to implement Azure policies, you can use a query to determine what the impact of your policy will be or to tailor your policy to your environment. For example, let’s say you wanted to create a policy to limit what VM SKUs you allowed to be deployed in your subscription. First, you could create a query that shows you the current breakdown of your VM’s by SKU.

PowerShell

search-AzureRmGraph -Query "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count() by tostring(properties.hardwareProfile.vmSize)| project SKU=properties_hardwareProfile_vmSize, total=count_"

CLI

az graph query -q "where type =~ 'Microsoft.Compute/virtualMachines' | summarize count() by tostring(properties.hardwareProfile.vmSize)| project SKU=properties_hardwareProfile_vmSize, total=count_"

This query returns a count broken down by VM Size.

[
  {
    "SKU": "Standard_DS1_v2",
    "total": 2
  },
  {
    "SKU": "Standard_B2s",
    "total": 1
  },
  {
    "SKU": "Standard_D2s_v3",
    "total": 2
  }
]

We then decided we want to prevent the creation of any DSv2 Machines, so we create a query to list all the existing DSv2 machines, so we can inform their owners that this size will be disallowed soon. We use the “matches regex” operator to search for any machine in the DSv2 SKU.

PowerShell

search-AzureRmGraph -Query "where type =~ 'Microsoft.Compute/virtualmachines' and properties.hardwareProfile.vmSize matches regex 'Standard_DS[0-9]*_v2'| project name"

CLI

az graph query -q "where type =~ 'Microsoft.Compute/virtualmachines' and properties.hardwareProfile.vmSize matches regex 'Standard_DS[0-9]*_v2'| project name"

Summary

Resource graph looks to be a handy tool, especially if you are looking to quickly understand what is in your Azure estate, look for anomalies or implement policy. We’re just scratching the surface here of what you can do with Resource Graph queries if you have any interesting or useful queries I’d love to see them.

You can also check out the Microsoft docs for more information.

####Image Attribution flicker_photos_lorenzodom_alltags_082206 flickr photo by brewbooks shared under a Creative Commons (BY-SA) license