Azure Policy allows organization to define and enforce how Azure resources should or should not be used. It allows for the ability to block resources with certain configurations from being created, or generate audit events when a particular configuration is used. It can also modify resources at creation stage to ensure it meets standards set by your organization.

Azure Policy can be used to:

Restrict users only use a list of Azure regions approved by your organization

Audit if a Public IP is being used

Enforce Hybrid Use Benefit is enabled on all Windows Server VMs

Automatically deploy the Log Analytics agent on any newly created VMs

The list can go on and on. Currently Azure provides over one hundred built-in policy definitions that can be utilized to consume free of charge, and also provides the ability to develop your own policy definitions if none of the built-in policy meets your requirements.

Azure policy engine evaluates policy assignments when ARM resources are created or updated within the assignment scope. It also runs delta and full scans on a schedule – to detect non-compliant resources.

A resource can be marked as non-compliant by audit policies, or detected by the periodic scans (for example, resources that were created before the policies were assigned).

In the Azure portal, you can view the list of non-compliant policies (as shown below):

Azure Monitor can generate alerts and trigger pre-defined actions when non-complaint resources are detected.

This blog post detail how to use Kusto query language to detect non-compliant resources by querying your Log Analytics workspace, and then how to create Azure monitor alert rules to take actions when non-compliant resources are detected by Azure Policy.

The Azure policy compliance status is logged in the Azure subscription’s Activity logs. The Azure Log Analytics workspace can be configured to collect Azure Activity logs from any subscriptions in the same tenant. Azure Monitor alert rules can then be created to execute queries in the Log Analytics workspace on a schedule and generate alerts when non-compliant resources are detected by the query.

Before we start creating alert rules, we need to make sure Azure Activity logs are being collected by a Log Analytics workspace. To configure a Log Analytics workspace to collect Azure Activity logs, you need to navigate to the Azure Activity log blade of the workspace in the portal, and connect the subscriptions (as shown below):

Once the Log Analytics workspace started collecting logs, the policy compliance status will be collected to your workspace.

The following Kusto queries can be used to check these logs in your Log Analytics workspace:

Get a list of non-compliant policy with resource count

AzureActivity | where Category == 'Policy' and Level != 'Informational' | extend p=todynamic(Properties) | extend policies=todynamic(tostring(p.policies)) | mvexpand policy = policies | summarize resource_count=count() by tostring(policy.policyDefinitionName)

Get a list of non-compliant resources from a single policy (using “audit-resources-without-tags-policyDef” definition as an example)

let policyDefId = 'audit-resources-without-tags-policyDef'; AzureActivity | where Category == 'Policy' and Level != 'Informational' | extend p=todynamic(Properties) | extend policies=todynamic(tostring(p.policies)) | mvexpand policy = policies | where policy.policyDefinitionName in (policyDefId) | distinct ResourceId

Get newly detected non-compliance resources from a single policy (using using “audit-resources-without-tags-policyDef” definition as an example):

let policyDefId = 'audit-resources-without-tags-policyDef'; AzureActivity | where Category == 'Policy' and Level != 'Informational' | extend p=todynamic(Properties) | extend policies=todynamic(tostring(p.policies)) | mvexpand policy = policies | where policy.policyDefinitionName in (policyDefId) and p.isComplianceCheck == 'False'

NOTE: As previously mentioned, the Azure Policy engine runs periodic scans across all resources (compliance check). The full scan runs every 24 hours. In this example, we have added a condition in the end to only retrieve non-compliant resources that are not detected by the compliance check (p.isComplianceCheck == ‘False”). Without this condition, you will get spammed with any new and existing non-compliant resources after every compliance check scan. This will most likely cause duplicate alerts being generated too.

Based on these sample queries, you can use the following queries to build your alert rules based on your scenarios:

To alert on any new non-compliant resources:

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

To alert on new non-compliant resources detected by a particular policy:

let policyDefId = '<enter-policy-definition-id-here>'; AzureActivity | where Category == 'Policy' and Level != 'Informational' | extend p=todynamic(Properties) | extend policies=todynamic(tostring(p.policies)) | mvexpand policy = policies | where policy.policyDefinitionName in (policyDefId) and p.isComplianceCheck == 'False'

The alert rule can be created from the Azure portal or by using ARM templates.

Follow the following steps to create the alert rule via the portal:

1. Browse to Azure Monitor and click on Alerts blade

2. Click on New alert rules

3. On the Create rule page, select the appropriate subscription and the Log Analytics workspace

4. Add a condition, on the Configure signal logic page, select Custom log search

5. Enter the search query, and configure the alert logic as Number of results greater than 0, specify the period and frequency (or leave it as default) 6. Select an existing action group, or create a new one

7. Provide alert name, severity and other optional information such as email subject, and finish the wizard

Since I have configured the action group to send me an email, when next time a non-compliant resource is created, I will get an email within few minutes (as shown below):

NOTE: Action groups in Azure Monitor supports other action types such as webhook, mobile push notifications, SMS, LogicApp, Azure Function, ITSM, etc. Although I have only used email in this example, you can customise the action group to suit your requirements (i.e. creating a ticket in ServiceNow via the ITSM connector.

A sample template is available in my GitHub repo if there is a need to create the alert rule using ARM template: https://github.com/tyconsulting/azurepolicy/tree/master/arm-templates/monitor-alert-rule-for-non-com...

This sample template is a subscription level ARM template, it creates a resource group, Azure monitor action group and alert rule within the resource group. Since it is a subscription level template, you will need to use new-azdeployment cmdlet to deploy it. For example:

new-azdeployment -Name sampleAlertRule -Location australiasoutheast -TemplateFile .\azuredeploy.json -logAnalyticsWorkspaceResourceId /subscriptions/<sub-id>/resourcegroups/<log-analytics-rg>/providers/microsoft.operationalinsights/workspaces/workspace-name

NOTE:

The “new-azdeployment” cmdlet is part of the Azure PowerShell module. To execute this command, you must firstly install the “Az” PowerShell module and login to your Azure subscription using the following commands:

#Install Az module from the PowerShell gallery. Run it as administrator Install-Module Az -Repository PSGallery -Force -AllowClobber #Login to Azure Connect-AzAccount

To make use of the sample ARM template, you also need to update the PolicyDefId from line 22 of the template JSON file. To retrieve the full list of available Azure policy definitions in your environment, execute the command below once you have signed in to Azure using Connect-AzAccount cmdlet:

get-azpolicydefinition |format-table ResourceName, @{Name='DisplayName'; Expression={$_.Properties.DisplayName}}, ResourceId

The policy definition Id is listed under the ResourceName column (as shown below):

In this post, I have used a custom policy definition so the policy Id is more readable (the names of built-in policies are all GUIDs). The custom policy can be found at my GitHub repo: https://github.com/tyconsulting/azurepolicy/tree/master/policy-definitions/audit-resources-without-t...

In this post, I have demonstrated how to use Kusto search queries in Azure Log Analytics to detect non-compliant resources detected by Azure Policy, and how to create alert rules in Azure Monitor when a new non-compliant resource is detected. If you’d like to learn more about Azure Policy and Azure Monitor, make sure you check out our free eBook Inside Azure Management (https://bit.ly/InsideAzureMgmt)

Lastly, I’d like to thank my friend and fellow MVP Stanislav Zhelyazkov (@StanZhelyazkov) for helping me out with fine-tuning the Kusto queries used in the alert rule.

Tao