Blog

Tracking Azure History with Azure Resource Graph

Introduction

When administrating an Azure environment, or any environment really, one will most likely find a way to track changes that were introduced. There are a number of ways to do this. Within Azure can query the Subscription or Resource Group Deployment, the downside though is this approach is limited to just the scope you are querying on. What if this is a larger organization with multiple subscriptions? You could also rely on a well-established CI/CD pipeline, a third-party governance tool, or in this case query Azure directly via the Resource Graph Explorer.

 

For this blog will focus on using the Azure Portal offering of the tool; however, want to note that since this is API driven there are numerous offerings such as Azure PowerShell, Azure CLI, .NET, even Ruby.

 

PreReqs

An understanding of Kusto Query Language (KQL) is very helpful
Knowing what types of tables are available in Resource Graph
The individual/process querying need to have read access over the available Azure resources

Limitations

Documentation clearly supports will store only last 14 days worth of changes.
Latest documentation shows the Resource Change History API has been in preview since 4/23/19

 

How

We will be querying two tables for this exercise. It is fairly straight forward; however, something anyone can further customize by joining additional tables and/or filters two. This will deal with only two tables: resources and resource changes. To access the Resource Graph Explorer type ‘Resource Graph Explorer’ in the Azure search bar.

 

resources

This table is defined as: “The default table if none defined in the query. Most Resource Manager resource types and properties are here.”

What this essentially translates to is that this table will hold the basic properties of all the resources.  As of this writing the here are some of the columns are available, and I am providing a brief description of what the values mean.

Column Name
Description

id
Unique id of the Azure Resource. This will include subscription ID down to individual resource.

name
Name of the Azure Resource

type
The Microsoft defined type of resource. Complete list available here.

tenantId
Azure AD Tenant associated with the resource

kind
Not always present: however, some values could be “StorageV2” or “functionapp” for microsoft.storage/storage account or microsoft.web/sites respectively

location
What location within azure the resource is deployed to, ‘global’ for those that are global.

resourceGroup
The Azure Resource Group the resource resides in

subscriptionId
The associated Azure Subscription

sku
Not always present; however, what pricing tier or sku the resource has been set to

properties
This will include the provisioning status as well as the JSON Azure Resource Manager definition of the resource

tags
Any tags that have been applied

 

resourcechanges

This is the table that is preview and whose dataset is limited to 14 days as mentioned above. This table will follow the same schema; however, the all-important information on what changed is contained in the properties as a JSON object.

See:

Screenshot of resourcechanges properties json

Query

So now we have the two tables, the question becomes how to join them together. If new I highly advise brushing up on KQL.  Essentially our query will need to accomplish:

Expanding the resourcechanges properties to retrieve change details and targetResourceId of the change
Join resources to resourcechanges on the resourceId and targetResourceId field
pull any additional resource information for reporting purposes.

For our purposes I’ll pull the Resource Name, Resource Type, Subscription, and Resource Group Name as these could be beneficial for anyone doing reporting.

 

One thing that did trip me up when working with joins in KQL. All columns which will be used from the join table need to be exposed in the project command, this INCLUDES the field you are joining on.

 

The Query

 
resourcechanges

| extend changeTime = todatetime(properties.changeAttributes.timestamp), targetResourceId = tostring(properties.targetResourceId),

changeType = tostring(properties.changeType), correlationId = properties.changeAttributes.correlationId,

changedProperties = properties.changes, changeCount = properties.changeAttributes.changesCount

| where changeTime > ago(180d)

| join kind=inner (resources | project resources_Name = name, resources_Type = type, resources_Subscription= subscriptionId, resources_ResourceGroup= resourceGroup, id) on $left.targetResourceId == $right.id

| project resources_Name, resources_Type, resources_Subscription, resources_ResourceGroup, changeTime, targetResourceId, changeType, correlationId, changeCount, changedProperties

 

The Results

 

Conclusion

There you go. This may seem like a complex approach to something as easy as what’s changed across my Azure subscriptions; however, this approach does accurately achieve that goal. Furthermore, with the APIs being exposed across multiple programming languages leaves upon limitless possibilities as to what one can do.

 

 

No Comments

Leave a Reply

Microsoft Teams helps manage apps for everyday work functions and fit our modern hybrid needsUse Static Web Apps API and API Management Authorizations to integrate third party services

Contact Us

We are always here to help. Please reach out to us and we'll get back to you as soon as possible.

Address:

Maruti Court, Ground Floor
Mvuli Road, Nairobi, Kenya

Phone:

+254716973110

Hours:

Mon-Fri 8am – 5pm
Sat 9am – 1am
Sun & Holidays Closed




    Generated by Feedzy