Skip to main content

Azure Policy and Blueprints — Governance at Scale

· 8 min read
Goel Academy
DevOps & Cloud Learning Hub

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.

CategoryExample Built-In Policies
ComputeAllowed VM sizes, require managed disks
NetworkNo public IPs on NICs, require NSG on subnets
StorageEnforce HTTPS, require encryption, deny public blob access
TagsRequire tag and its value, inherit tag from resource group
SecurityEnable Defender for Cloud, require encryption at rest
MonitoringDeploy 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 location
  • Indexed — 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.

EffectBehaviorBlocks Resource Creation?Use Case
DenyPrevents resource creation/updateYesHard enforcement (required tags, allowed regions)
AuditLogs non-compliance, allows creationNoVisibility and reporting before enforcement
ModifyAutomatically changes resource propertiesNo (fixes during create/update)Auto-add tags, enable encryption
AppendAdds properties during creationNoAdd fields like diagnosticSettings
DeployIfNotExistsDeploys a related resource if missingNoDeploy monitoring agent, enable backup
AuditIfNotExistsAudits if a related resource is missingNoCheck if diagnostics are enabled
DisabledPolicy exists but is not evaluatedNoTemporarily 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.