| title | Advanced query samples |
|---|---|
| description | Use Azure Resource Graph to run some advanced queries, including working with columns, listing tags used, and matching resources with regular expressions. |
| ms.date | 06/15/2022 |
| ms.topic | sample |
| ms.custom | devx-track-azurepowershell, devx-track-azurecli |
| ms.author | daphnema |
| author | daphnemamsft |
The first step to understanding queries with Azure Resource Graph is a basic understanding of the Query Language. If you aren't already familiar with Azure Data Explorer, it's recommended to review the basics to understand how to compose requests for the resources you're looking for.
We'll walk through the following advanced queries:
- Show API version for each resource type
- Get virtual machine scale set capacity and size
- Remove columns from results
- List all tag names
- Virtual machines matched by regex
- List Azure Cosmos DB with specific write locations
- Key vaults with subscription name
- List SQL Databases and their elastic pools
- List virtual machines with their network interface and public IP
- List all extensions installed on a virtual machine
- Find storage accounts with a specific tag on the resource group
- Combine results from two queries into a single result
- Get virtual networks and subnets of network interfaces
- Summarize virtual machine by the power states extended property
If you don't have an Azure subscription, create a free account before you begin.
Azure CLI (through an extension) and Azure PowerShell (through a module) support Azure Resource Graph. Before running any of the following queries, check that your environment is ready. See Azure CLI and Azure PowerShell for steps to install and validate your shell environment of choice.
Resource Graph primarily uses the most recent non-preview version of a Resource Provider API to
GET resource properties during an update. In some cases, the API version used has been overridden
to provide more current or widely used properties in the results. The following query details the
API version used for gathering properties on each resource type:
Resources
| distinct type, apiVersion
| where isnotnull(apiVersion)
| order by type ascaz graph query -q "Resources | distinct type, apiVersion | where isnotnull(apiVersion) | order by type asc"
Search-AzGraph -Query "Resources | distinct type, apiVersion | where isnotnull(apiVersion) | order by type asc"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Microsoft Azure operated by 21Vianet portal: portal.azure.cn
This query looks for virtual machine scale set resources and gets various details including the
virtual machine size and the capacity of the scale set. The query uses the toint() function to
cast the capacity to a number so that it can be sorted. Finally, the columns are renamed into custom
named properties.
Resources
| where type=~ 'microsoft.compute/virtualmachinescalesets'
| where name contains 'contoso'
| project subscriptionId, name, location, resourceGroup, Capacity = toint(sku.capacity), Tier = sku.name
| order by Capacity descaz graph query -q "Resources | where type=~ 'microsoft.compute/virtualmachinescalesets' | where name contains 'contoso' | project subscriptionId, name, location, resourceGroup, Capacity = toint(sku.capacity), Tier = sku.name | order by Capacity desc"
Search-AzGraph -Query "Resources | where type=~ 'microsoft.compute/virtualmachinescalesets' | where name contains 'contoso' | project subscriptionId, name, location, resourceGroup, Capacity = toint(sku.capacity), Tier = sku.name | order by Capacity desc"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
The following query uses summarize to count resources by subscription, join to combine it with
subscription details from ResourceContainers table, then project-away to remove some of the
columns.
Resources
| summarize resourceCount=count() by subscriptionId
| join (ResourceContainers | where type=='microsoft.resources/subscriptions' | project SubName=name, subscriptionId) on subscriptionId
| project-away subscriptionId, subscriptionId1az graph query -q "Resources | summarize resourceCount=count() by subscriptionId | join (ResourceContainers | where type=='microsoft.resources/subscriptions' | project SubName=name, subscriptionId) on subscriptionId| project-away subscriptionId, subscriptionId1"
Search-AzGraph -Query "Resources | summarize resourceCount=count() by subscriptionId | join (ResourceContainers | where type=='microsoft.resources/subscriptions' | project SubName=name, subscriptionId) on subscriptionId| project-away subscriptionId, subscriptionId1"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
This query starts with the tag and builds a JSON object listing all unique tag names and their corresponding types.
Resources
| project tags
| summarize buildschema(tags)az graph query -q "Resources | project tags | summarize buildschema(tags)"
Search-AzGraph -Query "Resources | project tags | summarize buildschema(tags)"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
This query looks for virtual machines that match a regular expression
(known as regex). The matches regex @ allows us to define the regex to match, which is ^Contoso(.*)[0-9]+$.
That regex definition is explained as:
^- Match must start at the beginning of the string.Contoso- The case-sensitive string.(.*)- A subexpression match:.- Matches any single character (except a new line).*- Matches previous element zero or more times.
[0-9]- Character group match for numbers 0 through 9.+- Matches previous element one or more times.$- Match of the previous element must occur at the end of the string.
After matching by name, the query projects the name and orders by name ascending.
Resources
| where type =~ 'microsoft.compute/virtualmachines' and name matches regex @'^Contoso(.*)[0-9]+$'
| project name
| order by name ascaz graph query -q "Resources | where type =~ 'microsoft.compute/virtualmachines' and name matches regex @'^Contoso(.*)[0-9]+\$' | project name | order by name asc"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.compute/virtualmachines' and name matches regex @'^Contoso(.*)[0-9]+$' | project name | order by name asc"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
The following query limits to Azure Cosmos DB resources, uses mv-expand to expand the property bag
for properties.writeLocations, then project specific fields and limit the results further to
properties.writeLocations.locationName values matching either 'East US' or 'West US'.
Resources
| where type =~ 'microsoft.documentdb/databaseaccounts'
| project id, name, writeLocations = (properties.writeLocations)
| mv-expand writeLocations
| project id, name, writeLocation = tostring(writeLocations.locationName)
| where writeLocation in ('East US', 'West US')
| summarize by id, nameaz graph query -q "Resources | where type =~ 'microsoft.documentdb/databaseaccounts' | project id, name, writeLocations = (properties.writeLocations) | mv-expand writeLocations | project id, name, writeLocation = tostring(writeLocations.locationName) | where writeLocation in ('East US', 'West US') | summarize by id, name"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.documentdb/databaseaccounts' | project id, name, writeLocations = (properties.writeLocations) | mv-expand writeLocations | project id, name, writeLocation = tostring(writeLocations.locationName) | where writeLocation in ('East US', 'West US') | summarize by id, name"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
The following query shows a complex use of join with kind as leftouter. The query limits the
joined table to subscriptions resources and with project to include only the original field
subscriptionId and the name field renamed to SubName. The field rename avoids join adding it
as name1 since the field already exists in resources. The original table is filtered with
where and the following project includes columns from both tables. The query result is all key
vaults displaying type, the name of the key vault, and the name of the subscription it's in.
Resources
| join kind=leftouter (ResourceContainers | where type=='microsoft.resources/subscriptions' | project SubName=name, subscriptionId) on subscriptionId
| where type == 'microsoft.keyvault/vaults'
| project type, name, SubNameaz graph query -q "Resources | join kind=leftouter (ResourceContainers | where type=='microsoft.resources/subscriptions' | project SubName=name, subscriptionId) on subscriptionId | where type == 'microsoft.keyvault/vaults' | project type, name, SubName"
Search-AzGraph -Query "Resources | join kind=leftouter (ResourceContainers | where type=='microsoft.resources/subscriptions' | project SubName=name, subscriptionId) on subscriptionId | where type == 'microsoft.keyvault/vaults' | project type, name, SubName"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
The following query uses leftouter join to bring together SQL Database resources and their
related elastic pools, if they've any.
Resources
| where type =~ 'microsoft.sql/servers/databases'
| project databaseId = id, databaseName = name, elasticPoolId = tolower(tostring(properties.elasticPoolId))
| join kind=leftouter (
Resources
| where type =~ 'microsoft.sql/servers/elasticpools'
| project elasticPoolId = tolower(id), elasticPoolName = name, elasticPoolState = properties.state)
on elasticPoolId
| project-away elasticPoolId1az graph query -q "Resources | where type =~ 'microsoft.sql/servers/databases' | project databaseId = id, databaseName = name, elasticPoolId = tolower(tostring(properties.elasticPoolId)) | join kind=leftouter ( Resources | where type =~ 'microsoft.sql/servers/elasticpools' | project elasticPoolId = tolower(id), elasticPoolName = name, elasticPoolState = properties.state) on elasticPoolId | project-away elasticPoolId1"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.sql/servers/databases' | project databaseId = id, databaseName = name, elasticPoolId = tolower(tostring(properties.elasticPoolId)) | join kind=leftouter ( Resources | where type =~ 'microsoft.sql/servers/elasticpools' | project elasticPoolId = tolower(id), elasticPoolName = name, elasticPoolState = properties.state) on elasticPoolId | project-away elasticPoolId1"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
This query uses two leftouter join commands to bring together virtual machines created with
the Resource Manager deployment model, their related network interfaces, and any public IP address
related to those network interfaces.
Resources
| where type =~ 'microsoft.compute/virtualmachines'
| extend nics=array_length(properties.networkProfile.networkInterfaces)
| mv-expand nic=properties.networkProfile.networkInterfaces
| where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic)
| project vmId = id, vmName = name, vmSize=tostring(properties.hardwareProfile.vmSize), nicId = tostring(nic.id)
| join kind=leftouter (
Resources
| where type =~ 'microsoft.network/networkinterfaces'
| extend ipConfigsCount=array_length(properties.ipConfigurations)
| mv-expand ipconfig=properties.ipConfigurations
| where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true'
| project nicId = id, publicIpId = tostring(ipconfig.properties.publicIPAddress.id))
on nicId
| project-away nicId1
| summarize by vmId, vmName, vmSize, nicId, publicIpId
| join kind=leftouter (
Resources
| where type =~ 'microsoft.network/publicipaddresses'
| project publicIpId = id, publicIpAddress = properties.ipAddress)
on publicIpId
| project-away publicIpId1az graph query -q "Resources | where type =~ 'microsoft.compute/virtualmachines' | extend nics=array_length(properties.networkProfile.networkInterfaces) | mv-expand nic=properties.networkProfile.networkInterfaces | where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic) | project vmId = id, vmName = name, vmSize=tostring(properties.hardwareProfile.vmSize), nicId = tostring(nic.id) | join kind=leftouter ( Resources | where type =~ 'microsoft.network/networkinterfaces' | extend ipConfigsCount=array_length(properties.ipConfigurations) | mv-expand ipconfig=properties.ipConfigurations | where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true' | project nicId = id, publicIpId = tostring(ipconfig.properties.publicIPAddress.id)) on nicId | project-away nicId1 | summarize by vmId, vmName, vmSize, nicId, publicIpId | join kind=leftouter ( Resources | where type =~ 'microsoft.network/publicipaddresses' | project publicIpId = id, publicIpAddress = properties.ipAddress) on publicIpId | project-away publicIpId1"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.compute/virtualmachines' | extend nics=array_length(properties.networkProfile.networkInterfaces) | mv-expand nic=properties.networkProfile.networkInterfaces | where nics == 1 or nic.properties.primary =~ 'true' or isempty(nic) | project vmId = id, vmName = name, vmSize=tostring(properties.hardwareProfile.vmSize), nicId = tostring(nic.id) | join kind=leftouter ( Resources | where type =~ 'microsoft.network/networkinterfaces' | extend ipConfigsCount=array_length(properties.ipConfigurations) | mv-expand ipconfig=properties.ipConfigurations | where ipConfigsCount == 1 or ipconfig.properties.primary =~ 'true' | project nicId = id, publicIpId = tostring(ipconfig.properties.publicIPAddress.id)) on nicId | project-away nicId1 | summarize by vmId, vmName, vmSize, nicId, publicIpId | join kind=leftouter ( Resources | where type =~ 'microsoft.network/publicipaddresses' | project publicIpId = id, publicIpAddress = properties.ipAddress) on publicIpId | project-away publicIpId1"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
First, this query uses extend on the virtual machines resource type to get the ID in uppercase
(toupper()) the ID, get the operating system name and type, and get the virtual machine size.
Getting the resource ID in upper case is a good way to prepare to join to another property. Then,
the query uses join with kind as leftouter to get virtual machine extensions by matching an
upper cased substring of the extension ID. The portion of the ID before
"/extensions/<ExtensionName>" is the same format as the virtual machines ID, so we use this
property for the join. summarize is then used with make_list on the name of the virtual
machine extension to combine the name of each extension where id, OSName, OSType, and VMSize
are the same into a single array property. Lastly, we order by the lower cased OSName with
asc. By default, order by is descending.
Resources
| where type == 'microsoft.compute/virtualmachines'
| extend
JoinID = toupper(id),
OSName = tostring(properties.osProfile.computerName),
OSType = tostring(properties.storageProfile.osDisk.osType),
VMSize = tostring(properties.hardwareProfile.vmSize)
| join kind=leftouter(
Resources
| where type == 'microsoft.compute/virtualmachines/extensions'
| extend
VMId = toupper(substring(id, 0, indexof(id, '/extensions'))),
ExtensionName = name
) on $left.JoinID == $right.VMId
| summarize Extensions = make_list(ExtensionName) by id, OSName, OSType, VMSize
| order by tolower(OSName) ascaz graph query -q "Resources | where type == 'microsoft.compute/virtualmachines' | extend JoinID = toupper(id), OSName = tostring(properties.osProfile.computerName), OSType = tostring(properties.storageProfile.osDisk.osType), VMSize = tostring(properties.hardwareProfile.vmSize) | join kind=leftouter( Resources | where type == 'microsoft.compute/virtualmachines/extensions' | extend VMId = toupper(substring(id, 0, indexof(id, '/extensions'))), ExtensionName = name ) on \$left.JoinID == \$right.VMId | summarize Extensions = make_list(ExtensionName) by id, OSName, OSType, VMSize | order by tolower(OSName) asc"
Search-AzGraph -Query "Resources | where type == 'microsoft.compute/virtualmachines' | extend JoinID = toupper(id), OSName = tostring(properties.osProfile.computerName), OSType = tostring(properties.storageProfile.osDisk.osType), VMSize = tostring(properties.hardwareProfile.vmSize) | join kind=leftouter( Resources | where type == 'microsoft.compute/virtualmachines/extensions' | extend VMId = toupper(substring(id, 0, indexof(id, '/extensions'))), ExtensionName = name ) on `$left.JoinID == `$right.VMId | summarize Extensions = make_list(ExtensionName) by id, OSName, OSType, VMSize | order by tolower(OSName) asc"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
The following query uses an inner join to connect storage accounts with resource groups that
have a specified case-sensitive tag name and tag value.
Resources
| where type =~ 'microsoft.storage/storageaccounts'
| join kind=inner (
ResourceContainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| where tags['Key1'] =~ 'Value1'
| project subscriptionId, resourceGroup)
on subscriptionId, resourceGroup
| project-away subscriptionId1, resourceGroup1az graph query -q "Resources | where type =~ 'microsoft.storage/storageaccounts' | join kind=inner ( ResourceContainers | where type =~ 'microsoft.resources/subscriptions/resourcegroups' | where tags['Key1'] =~ 'Value1' | project subscriptionId, resourceGroup) on subscriptionId, resourceGroup | project-away subscriptionId1, resourceGroup1"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.storage/storageaccounts' | join kind=inner ( ResourceContainers | where type =~ 'microsoft.resources/subscriptions/resourcegroups' | where tags['Key1'] =~ 'Value1' | project subscriptionId, resourceGroup) on subscriptionId, resourceGroup | project-away subscriptionId1, resourceGroup1"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
If it's necessary to look for a case insensitive tag name and tag value, use mv-expand with the
bagexpansion parameter. This query uses more quota than the previous query, so use mv-expand
only if necessary.
Resources
| where type =~ 'microsoft.storage/storageaccounts'
| join kind=inner (
ResourceContainers
| where type =~ 'microsoft.resources/subscriptions/resourcegroups'
| mv-expand bagexpansion=array tags
| where isnotempty(tags)
| where tags[0] =~ 'key1' and tags[1] =~ 'value1'
| project subscriptionId, resourceGroup)
on subscriptionId, resourceGroup
| project-away subscriptionId1, resourceGroup1az graph query -q "Resources | where type =~ 'microsoft.storage/storageaccounts' | join kind=inner ( ResourceContainers | where type =~ 'microsoft.resources/subscriptions/resourcegroups' | mv-expand bagexpansion=array tags | where isnotempty(tags) | where tags[0] =~ 'key1' and tags[1] =~ 'value1' | project subscriptionId, resourceGroup) on subscriptionId, resourceGroup | project-away subscriptionId1, resourceGroup1"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.storage/storageaccounts' | join kind=inner ( ResourceContainers | where type =~ 'microsoft.resources/subscriptions/resourcegroups' | mv-expand bagexpansion=array tags | where isnotempty(tags) | where tags[0] =~ 'key1' and tags[1] =~ 'value1' | project subscriptionId, resourceGroup) on subscriptionId, resourceGroup | project-away subscriptionId1, resourceGroup1"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
The following query uses union to get results from the ResourceContainers table and add them to
results from the Resources table.
ResourceContainers
| where type=='microsoft.resources/subscriptions/resourcegroups' | project name, type | limit 5
| union (Resources | project name, type | limit 5)az graph query -q "ResourceContainers | where type=='microsoft.resources/subscriptions/resourcegroups' | project name, type | limit 5 | union (Resources | project name, type | limit 5)"
Search-AzGraph -Query "ResourceContainers | where type=='microsoft.resources/subscriptions/resourcegroups' | project name, type | limit 5 | union (Resources | project name, type | limit 5)"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
Use a regular expression parse to get the virtual network and subnet names from the resource ID
property. While parse enables getting data from a complex field, it's optimal to access properties
directly if they exist instead of using parse.
Resources
| where type =~ 'microsoft.network/networkinterfaces'
| project id, ipConfigurations = properties.ipConfigurations
| mvexpand ipConfigurations
| project id, subnetId = tostring(ipConfigurations.properties.subnet.id)
| parse kind=regex subnetId with '/virtualNetworks/' virtualNetwork '/subnets/' subnet
| project id, virtualNetwork, subnetaz graph query -q "Resources | where type =~ 'microsoft.network/networkinterfaces' | project id, ipConfigurations = properties.ipConfigurations | mvexpand ipConfigurations | project id, subnetId = tostring(ipConfigurations.properties.subnet.id) | parse kind=regex subnetId with '/virtualNetworks/' virtualNetwork '/subnets/' subnet | project id, virtualNetwork, subnet"
Search-AzGraph -Query "Resources | where type =~ 'microsoft.network/networkinterfaces' | project id, ipConfigurations = properties.ipConfigurations | mvexpand ipConfigurations | project id, subnetId = tostring(ipConfigurations.properties.subnet.id) | parse kind=regex subnetId with '/virtualNetworks/' virtualNetwork '/subnets/' subnet | project id, virtualNetwork, subnet"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
This query uses the extended properties on virtual machines to summarize by power states.
Resources
| where type == 'microsoft.compute/virtualmachines'
| summarize count() by tostring(properties.extended.instanceView.powerState.code)az graph query -q "Resources | where type == 'microsoft.compute/virtualmachines' | summarize count() by tostring(properties.extended.instanceView.powerState.code)"
Search-AzGraph -Query "Resources | where type == 'microsoft.compute/virtualmachines' | summarize count() by tostring(properties.extended.instanceView.powerState.code)"
Try this query in Azure Resource Graph Explorer:
- Azure portal: portal.azure.com
- Azure Government portal: portal.azure.us
- Azure operated by 21Vianet portal: portal.azure.cn
- See samples of Starter queries.
- Learn more about the query language.
- Learn more about how to explore resources.