Azure Policy enables you to enforce organizational standards and assess compliance across your Azure environment. This guide covers creating policy definitions, building initiatives, assigning policies, monitoring compliance, and implementing automated remediation.
This article is part of our comprehensive guide on Cloud Security Tips for 2026, which covers essential security practices across all major cloud platforms.
Overview
Azure Policy provides:
- Preventive controls: Block non-compliant resource deployments
- Detective controls: Identify existing non-compliant resources
- Remediation: Automatically fix non-compliant configurations
- Compliance reporting: Track adherence to organizational standards
Policy effects determine how violations are handled: Deny, Audit, Append, Modify, DeployIfNotExists, AuditIfNotExists, and Disabled.
Prerequisites
Before implementing Azure Policy, ensure you have:
- Azure subscription with Owner or Policy Contributor role
- Management group structure (recommended for enterprise)
- Azure CLI installed (version 2.50 or later)
- Understanding of ARM templates and resource properties
- Documentation of organizational standards to enforce
Step 1: Understand Policy Concepts
Policy Definition
A policy definition describes the condition and effect:
{
"if": {
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
"then": {
"effect": "deny"
}
}
Policy Initiative (Definition Set)
Groups multiple policies for easier assignment:
- Microsoft Cloud Security Benchmark
- CIS Azure Foundations Benchmark
- Custom organizational initiatives
Policy Assignment
Applies policies to a scope (management group, subscription, or resource group).
Policy Effects
| Effect | When Applied | Use Case |
|---|---|---|
| Deny | Blocks non-compliant deployments | Enforce security requirements |
| Audit | Logs non-compliance, allows deployment | Assessment and testing |
| Append | Adds properties to resources | Auto-tagging |
| Modify | Changes properties on existing resources | Remediation |
| DeployIfNotExists | Deploys related resources | Deploy diagnostics |
| AuditIfNotExists | Audits for related resources | Check for dependencies |
Step 2: Explore Built-in Policies
Browse Policies in Portal
- Sign in to the Azure Portal
- Search for Policy and select it
- Go to Definitions
- Filter by:
- Type: Built-in
- Category: Security, Tags, Compute, Storage, etc.
- Click a policy to view its definition and parameters
List Built-in Policies via CLI
# List all built-in policies
az policy definition list \
--filter "policyType eq 'BuiltIn'" \
--query "[].{Name:displayName, Category:metadata.category}" \
-o table | head -50
# Search for storage-related policies
az policy definition list \
--filter "policyType eq 'BuiltIn'" \
--query "[?contains(displayName, 'storage')].{Name:displayName, ID:name}" \
-o table
# Get details of a specific policy
az policy definition show \
--name "404c3081-a854-4457-ae30-26a93ef643f9" \
--query "{Name:displayName, Description:description, Effect:policyRule.then.effect}"
Key Security Policies
| Policy Name | Effect | Purpose |
|---|---|---|
| Storage accounts should use customer-managed key | Audit | Data encryption |
| VMs should use managed disks | Deny | Security baseline |
| Subnets should have NSG associated | Audit | Network security |
| Key Vault should use RBAC | Audit | Access control |
| SQL databases should have TDE enabled | DeployIfNotExists | Data protection |
Step 3: Create Custom Policy Definitions
Create Policy via Portal
- In Policy, go to Definitions
- Click + Policy definition
- Configure:
- Definition location: Select management group or subscription
- Name:
Require-Resource-Tags - Category: Create new or select existing
- Enter policy rule (JSON)
- Click Save
Create Policy via Azure CLI
# Create a policy requiring specific tags
cat > /tmp/require-env-tag.json << 'EOF'
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "tags['Environment']",
"exists": "false"
}
]
},
"then": {
"effect": "deny"
}
}
EOF
az policy definition create \
--name "require-environment-tag" \
--display-name "Require Environment tag on resource groups" \
--description "Denies creation of resource groups without an Environment tag" \
--rules /tmp/require-env-tag.json \
--mode All
# Create policy to restrict VM sizes
cat > /tmp/allowed-vm-sizes.json << 'EOF'
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"not": {
"field": "Microsoft.Compute/virtualMachines/sku.name",
"in": "[parameters('allowedSizes')]"
}
}
]
},
"then": {
"effect": "deny"
}
}
EOF
cat > /tmp/allowed-vm-sizes-params.json << 'EOF'
{
"allowedSizes": {
"type": "Array",
"metadata": {
"displayName": "Allowed VM Sizes",
"description": "List of allowed VM sizes"
},
"defaultValue": ["Standard_D2s_v3", "Standard_D4s_v3", "Standard_D8s_v3"]
}
}
EOF
az policy definition create \
--name "allowed-vm-sizes" \
--display-name "Allowed virtual machine sizes" \
--description "Restricts VM deployments to approved sizes" \
--rules /tmp/allowed-vm-sizes.json \
--params /tmp/allowed-vm-sizes-params.json \
--mode All
Create Policy with DeployIfNotExists
# Policy to auto-deploy diagnostics to storage accounts
cat > /tmp/deploy-storage-diagnostics.json << 'EOF'
{
"if": {
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
"then": {
"effect": "deployIfNotExists",
"details": {
"type": "Microsoft.Insights/diagnosticSettings",
"name": "storageAccountDiagnostics",
"existenceCondition": {
"allOf": [
{
"field": "Microsoft.Insights/diagnosticSettings/logs.enabled",
"equals": "true"
}
]
},
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa",
"/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293"
],
"deployment": {
"properties": {
"mode": "incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": { "type": "string" },
"logAnalyticsWorkspaceId": { "type": "string" }
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/providers/diagnosticSettings",
"apiVersion": "2021-05-01-preview",
"name": "[concat(parameters('storageAccountName'), '/Microsoft.Insights/storageAccountDiagnostics')]",
"properties": {
"workspaceId": "[parameters('logAnalyticsWorkspaceId')]",
"logs": [
{ "category": "StorageRead", "enabled": true },
{ "category": "StorageWrite", "enabled": true }
]
}
}
]
}
}
}
}
}
}
EOF
Step 4: Create Policy Initiatives
Create Initiative via Portal
- In Policy, go to Definitions
- Click + Initiative definition
- Configure:
- Definition location: Management group or subscription
- Name:
Security-Baseline-Initiative - Category: Security
- Add policy definitions to the initiative
- Configure parameters and groupings
- Click Create
Create Initiative via Azure CLI
# Create initiative combining multiple policies
cat > /tmp/security-initiative.json << 'EOF'
{
"properties": {
"displayName": "Organization Security Baseline",
"description": "Enforces security requirements for all resources",
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/404c3081-a854-4457-ae30-26a93ef643f9",
"parameters": {}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0a914e76-4921-4c19-b460-a2d36003525a",
"parameters": {}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e71308d3-144b-4262-b144-efdc3cc90517",
"parameters": {}
}
]
}
}
EOF
az policy set-definition create \
--name "org-security-baseline" \
--display-name "Organization Security Baseline" \
--description "Security policies required for all subscriptions" \
--definitions '[
{"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/404c3081-a854-4457-ae30-26a93ef643f9"},
{"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/0a914e76-4921-4c19-b460-a2d36003525a"},
{"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e71308d3-144b-4262-b144-efdc3cc90517"}
]'
Step 5: Assign Policies
Assign via Portal
- In Policy, go to Assignments
- Click Assign policy or Assign initiative
- Configure:
- Scope: Management group, subscription, or resource group
- Exclusions: Optional exclusions
- Policy definition: Select policy or initiative
- Configure parameters (if applicable)
- Configure remediation (for DeployIfNotExists)
- Set Non-compliance message
- Click Create
Assign via Azure CLI
# Assign a single policy to a subscription
az policy assignment create \
--name "require-tags-assignment" \
--display-name "Require Environment Tag" \
--policy "require-environment-tag" \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--enforcement-mode Default
# Assign policy with parameters
az policy assignment create \
--name "allowed-vm-sizes-assignment" \
--display-name "Restrict VM Sizes" \
--policy "allowed-vm-sizes" \
--scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/rg-production" \
--params '{"allowedSizes": {"value": ["Standard_D2s_v3", "Standard_D4s_v3"]}}'
# Assign built-in initiative (Microsoft Cloud Security Benchmark)
az policy assignment create \
--name "mcsb-assignment" \
--display-name "Microsoft Cloud Security Benchmark" \
--policy-set-definition "1f3afdf9-d0c9-4c3d-847f-89da613e70a8" \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--mi-system-assigned \
--location "eastus"
# Assign to management group
MANAGEMENT_GROUP_ID="mg-production"
az policy assignment create \
--name "security-baseline-mg" \
--display-name "Security Baseline" \
--policy-set-definition "org-security-baseline" \
--scope "/providers/Microsoft.Management/managementGroups/$MANAGEMENT_GROUP_ID"
Enable Remediation with Managed Identity
# Assign policy with managed identity for remediation
az policy assignment create \
--name "deploy-diagnostics" \
--display-name "Deploy Storage Diagnostics" \
--policy "deploy-storage-diagnostics" \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--mi-system-assigned \
--location "eastus" \
--role "Contributor" \
--identity-scope "/subscriptions/$SUBSCRIPTION_ID"
Step 6: Monitor Compliance
View Compliance in Portal
- In Policy, go to Compliance
- Review:
- Overall compliance percentage
- Non-compliant resources by policy
- Compliance by scope
- Click a policy to see affected resources
- Export compliance data for reporting
Query Compliance via CLI
# Get overall compliance state
az policy state summarize \
--query "{Compliant:results.complianceState.compliant, NonCompliant:results.complianceState.nonCompliant}"
# List non-compliant resources
az policy state list \
--filter "complianceState eq 'NonCompliant'" \
--query "[].{Resource:resourceId, Policy:policyDefinitionName}" \
-o table
# Get compliance for specific policy
az policy state list \
--policy-assignment "require-tags-assignment" \
--query "[].{Resource:resourceId, State:complianceState}" \
-o table
Query with Azure Resource Graph
# Comprehensive compliance report
az graph query -q "
policyresources
| where type == 'microsoft.policyinsights/policystates'
| where properties.complianceState == 'NonCompliant'
| extend policy = tostring(properties.policyDefinitionName)
| extend resource = tostring(properties.resourceId)
| summarize NonCompliantCount=count() by policy
| order by NonCompliantCount desc
"
# Non-compliant resources by subscription
az graph query -q "
policyresources
| where type == 'microsoft.policyinsights/policystates'
| where properties.complianceState == 'NonCompliant'
| extend subscriptionId = tostring(split(properties.resourceId, '/')[2])
| summarize count() by subscriptionId
"
Step 7: Remediate Non-Compliance
Create Remediation Task via Portal
- In Policy, go to Compliance
- Select a non-compliant policy
- Click Create remediation task
- Configure:
- Scope for remediation
- Resource discovery mode
- Click Remediate
Create Remediation Task via CLI
# List non-compliant resources for a policy
az policy state list \
--policy-assignment "deploy-diagnostics" \
--filter "complianceState eq 'NonCompliant'" \
--query "[].resourceId" -o tsv
# Create remediation task
az policy remediation create \
--name "remediate-storage-diagnostics" \
--policy-assignment "deploy-diagnostics" \
--resource-discovery-mode ExistingNonCompliant \
--resource-group "rg-production"
# Check remediation status
az policy remediation show \
--name "remediate-storage-diagnostics" \
--resource-group "rg-production" \
--query "{Status:provisioningState, Progress:deploymentStatus}"
# List all remediation tasks
az policy remediation list \
--query "[].{Name:name, Status:provisioningState, Failed:deploymentStatus.failedDeployments}" \
-o table
Best Practices
- Start with Audit effect: Assess impact before enforcing Deny
- Use management groups: Apply policies consistently across subscriptions
- Document exclusions: Track why resources are exempted
- Version control policies: Store definitions in Git repositories
- Test in non-production: Validate policies before production rollout
- Monitor remediation: Review failed remediation tasks regularly
- Use initiatives: Group related policies for easier management
- Set meaningful messages: Help users understand non-compliance reasons
Troubleshooting
Policy not evaluating new resources:
- Allow 30 minutes for evaluation cycle
- Trigger manual evaluation:
az policy state trigger-scan - Verify policy assignment scope includes the resource
Remediation task failing:
- Check managed identity has required permissions
- Verify resource supports the remediation action
- Review deployment logs in remediation task details
Policy conflicts:
- Check for overlapping assignments at different scopes
- Lower scope assignments override higher scope
- Use exemptions to resolve legitimate conflicts
Compliance showing incorrect state:
- Trigger policy evaluation refresh
- Check if resource type is supported by policy
- Verify policy definition logic is correct
Next Steps
After implementing Azure Policy, enhance your governance:
- Implement Azure Blueprints for environment templates
- Configure Privileged Identity Management for admin access
- Enable Microsoft Defender for Cloud regulatory compliance
- Review Cloud Security Tips for 2026 for comprehensive cloud security guidance