Azure Policy Non-Compliance alerts with central workspace

So I was following this excellent blog post from Tao Yang at Microsoft, and I could not get any hits with the query written in the guide:

| where Category == 'Policy' and Level != 'Informational'
| extend p=todynamic(Properties)
| extend policies=todynamic(tostring(p.policies))
| mvexpand policy = policies
| where p.isComplianceCheck == 'False'

I created a test resource group which I know should show as non-compliant. Then I waited a few hours to be sure of events hitting the Activity Log. Sure enough, there were events in the activity log stating that resources were non-compliant. Category == Policy and Level == Warning. The problem has to lie elsewhere.

Previously I have configured the Activity Logs of all relevant subscriptions to be sent to a dedicated management log analytics workspace using the diagnostic settings. This is common to all my subscriptions, and allows central queries for several environments. The activity log is also archived to storage account for cheap long term storage.

If I visit the paragraph explaining changes for data structures, I can see there are structural changes, but not something that seems to apply directly to the Policy Category data structure. Nevertheless, I can see that the category information just is not stored structurally as I would expect (no Category, so I have to query the CategoryValue):

This is what I could expect when running the query above:

I don’t know if this is a planned structure change in Azure, or if this is a bug, but I have a few workarounds right now.

The first and simplest workaround, is to create a dedicated log analytics workspace for activity log, and send it to this with the legacy workspace connection method*. Then the query will work as it is posted in the MS article. There is no charge for the default 90 days retention of AzureActivity and Usage, so this will most likely not incur extra cost.

The second method – or at least what I have found so far – is to change the query somewhat. You won’t get the same results and the same level of detail, but the important information should still be there. Run this query:

| where OperationNameValue startswith "MICROSOFT.AUTHORIZATION/POLICIES"
| extend p=todynamic(Properties)
| where p.isComplianceCheck == 'False'
Should look something like this when you expand the information:
Bottom line is that it looks like the level information is missing in this experience. Maybe this will be included later, I don’t know. I found this comment from an MS employee:
Please let me know in the comment section if there is something I have missed here. Either I am doing something wrong, or this is intended data structure change.
* I do not know how long MS will support the legacy functionality, and I cannot recommend doing this in the long run. This is what I have done right now, just to be able to get alerts until I can figure out where the category and level data is stored in the new structure.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.