Azure Policy and Blueprints — Governance at Scale
You set up cost alerts. You documented tagging standards. You sent emails. And developers still create VMs in regions you do not operate in, without tags, using sizes that cost more than some people's salaries. Documentation does not enforce rules — Azure Policy does. It evaluates every resource creation and modification in real time, and it can deny, audit, or automatically fix violations before they become your problem.
What Is Azure Policy
Azure Policy is a service that creates, assigns, and manages rules that your Azure resources must follow. It evaluates resources during creation (via Azure Resource Manager) and on a periodic compliance scan (every 24 hours by default). If a resource violates a policy, Azure can block it, flag it, or fix it automatically.
The core components:
- Policy Definition — The rule itself (what to evaluate and what to do)
- Policy Assignment — Applying a definition to a specific scope (subscription, resource group, management group)
- Initiative Definition — A group of related policy definitions bundled together
- Compliance State — Whether resources at a scope pass or fail the assigned policies
# List all built-in policy definitions (there are 1000+)
az policy definition list \
--query "[?policyType=='BuiltIn'].{Name:displayName, Category:metadata.category}" \
--output table | head -30
# Search for tag-related built-in policies
az policy definition list \
--query "[?policyType=='BuiltIn' && contains(displayName, 'tag')].{Name:displayName, ID:name}" \
--output table
Built-In vs Custom Policies
Azure ships with over 1,000 built-in policies covering compute, networking, storage, security, monitoring, and more. Always check for a built-in policy before writing a custom one.
| Category | Example Built-In Policies |
|---|---|
| Compute | Allowed VM sizes, require managed disks |
| Network | No public IPs on NICs, require NSG on subnets |
| Storage | Enforce HTTPS, require encryption, deny public blob access |
| Tags | Require tag and its value, inherit tag from resource group |
| Security | Enable Defender for Cloud, require encryption at rest |
| Monitoring | Deploy diagnostics settings, require Log Analytics agent |
When built-in policies do not cover your specific requirement, you write a custom policy definition in JSON.
Policy Definition Structure
Every policy definition follows this structure:
{
"properties": {
"displayName": "Require CostCenter tag on resource groups",
"description": "Denies creation of resource groups without a CostCenter tag",
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "tags['CostCenter']",
"exists": "false"
}
]
},
"then": {
"effect": "deny"
}
},
"parameters": {}
}
}
The mode determines which resource types are evaluated:
All— Evaluates all resource types including tags and locationIndexed— Only evaluates resource types that support tags and location (skips things like role assignments)
Policy Effects — Deny, Audit, Modify, and More
The effect controls what happens when a resource violates the policy. Choosing the right effect is critical — start with Audit to understand the blast radius before switching to Deny.
| Effect | Behavior | Blocks Resource Creation? | Use Case |
|---|---|---|---|
| Deny | Prevents resource creation/update | Yes | Hard enforcement (required tags, allowed regions) |
| Audit | Logs non-compliance, allows creation | No | Visibility and reporting before enforcement |
| Modify | Automatically changes resource properties | No (fixes during create/update) | Auto-add tags, enable encryption |
| Append | Adds properties during creation | No | Add fields like diagnosticSettings |
| DeployIfNotExists | Deploys a related resource if missing | No | Deploy monitoring agent, enable backup |
| AuditIfNotExists | Audits if a related resource is missing | No | Check if diagnostics are enabled |
| Disabled | Policy exists but is not evaluated | No | Temporarily disable a policy |
# Assign a built-in policy: "Require a tag on resources"
az policy assignment create \
--name "require-costcenter-tag" \
--display-name "Require CostCenter tag on all resources" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b466-ef391786570f" \
--scope "/subscriptions/<subscription-id>" \
--params '{"tagName":{"value":"CostCenter"}}' \
--enforcement-mode Default
Initiative Definitions (Policy Sets)
An initiative groups multiple related policies into a single assignment. Instead of assigning 10 individual tag policies, create one initiative called "Tagging Standards" and assign it once.
# Create a custom initiative definition
az policy set-definition create \
--name "tagging-standards" \
--display-name "Organization Tagging Standards" \
--description "Requires CostCenter, Environment, Team, and Owner tags" \
--definitions '[
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b466-ef391786570f",
"parameters": {"tagName": {"value": "CostCenter"}}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b466-ef391786570f",
"parameters": {"tagName": {"value": "Environment"}}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b466-ef391786570f",
"parameters": {"tagName": {"value": "Team"}}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b466-ef391786570f",
"parameters": {"tagName": {"value": "Owner"}}
}
]'
# Assign the initiative to a subscription
az policy assignment create \
--name "enforce-tagging" \
--display-name "Enforce Tagging Standards" \
--policy-set-definition "tagging-standards" \
--scope "/subscriptions/<subscription-id>"
Compliance Dashboard
The compliance dashboard in the portal shows the percentage of compliant resources at every scope. You can drill down by policy, resource group, or individual resource. The compliance scan runs automatically every 24 hours, but you can trigger an on-demand scan:
# Trigger a compliance scan for a subscription
az policy state trigger-scan \
--subscription "<subscription-id>" \
--no-wait
# Check compliance state for an assignment
az policy state list \
--policy-assignment "require-costcenter-tag" \
--query "[?complianceState=='NonCompliant'].{Resource:resourceId, State:complianceState}" \
--output table
The compliance dashboard is your weekly governance review tool. Export compliance data to Log Analytics for trend analysis and executive reporting.
Remediation Tasks
Policies with Modify or DeployIfNotExists effects can automatically fix existing non-compliant resources through remediation tasks. This is powerful but dangerous — always test on a non-production scope first.
# Create a remediation task for a policy assignment
az policy remediation create \
--name "remediate-missing-tags" \
--policy-assignment "require-costcenter-tag" \
--resource-group rg-production \
--resource-discovery-mode ReEvaluateCompliance
# Check remediation progress
az policy remediation show \
--name "remediate-missing-tags" \
--resource-group rg-production \
--query "{Status:provisioningState, Succeeded:deploymentStatus.totalDeployments, Failed:deploymentStatus.failedDeployments}" \
--output table
Real Policy Examples
Here are three policies every organization should implement:
1. Restrict Allowed Regions (Deny)
{
"properties": {
"displayName": "Allowed locations",
"policyRule": {
"if": {
"allOf": [
{
"field": "location",
"notIn": ["eastus", "eastus2", "westus2", "centralus"]
},
{
"field": "location",
"notEquals": "global"
}
]
},
"then": {
"effect": "deny"
}
}
}
}
2. Restrict VM Sizes (Deny)
# Assign built-in "Allowed virtual machine size SKUs" policy
az policy assignment create \
--name "allowed-vm-sizes" \
--display-name "Allowed VM Sizes" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/cccc23c7-8427-4f53-ad12-b6a63eb452b3" \
--scope "/subscriptions/<subscription-id>" \
--params '{"listOfAllowedSKUs":{"value":["Standard_D2s_v5","Standard_D4s_v5","Standard_D8s_v5","Standard_B2ms","Standard_B4ms"]}}'
3. Auto-Inherit Tags from Resource Group (Modify)
# Assign built-in "Inherit a tag from the resource group" policy
az policy assignment create \
--name "inherit-costcenter-tag" \
--display-name "Inherit CostCenter tag from resource group" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/cd3aa116-8754-49c9-a813-ad46512ece54" \
--scope "/subscriptions/<subscription-id>" \
--params '{"tagName":{"value":"CostCenter"}}' \
--mi-system-assigned \
--location eastus
The --mi-system-assigned flag is required for Modify effect policies because Azure needs a managed identity to make changes to resources on your behalf.
Azure Blueprints for Repeatable Environments
Azure Blueprints package policies, role assignments, ARM templates, and resource groups into a single deployable artifact. Think of a blueprint as a "stamp" for creating new subscriptions or environments that are pre-configured with your governance rules.
A blueprint can contain:
- Policy assignments
- Role assignments (RBAC)
- ARM templates (infrastructure)
- Resource groups
This is particularly useful for landing zones. When a new team requests a subscription, you stamp it with a blueprint that creates the required resource groups, assigns policies, grants appropriate roles, and deploys baseline infrastructure (networking, monitoring, security).
Management Groups Hierarchy
Policies assigned at higher scopes inherit downward. Management groups sit above subscriptions and let you organize your Azure estate hierarchically:
Root Management Group
├── Platform
│ ├── Connectivity (hub networking, DNS, firewall)
│ └── Identity (Entra ID, domain controllers)
├── Landing Zones
│ ├── Production
│ │ ├── Sub: Prod-App-A
│ │ └── Sub: Prod-App-B
│ └── Non-Production
│ ├── Sub: Dev
│ └── Sub: Staging
└── Sandbox
└── Sub: Innovation-Lab
# Create a management group
az account management-group create \
--name "LandingZones" \
--display-name "Landing Zones"
# Assign a policy at the management group level (inherits to all child subscriptions)
az policy assignment create \
--name "enforce-tags-all-subs" \
--display-name "Enforce Tags Across All Landing Zones" \
--policy-set-definition "tagging-standards" \
--scope "/providers/Microsoft.Management/managementGroups/LandingZones"
Assign common governance policies at the management group level — they automatically apply to every subscription underneath. Teams cannot opt out.
Wrapping Up
Azure Policy turns your governance documentation into automated enforcement. Start with Audit mode to understand your current compliance posture, then progressively switch critical policies to Deny. Use initiatives to bundle related policies, management groups to apply them at scale, and remediation tasks to fix existing drift. The goal is not to block developers from working — it is to make the compliant path the easiest path. When creating a tagged, properly-sized VM in an approved region is the default, you do not need to chase violations.
Next up: We will explore advanced Azure networking — Private Link, Service Endpoints, and Hub-Spoke topology for securing your network architecture at enterprise scale.
