intermediate25 min readcloud-providersUpdated: 2024-06-22

Azure Policy Deep Dive

Master Azure Policy definitions, initiatives, and compliance assessments for enterprise governance

๐Ÿ“‹ Prerequisites

  • Azure subscription with appropriate permissions
  • Azure CLI or PowerShell installed
  • Basic understanding of ARM templates or Bicep
  • Familiarity with JSON and PowerShell/CLI
  • Read: What is Policy-as-Code?

๐ŸŽฏ What You'll Learn

  • Creating and managing Azure Policy definitions
  • Building policy initiatives for comprehensive governance
  • Implementing compliance assessments and remediation
  • Using Azure Policy for cost management
  • Integrating policies with Azure DevOps pipelines
  • Best practices for enterprise-scale policy governance

๐Ÿท๏ธ Topics Covered

azure policy definition examplesazure policy initiatives tutorialazure policy compliance assessmentazure resource governance policiesazure policy json examplesazure policy best practices

Azure Policy Definition Examples: Complete Governance Guide

Azure Policy is Microsoft's native governance service that enables you to create, assign, and manage policies to enforce rules and effects over your Azure resources. It provides centralized policy management, compliance reporting, and automated remediation capabilities.

๐Ÿ“œ Policy Definitions

Individual rules that define what you want to evaluate and what action to take

๐Ÿ“‹ Policy Initiatives

Collections of policy definitions grouped together for comprehensive governance

๐ŸŽฏ Policy Assignments

Application of policies or initiatives to specific scopes with enforcement parameters

Azure Policy Initiatives Tutorial: Step-by-Step Creation Guide

Policy definitions are the building blocks of Azure Policy. They define the conditions to evaluate and the actions to take when those conditions are met.

Basic Policy Structure

Basic Policy Definition Structure

{
  "properties": {
    "displayName": "Policy Display Name",
    "description": "Detailed description of what this policy does",
    "policyType": "Custom",
    "mode": "All",
    "parameters": {
      // Parameter definitions
    },
    "policyRule": {
      "if": {
        // Conditions to evaluate
      },
      "then": {
        "effect": "deny|audit|append|modify|deployIfNotExists"
      }
    }
  }
}

Example: Require Resource Tags

require-tags-policy.json

{
  "properties": {
    "displayName": "Require specific tags on resources",
    "description": "Enforces the existence of specific tags on all resources",
    "policyType": "Custom",
    "mode": "Indexed",
    "parameters": {
      "tagName": {
        "type": "String",
        "metadata": {
          "displayName": "Tag Name",
          "description": "Name of the tag, such as 'Environment'"
        }
      },
      "tagValues": {
        "type": "Array",
        "metadata": {
          "displayName": "Tag Values",
          "description": "List of allowed values for the tag"
        }
      }
    },
    "policyRule": {
      "if": {
        "anyOf": [
          {
            "field": "[concat('tags[', parameters('tagName'), ']')]",
            "exists": "false"
          },
          {
            "field": "[concat('tags[', parameters('tagName'), ']')]",
            "notIn": "[parameters('tagValues')]"
          }
        ]
      },
      "then": {
        "effect": "deny"
      }
    }
  }
}

Example: Storage Account Security Policy

storage-security-policy.json

{
  "properties": {
    "displayName": "Storage accounts should use HTTPS traffic only",
    "description": "Audit requirement of Secure transfer in your storage account",
    "policyType": "Custom",
    "mode": "Indexed",
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Storage/storageAccounts"
          },
          {
            "field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
            "notEquals": "true"
          }
        ]
      },
      "then": {
        "effect": "audit"
      }
    }
  }
}

Creating Policies with Azure CLI

Create Policy Definition

# Create a custom policy definition
az policy definition create \
  --name "require-tags-policy" \
  --display-name "Require specific tags on resources" \
  --description "Enforces the existence of specific tags on all resources" \
  --rules require-tags-policy.json \
  --mode Indexed

# List all policy definitions
az policy definition list --query "[?policyType=='Custom']" --output table

# Get specific policy definition
az policy definition show --name "require-tags-policy"

Advanced Policy Examples

๐Ÿ”ง 1. VM Size Restriction Policy

vm-size-restriction.json

{
  "properties": {
    "displayName": "Allowed virtual machine size SKUs",
    "description": "This policy enables you to specify a set of VM size SKUs that your organization can deploy",
    "policyType": "Custom",
    "mode": "Indexed",
    "parameters": {
      "allowedSKUs": {
        "type": "Array",
        "metadata": {
          "description": "The list of size SKUs that can be specified for virtual machines",
          "displayName": "Allowed Size SKUs",
          "strongType": "VMSKUs"
        }
      }
    },
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Compute/virtualMachines"
          },
          {
            "not": {
              "field": "Microsoft.Compute/virtualMachines/sku.name",
              "in": "[parameters('allowedSKUs')]"
            }
          }
        ]
      },
      "then": {
        "effect": "Deny"
      }
    }
  }
}

๐Ÿ”ง 2. Network Security Group Rule Policy

nsg-rdp-restriction.json

{
  "properties": {
    "displayName": "Network Security Groups should not allow unrestricted access on port 3389",
    "description": "This policy audits any network security group with unrestricted access on port 3389",
    "policyType": "Custom",
    "mode": "All",
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Network/networkSecurityGroups/securityRules"
          },
          {
            "allOf": [
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules/access",
                "equals": "Allow"
              },
              {
                "field": "Microsoft.Network/networkSecurityGroups/securityRules/direction",
                "equals": "Inbound"
              },
              {
                "anyOf": [
                  {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
                    "equals": "*"
                  },
                  {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules/destinationPortRange",
                    "equals": "3389"
                  }
                ]
              },
              {
                "anyOf": [
                  {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
                    "equals": "*"
                  },
                  {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules/sourceAddressPrefix",
                    "equals": "Internet"
                  }
                ]
              }
            ]
          }
        ]
      },
      "then": {
        "effect": "audit"
      }
    }
  }
}

๐Ÿ”ง 3. Automatic Tagging with Modify Effect

auto-tag-policy.json

{
  "properties": {
    "displayName": "Add or replace a tag on resources",
    "description": "Adds or replaces the specified tag and value when any resource is created or updated",
    "policyType": "Custom",
    "mode": "Indexed",
    "parameters": {
      "tagName": {
        "type": "String",
        "metadata": {
          "displayName": "Tag Name",
          "description": "Name of the tag, such as 'Environment'"
        }
      },
      "tagValue": {
        "type": "String",
        "metadata": {
          "displayName": "Tag Value",
          "description": "Value of the tag, such as 'Production'"
        }
      }
    },
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "[concat('tags[', parameters('tagName'), ']')]",
            "notEquals": "[parameters('tagValue')]"
          },
          {
            "field": "type",
            "notEquals": "Microsoft.Resources/subscriptions/resourceGroups"
          }
        ]
      },
      "then": {
        "effect": "modify",
        "details": {
          "roleDefinitionIds": [
            "/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
          ],
          "operations": [
            {
              "operation": "addOrReplace",
              "field": "[concat('tags[', parameters('tagName'), ']')]",
              "value": "[parameters('tagValue')]"
            }
          ]
        }
      }
    }
  }
}

Azure Policy Compliance Assessment: Initiative Best Practices

Policy initiatives group related policy definitions together for easier management and assignment. They're essential for implementing comprehensive compliance frameworks.

Security Baseline Initiative

security-baseline-initiative.json

{
  "properties": {
    "displayName": "Security Baseline Initiative",
    "description": "Collection of security policies for baseline compliance",
    "metadata": {
      "version": "1.0.0",
      "category": "Security Center"
    },
    "parameters": {
      "allowedLocations": {
        "type": "Array",
        "metadata": {
          "description": "The list of locations that can be specified when deploying resources",
          "displayName": "Allowed locations",
          "strongType": "location"
        }
      },
      "requiredTagName": {
        "type": "String",
        "metadata": {
          "description": "Required tag name",
          "displayName": "Required Tag Name"
        },
        "defaultValue": "Environment"
      }
    },
    "policyDefinitions": [
      {
        "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c",
        "parameters": {
          "listOfAllowedLocations": {
            "value": "[parameters('allowedLocations')]"
          }
        },
        "groupNames": ["LocationCompliance"]
      },
      {
        "policyDefinitionId": "/subscriptions/{subscription-id}/providers/Microsoft.Authorization/policyDefinitions/require-tags-policy",
        "parameters": {
          "tagName": {
            "value": "[parameters('requiredTagName')]"
          }
        },
        "groupNames": ["TagCompliance"]
      },
      {
        "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/404c3081-a854-4457-ae30-26a93ef643f9",
        "parameters": {},
        "groupNames": ["StorageSecurity"]
      }
    ],
    "policyDefinitionGroups": [
      {
        "name": "LocationCompliance",
        "displayName": "Location Compliance",
        "description": "Policies related to allowed locations"
      },
      {
        "name": "TagCompliance",
        "displayName": "Tagging Compliance",
        "description": "Policies related to required tags"
      },
      {
        "name": "StorageSecurity",
        "displayName": "Storage Security",
        "description": "Policies related to storage account security"
      }
    ]
  }
}

Creating Initiatives with Azure CLI

Create and Assign Initiative

# Create policy initiative
az policy set-definition create \
  --name "security-baseline" \
  --display-name "Security Baseline Initiative" \
  --description "Collection of security policies for baseline compliance" \
  --definitions security-baseline-initiative.json

# Assign initiative to subscription
az policy assignment create \
  --name "security-baseline-assignment" \
  --display-name "Security Baseline Assignment" \
  --policy-set-definition "security-baseline" \
  --scope "/subscriptions/{subscription-id}" \
  --params '{
    "allowedLocations": {
      "value": ["eastus", "westus2", "centralus"]
    },
    "requiredTagName": {
      "value": "Environment"
    }
  }'

# List policy assignments
az policy assignment list --scope "/subscriptions/{subscription-id}" --output table

Azure Policy JSON Examples: Monitoring and Compliance Automation

Azure Policy provides comprehensive compliance monitoring and reporting capabilities to track your governance posture across all resources.

Viewing Compliance Status

Compliance Commands

# Get compliance summary for a subscription
az policy state summarize --scope "/subscriptions/{subscription-id}"

# Get detailed compliance for specific assignment
az policy state list \
  --assignment-name "security-baseline-assignment" \
  --scope "/subscriptions/{subscription-id}" \
  --filter "ComplianceState eq 'NonCompliant'" \
  --output table

# Get compliance for a specific resource
az policy state list \
  --resource "/subscriptions/{sub-id}/resourceGroups/{rg-name}/providers/{provider}/{resource-name}" \
  --output table

# Trigger compliance evaluation
az policy state trigger-scan --scope "/subscriptions/{subscription-id}"

PowerShell Compliance Reporting

compliance-report.ps1

# Connect to Azure
Connect-AzAccount

# Get compliance summary
$complianceSummary = Get-AzPolicyStateSummary -SubscriptionId "{subscription-id}"

# Display compliance by policy assignment
$complianceSummary.PolicyAssignments | ForEach-Object {
    Write-Host "Assignment: $($_.PolicyAssignmentId)"
    Write-Host "Compliant Resources: $($_.Results.ResourceDetails.ComplianceState.Compliant)"
    Write-Host "Non-Compliant Resources: $($_.Results.ResourceDetails.ComplianceState.NonCompliant)"
    Write-Host "---"
}

# Get detailed non-compliant resources
$nonCompliantResources = Get-AzPolicyState -SubscriptionId "{subscription-id}" | 
    Where-Object { $_.ComplianceState -eq "NonCompliant" }

# Export to CSV
$nonCompliantResources | Export-Csv -Path "compliance-report.csv" -NoTypeInformation

# Generate summary report
$report = @{
    TotalResources = $complianceSummary.Results.ResourceDetails.TotalResources
    CompliantResources = $complianceSummary.Results.ResourceDetails.ComplianceState.Compliant
    NonCompliantResources = $complianceSummary.Results.ResourceDetails.ComplianceState.NonCompliant
    CompliancePercentage = [math]::Round(($complianceSummary.Results.ResourceDetails.ComplianceState.Compliant / $complianceSummary.Results.ResourceDetails.TotalResources) * 100, 2)
}

Write-Host "Compliance Summary:"
Write-Host "Total Resources: $($report.TotalResources)"
Write-Host "Compliant: $($report.CompliantResources)"
Write-Host "Non-Compliant: $($report.NonCompliantResources)"
Write-Host "Compliance Rate: $($report.CompliancePercentage)%"

Azure Resource Governance Policies: Automated Remediation Guide

Azure Policy can automatically remediate non-compliant resources using remediation tasks and policies with modify or deployIfNotExists effects.

Creating Remediation Tasks

Create Remediation Task

# Create remediation task for non-compliant resources
az policy remediation create \
  --name "tag-remediation-task" \
  --policy-assignment "/subscriptions/{sub-id}/providers/Microsoft.Authorization/policyAssignments/require-tags" \
  --scope "/subscriptions/{subscription-id}" \
  --resource-discovery-mode "ExistingNonCompliant"

# Monitor remediation progress
az policy remediation show \
  --name "tag-remediation-task" \
  --scope "/subscriptions/{subscription-id}"

# List all remediation tasks
az policy remediation list --scope "/subscriptions/{subscription-id}"

DeployIfNotExists Policy Example

deploy-diagnostic-settings.json

{
  "properties": {
    "displayName": "Deploy Diagnostic Settings for Storage Accounts",
    "description": "Deploys diagnostic settings for storage accounts to a Log Analytics workspace",
    "policyType": "Custom",
    "mode": "Indexed",
    "parameters": {
      "logAnalyticsWorkspaceId": {
        "type": "String",
        "metadata": {
          "displayName": "Log Analytics Workspace ID",
          "description": "Resource ID of the Log Analytics workspace"
        }
      }
    },
    "policyRule": {
      "if": {
        "field": "type",
        "equals": "Microsoft.Storage/storageAccounts"
      },
      "then": {
        "effect": "deployIfNotExists",
        "details": {
          "type": "Microsoft.Insights/diagnosticSettings",
          "name": "storageAccountDiagnosticSettings",
          "existenceCondition": {
            "field": "Microsoft.Insights/diagnosticSettings/workspaceId",
            "equals": "[parameters('logAnalyticsWorkspaceId')]"
          },
          "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": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                "contentVersion": "1.0.0.0",
                "parameters": {
                  "storageAccountName": {
                    "type": "string"
                  },
                  "workspaceId": {
                    "type": "string"
                  }
                },
                "resources": [
                  {
                    "type": "Microsoft.Storage/storageAccounts/providers/diagnosticSettings",
                    "apiVersion": "2017-05-01-preview",
                    "name": "[concat(parameters('storageAccountName'), '/Microsoft.Insights/storageAccountDiagnosticSettings')]",
                    "properties": {
                      "workspaceId": "[parameters('workspaceId')]",
                      "logs": [
                        {
                          "category": "StorageRead",
                          "enabled": true
                        },
                        {
                          "category": "StorageWrite",
                          "enabled": true
                        }
                      ],
                      "metrics": [
                        {
                          "category": "Transaction",
                          "enabled": true
                        }
                      ]
                    }
                  }
                ]
              },
              "parameters": {
                "storageAccountName": {
                  "value": "[field('name')]"
                },
                "workspaceId": {
                  "value": "[parameters('logAnalyticsWorkspaceId')]"
                }
              }
            }
          }
        }
      }
    }
  }
}

Azure Policy Best Practices: DevOps Pipeline Integration

Integrate Azure Policy validation into your CI/CD pipelines to ensure infrastructure compliance before deployment.

Azure DevOps Pipeline

azure-pipelines.yml

trigger:
  branches:
    include:
    - main
    - develop
  paths:
    include:
    - infrastructure/*
    - policies/*

variables:
  azureSubscription: 'your-service-connection'
  resourceGroupName: 'policy-governance-rg'
  
stages:
- stage: PolicyValidation
  displayName: 'Policy Validation'
  jobs:
  - job: ValidatePolicies
    displayName: 'Validate Policy Definitions'
    pool:
      vmImage: 'ubuntu-latest'
    
    steps:
    - task: AzureCLI@2
      displayName: 'Validate Policy Definitions'
      inputs:
        azureSubscription: $(azureSubscription)
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
          echo "Validating policy definitions..."
          
          # Validate JSON syntax
          find policies/ -name "*.json" | while read policy; do
            echo "Validating $policy"
            if ! jq empty "$policy"; then
              echo "Invalid JSON in $policy"
              exit 1
            fi
          done
          
          # Test create policy definitions (dry run)
          find policies/definitions/ -name "*.json" | while read policy; do
            policyName=$(basename "$policy" .json)
            echo "Testing policy definition: $policyName"
            
            az policy definition create \
              --name "test-$policyName" \
              --rules "$policy" \
              --mode Indexed \
              --subscription $(az account show --query id -o tsv) \
              --only-show-errors || exit 1
              
            # Clean up test policy
            az policy definition delete \
              --name "test-$policyName" \
              --subscription $(az account show --query id -o tsv)
          done

- stage: InfrastructureValidation
  displayName: 'Infrastructure Validation'
  dependsOn: PolicyValidation
  jobs:
  - job: ValidateTemplates
    displayName: 'Validate ARM Templates'
    pool:
      vmImage: 'ubuntu-latest'
      
    steps:
    - task: AzureCLI@2
      displayName: 'Deploy and Test Infrastructure'
      inputs:
        azureSubscription: $(azureSubscription)
        scriptType: 'bash'
        scriptLocation: 'inlineScript'
        inlineScript: |
          # Create test resource group
          az group create \
            --name "test-$(resourceGroupName)" \
            --location "East US"
          
          # Validate ARM template
          az deployment group validate \
            --resource-group "test-$(resourceGroupName)" \
            --template-file infrastructure/main.bicep \
            --parameters infrastructure/parameters.json
          
          # Deploy with what-if to see policy impact
          az deployment group what-if \
            --resource-group "test-$(resourceGroupName)" \
            --template-file infrastructure/main.bicep \
            --parameters infrastructure/parameters.json
          
          # Clean up test resource group
          az group delete \
            --name "test-$(resourceGroupName)" \
            --yes --no-wait

- stage: PolicyDeployment
  displayName: 'Deploy Policies'
  dependsOn: InfrastructureValidation
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  jobs:
  - deployment: DeployPolicies
    displayName: 'Deploy Policy Definitions and Assignments'
    environment: 'Production'
    pool:
      vmImage: 'ubuntu-latest'
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureCLI@2
            displayName: 'Deploy Policy Definitions'
            inputs:
              azureSubscription: $(azureSubscription)
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                # Deploy policy definitions
                find policies/definitions/ -name "*.json" | while read policy; do
                  policyName=$(basename "$policy" .json)
                  echo "Deploying policy definition: $policyName"
                  
                  az policy definition create \
                    --name "$policyName" \
                    --rules "$policy" \
                    --mode Indexed \
                    --subscription $(az account show --query id -o tsv)
                done
                
                # Deploy policy initiatives
                find policies/initiatives/ -name "*.json" | while read initiative; do
                  initiativeName=$(basename "$initiative" .json)
                  echo "Deploying policy initiative: $initiativeName"
                  
                  az policy set-definition create \
                    --name "$initiativeName" \
                    --definitions "$initiative" \
                    --subscription $(az account show --query id -o tsv)
                done
                
                # Deploy policy assignments
                find policies/assignments/ -name "*.json" | while read assignment; do
                  assignmentName=$(basename "$assignment" .json)
                  echo "Deploying policy assignment: $assignmentName"
                  
                  az deployment sub create \
                    --location "East US" \
                    --template-file "$assignment" \
                    --parameters subscriptionId=$(az account show --query id -o tsv)
                done
          
          - task: AzureCLI@2
            displayName: 'Generate Compliance Report'
            inputs:
              azureSubscription: $(azureSubscription)
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                # Wait for policy evaluation
                echo "Waiting for policy evaluation..."
                sleep 300
                
                # Generate compliance report
                az policy state summarize \
                  --scope "/subscriptions/$(az account show --query id -o tsv)" \
                  --output table
                
                # Trigger compliance scan
                az policy state trigger-scan \
                  --scope "/subscriptions/$(az account show --query id -o tsv)"

Azure Policy Best Practices

๐ŸŽฏ

Policy Design Principles

Start with audit effects before enforcement, use meaningful names and descriptions, implement parameterized policies for flexibility, and group related policies into initiatives.

๐Ÿ“Š

Scope Management

Assign at appropriate scope (subscription/RG), use exclusions judiciously, test at smaller scopes first, and document scope decisions and rationale.

๐Ÿงช

Testing Strategy

Validate JSON syntax automatically, test with representative resources, use What-If deployments, and monitor compliance after deployment.

๐Ÿ”„

Lifecycle Management

Version control all policy definitions, use systematic naming conventions, regular review and cleanup, and plan for policy deprecation.

๐Ÿ“š

Documentation

Clear business justification, remediation guidance, exception handling process, and contact information for questions.

โšก

Performance

Optimize policy rule complexity, monitor evaluation performance, use appropriate policy modes, and consider resource provider limits.

Troubleshooting Common Issues

Policy Definition Issues

โŒ Policy not evaluating resources

Problem: Policy assignment exists but compliance shows no resources evaluated.

Solutions:

  • Check policy mode (All vs Indexed)
  • Verify resource types in scope
  • Check assignment scope and exclusions
  • Wait for evaluation cycle (can take up to 30 minutes)

โŒ Remediation task failing

Problem: Remediation tasks fail with permission or template errors.

Solutions:

  • Verify managed identity has required permissions
  • Check ARM template syntax in policy
  • Review activity logs for detailed error messages
  • Test template deployment separately

Assignment Issues

โŒ Unexpected policy violations

Problem: Resources showing as non-compliant unexpectedly.

Solutions:

  • Review policy rule logic carefully
  • Check for case sensitivity in field values
  • Verify parameter values in assignment
  • Use compliance details to understand violations

๐ŸŽ‰ Congratulations!

Azure Policy Governance Mastery

You now have comprehensive knowledge of Azure Policy implementation including:

โœ…

Custom Policy Definitions

Create and manage custom policy definitions and initiatives for comprehensive governance.

โœ…

Compliance Monitoring

Implement compliance monitoring and reporting to track your governance posture.

โœ…

Automated Remediation

Set up automated remediation for non-compliant resources using policy effects.

โœ…

DevOps Integration

Integrate policies into DevOps pipelines for continuous compliance validation.

โœ…

Enterprise Best Practices

Apply proven patterns for enterprise-scale Azure governance and policy management.

โœ…

Troubleshooting Skills

Debug and resolve common policy issues with systematic approaches.

Next Steps