Jump to content

Featured Replies

Posted

largevv2px999.png.bdceca93e95ee5cea416450d4e4b1380.png

 

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

 

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:

 

largevv2px999.png.7788e0c30962ed7c99048146ea9e1eb6.pngScreenshot 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

 

 

largevv2px999.png.ef1991f70e41b466b4ce1e927c1c8972.png

 

 

 

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.

 

Continue reading...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...