
Like all things I write here, I always start with “My customer asked me…” This time is was regarding deploying Azure Monitor Data Collection rules at scale with Azure Policy.
The cyber-security team wanted to be able to onboard the Azure Monitor Agent and start collecting data based on different scenarios, such as varying the security logs collected based on the role of a server. For example domain controllers and other tier 0 assets like domain controllers would have a higher level of security logs collected than tier 1 servers.
You can target them with Azure Policy and tags I told them though some were unsure on this. It turns out the Microsoft documentation and tagging examples don’t extend as far as this scenario, so here I am with some examples.
Each of the patterns listed below have the audit effect by default, if the tags and resource match (in this vase, Azure virtual machines) the policy will be marked as non-compliant. In my GitHub repository, I have provided examples of modified built in policies, incorporating these patterns. These are all linked at the bottom of this page.
This image shows these policies deployed in my demo environment.

You can see the “TCBD: Windows Application Events Level 2” policy is non-compliant. I better run a remediation task for that to associate the data collection rule.
Pattern 1 – Single Tag Name and Value
Based on the tag pattern found here, this example uses two parameters, one for the tag name and one for the value. It evaluates the tag field and the concat function to target the tag by name.
{
"properties": {
"displayName": "TCBD: Audit Tags",
"mode": "indexed",
"description": "Audit Tags",
"metadata": {
"version": "1.0.0",
"category": "Tags"
},
"version": "1.0.0",
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
},
"allowedValues": [
"Audit",
"Disabled"
],
"defaultValue": "Audit"
},
"tagName": {
"type": "String",
"metadata": {
"displayName": "Tag Name",
"description": "Name of the Tag to be evaluated"
}
},
"tagValue": {
"type": "String",
"metadata": {
"displayName": "Tag Value",
"description": "Value of the Tag to be evaluated"
}
}
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"field": "[concat('tags[', parameters('tagName'), ']')]",
"equals": "[parameters('tagValue')]"
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
}
},
"id": "/providers/Microsoft.Authorization/policyDefinitions/75d13af6-e718-468a-95eb-c5acc8a4be8a",
"name": "75d13af6-e718-468a-95eb-c5acc8a4be8a"
}
This will evaluate the resource and if the tag name and value exist and it is an Azure virtual machine, then the effect will occur.
Pattern 2 – Multiple Tags
The tagValues parameter is formatted
This example uses multiple tags written in it’s JSON format to the parameter. This example has two ways to evaluate the tags, either all the values must match, or one of the values. This is done using the tagOperator parameter, which has the allowed values of “Any” or “All”. Below is an example of the tagValues parameters. This doesn’t need to be formatted across multiple lines, just correctly case and syntax.
[
{
"key": "tagKey1",
"value": "value1"
},
{
"key": "tagKey2",
"value": "value2"
}
]
This example uses the count operator to iterate through each tag in the tagValues parameter and compare them against the resources tags. When using the “Any” tagOperator the count value only has to exceed zero, otherwise the “All” tagOperator requires the count value to be the same as the number of tags in tagValues.
{
"properties": {
"displayName": "TCBD: Audit Multiple Tags",
"mode": "indexed",
"description": "Audit Multiple Tags",
"metadata": {
"version": "1.0.0",
"category": "Tags"
},
"version": "1.0.0",
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
},
"allowedValues": [
"Audit",
"Disabled"
],
"defaultValue": "Audit"
},
"tagValues": {
"type": "Array",
"metadata": {
"displayName": "Tags on machines",
"description": "The list of tags that need to matched for getting target machines (case sensitive). Example: [ {\"key\": \"tagKey1\", \"value\": \"value1\"}, {\"key\": \"tagKey2\", \"value\": \"value2\"}]."
},
"defaultValue": []
},
"tagOperator": {
"type": "String",
"metadata": {
"displayName": "Tags operator",
"description": "Matching condition for resource tags"
},
"allowedValues": [
"All",
"Any"
],
"defaultValue": "All"
}
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"anyOf": [
{
"value": "[empty(parameters('tagValues'))]",
"equals": true
},
{
"allOf": [
{
"value": "[empty(field('tags'))]",
"equals": false
},
{
"value": "[parameters('tagOperator')]",
"equals": "Any"
},
{
"count": {
"value": "[parameters('tagValues')]",
"name": "tagKvp",
"where": {
"value": "[length(intersection(createObject(current('tagKvp').key, current('tagKvp').value), field('tags')))]",
"greater": 0
}
},
"greater": 0
}
]
},
{
"allOf": [
{
"value": "[empty(field('tags'))]",
"equals": false
},
{
"value": "[parameters('tagOperator')]",
"equals": "All"
},
{
"count": {
"value": "[parameters('tagValues')]",
"name": "tagKvp",
"where": {
"value": "[length(intersection(createObject(current('tagKvp').key, current('tagKvp').value), field('tags')))]",
"greater": 0
}
},
"equals": "[length(parameters('tagValues'))]"
}
]
}
]
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
}
},
"id": "/providers/Microsoft.Authorization/policyDefinitions/e73d483b-0821-4dea-8359-44fd440f816c",
"name": "e73d483b-0821-4dea-8359-44fd440f816c"
}
Pattern 3 – Tag Name only
This is very similar to the previous example, however rather than comparing the tag names and values, it is only comparing the names. Each tag name is passed to the tagValues parameter as a single array like below:
[
"tagKey1",
"tagKey2"
]
{
"properties": {
"displayName": "TCBD: Audit Multiple Tag Names",
"mode": "indexed",
"description": "Audit Multiple Tag Names",
"metadata": {
"version": "1.0.0",
"category": "Tags"
},
"version": "1.0.0",
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
},
"allowedValues": [
"Audit",
"Disabled"
],
"defaultValue": "Audit"
},
"tagValues": {
"type": "Array",
"metadata": {
"displayName": "Tags keys on machines",
"description": "The list of tags keys that need to exist for getting target machines (case sensitive). Example: [\"tagKey1\", \"tagKey2\"]."
},
"defaultValue": []
},
"tagOperator": {
"type": "String",
"metadata": {
"displayName": "Tags operator",
"description": "Matching condition for resource tags"
},
"allowedValues": [
"All",
"Any"
],
"defaultValue": "All"
}
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"anyOf": [
{
"value": "[empty(parameters('tagValues'))]",
"equals": true
},
{
"allOf": [
{
"value": "[empty(field('tags'))]",
"equals": false
},
{
"value": "[parameters('tagOperator')]",
"equals": "Any"
},
{
"count": {
"value": "[parameters('tagValues')]",
"name": "tagKey",
"where": {
"field": "tags",
"containsKey": "[current('tagKey')]"
}
},
"greater": 0
}
]
},
{
"allOf": [
{
"value": "[empty(field('tags'))]",
"equals": false
},
{
"value": "[parameters('tagOperator')]",
"equals": "All"
},
{
"count": {
"value": "[parameters('tagValues')]",
"name": "tagKey",
"where": {
"field": "tags",
"containsKey": "[current('tagKey')]"
}
},
"equals": "[length(parameters('tagValues'))]"
}
]
}
]
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
}
},
"id": "/providers/Microsoft.Authorization/policyDefinitions/e1359552-095e-4db7-bd76-72572a4911d4",
"name": "e1359552-095e-4db7-bd76-72572a4911d4"
}
Now all you have to do is take these patterns, integrate them with which ever policy you want to use for tag targeting. Below are the links to the sample patterns, as well as two examples using modified versions of built in policies.
- Audit Single Tag and Value
- Audit Multiple Tags and Values
- Audit Multiple Tag Names
- Deploy the Azure Monitor Agent to Windows Virtual Machines using Tag Names
- Based on the built in policy: Configure Windows virtual machines to run Azure Monitor Agent using system-assigned managed identity
- Associate a Windows Machines with a Data collection rule or data collection endpoint using tags
- Based on the built in policy: Configure Windows Machines to be associated with a Data Collection Rule or a Data Collection Endpoint