PowerShell and Azure Resource Graph


Introduction

On today’s post, I want to discuss a service introduced on the Microsoft Azure platform fairly recently, Azure Resource Graph. Azure Resource Graph service enables the administrators to explore resources in large scale deployment with relative ease across multiple subscriptions and management groups. Azure Resource Graph queries provide the following capabilities.
  • Ability to query resources with complex filtering, grouping, and sorting by resource properties.
  • Ability to iteratively explore resources based on governance requirements and convert the resulting expression into a policy definition.
  • Ability to assess the impact of applying policies in a vast cloud environment.
  • Ability to detail changes made to resource properties (preview).
Azure Resource Graph service was announced during 2018’s Microsoft Ignite Conference. The service coupled with cross-platform PowerShell Az module enables the administrators to discover resources much faster compared to the current method of sending requests to various resource providers to get a piece of limited information on resources.

The Azure Resource Graph service uses the Kusto Query language used by Azure Data Explorer and Azure Log Analytics. For the folks who are already familiar using Log Analytics should feel right at home with the query structure. Azure Resource Graph service can be accessed through Azure PowerShell modules, Azure CLI or through rest API calls.

Querying using Azure Powershell and Resource Graph

In this article, we will be concentrating on the basic query structure of Azure Resource Graph and querying using Az.ResourceGraph module. The module contains only one cmdlet Search-AzGraph.
Let's dive in and explore.

The basic query which you can fire at Azure Resource Graph is simply "", it's equivalent to SELECT * FROM in SQL server.


If you want to get a summary of resources according to the resource type you may use the below query.


In the above query summarize, count and sort by are tabular operators. You can find more information on supported operators and functions in here.

Search-AzGraph Parameters: Subscription, Skip, First

Before diving deeper into the query parts lets take a look at the options available in Search-AzGraph cmdlet. As you can see from the help info

We can specify the subscriptions as a list, and the number of results that need to be skipped using –skip. Then one of the most important consideration is to specify the parameter –First, it tells how many resources to be fetched. By default, it is set to 100, but when we are dealing with a larger number of resources we have to explicitly mention this value to a higher digit. I did raise a false issue in Az module’s GitHub because of this limitation. You can check out the mistake which I made in this link. Another information which needs to be mentioned is that while using PowerShell, the result is returned as a PSCustomObject. For compatibility with Azure CLI, please consider converting the result using ConvertTo-Json cmdlet. ConvertTo-Json has a default –depth value specified as 2, please consider using a higher value to get a complete result, possibly something above 50.

Search-AzGrpah Result Object

By default, the Search-AzGraph returns following properties in the result object.
  • id
  • type
  • location
  • name
  • subscriptionId
  • Properties
  • tags
  • tenantId
  • type
By seeing the above properties you might think of how it’s different from Get-AzResource cmdlet. But wait, the object property ‘properties’ holds the difference. The property ‘properties’ is again a PSCustomObject, and it holds a lot of information about that particular resource and Azure Graph Query itself provides tools to extract the data we need at the querying time itself.

Now let's check a query to get the data of virtual machines under a subscription.
$vmQuery = "where type =~ 'Microsoft.Compute/virtualMachines'
| extend osType = tostring(properties.storageProfile.osDisk.osType)
| extend osDiskSizeGB = tostring(properties.storageProfile.osDisk.diskSizeGB)
| extend vmSize = tostring(properties.hardwareProfile.vmSize)
| extend ScaleSet = tostring(properties.vailabilitySet.id)
| extend adminUser = tostring(properties.osProfile.adminUsername)
| extend nic = tostring(properties['networkProfile']['networkInterfaces'][0]['id'])
| project type, name, osType, vmSize, resourceGroup, location, subscriptionId, adminUser, osDiskSizeGB, nic, id"
Here extend is a tabular operator used to create and append calculated column to result set and tostring() is a scalar function that converts the input to string representation. Below is a code snippet useful to query info on virtual machines.
# Get VM details
$vmQuery = "where type =~ 'Microsoft.Compute/virtualMachines'
| extend osType = tostring(properties.storageProfile.osDisk.osType)
| extend osDiskSizeGB = tostring(properties.storageProfile.osDisk.diskSizeGB)
| extend vmSize = tostring(properties.hardwareProfile.vmSize)
| extend ScaleSet = tostring(properties.vailabilitySet.id)
| extend adminUser = tostring(properties.osProfile.adminUsername)
| extend nic = tostring(properties['networkProfile']['networkInterfaces'][0]['id'])
| project type, name, osType, vmSize, resourceGroup,
location, subscriptionId, adminUser, osDiskSizeGB, nic, id"

$vms = Search-AzGraph -Query $vmQuery -First 2000 -Verbose

$virtualMachines = @()
foreach ($vm in $vms)
{
# Initialising variables
$ipQuery = $null
$nicQuery = $null
$nicDetails = $null
$PublicIP = $null
$vmSize = $null

$nicQuery = "where id =~ '{0}'
| extend privateIP = tostring(properties['ipConfigurations'][0]['properties']['privateIPAddress'])
| extend publicIPID = tostring(properties['ipConfigurations'][0]['properties']['publicIPAddress']['id'])
| project privateIP, publicIPID" -f $vm.nic

try {
$nicDetails = Search-AzGraph -Query $nicQuery
# $nicDetails | convertTo-JSON -DEPTH 100 | OUT-FILE .\NICDATA.JSON
if ($nicDetails.publicIPID)
{
$ipQuery = "where id =~ '{0}'
| project PublicIP = tostring(properties.ipAddress)" -f $nicDetails.publicIPID
$PublicIP = (Search-AzGraph -Query $ipQuery).PublicIP
}
else {
$PublicIP = 'N/A'
$ipQuery = ""
}

$vmSize = Get-AzVMSize -Location $VM.location | Where-Object {$_.Name -eq $vm.vmSize}

$vmInfo = Get-AzVM -ResourceGroupName $vm.resourceGroup -Name $vm.name -Status

$avgmetrics = Get-AzMetric -ResourceId $vm.id -MetricName 'Percentage CPU' | Measure-Object -Average

$maxMetrics = Get-AzMetric -ResourceId $vm.id -MetricName 'Percentage CPU'| Measure-Object -Maximum

$vmProperties = [ordered]@{
resourceType = $vm.type
ServerName = $vm.name
osType = $vm.osType
vmStatus = $vmInfo.Statuses[1].DisplayStatus
createdOn = $vmInfo.Statuses[0].Time
NumberOfCores = $vmSize.NumberOfCores
MemoryInGB = ($vmSize.MemoryInMB / 1024)
PublicIP = $PublicIP
PrivateIP = $nicDetails.privateIP
vmSize = $vm.vmSize
osDiskSizeGB = $vm.osDiskSizeGB
ResourceGroup = $vm.resourceGroup
SubscriptionID = $vm.subscriptionId
Location = $vm.location
adminUser = $vm.adminUser
AvgCpuUsage = $avgmetrics.Average
MaxCpuUsage = $maxMetrics.Maximum
}

$vmObject = New-Object -TypeName psobject -Property $vmProperties
$virtualMachines += $vmObject
}

catch {
$errorMessage = $_.Exception.Message
$errorMessage
}
}
Some References:
Update:
As a relatively new service addition to Azure. The resource graph explorer is continuously undergoing changes. New functionalities are being added with each new release. Resource graph now allows us to list out subscriptions, resource groups, and security management resources separately apart from resources. I will add more details soon.

Comments

Popular posts from this blog

Check SQL Server Database Status

Static Code Analysis: Some Tools