Compliance as Code — Azure Policy

Automating Governance at Scale


Introduction

Compliance as Code with Azure Policy enables you to define, enforce, and audit cloud resources programmatically. Instead of manual reviews and sporadic checks, policies continuously ensure your integration workloads adhere to organizational standards, security requirements, and regulatory compliance. This approach shifts compliance left—catching issues before they reach production.

This comprehensive guide covers:

  • Policy fundamentals — Understanding Azure Policy
  • Policy definition — Creating custom policies
  • Initiatives — Grouping related policies
  • Assignment — Applying policies to resources
  • Remediation — Automatic issue fixing
  • Enterprise governance — At scale management

Policy Fundamentals

How Azure Policy Works

┌─────────────────────────────────────────────────────────────────────┐
│                  AZURE POLICY ARCHITECTURE                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   POLICY DEFINITION                                                 │
│   (What rules to enforce)                                           │
│   └─── "Storage accounts must use HTTPS"                            │
│   └─── "Functions must use managed identity"                        │
│   └─── "Service Bus must have firewall enabled"                     │
│                                                                     │
│        ↓                                                            │
│                                                                     │
│   POLICY ASSIGNMENT                                                 │
│   (Where to apply)                                                  │
│   └─── Subscription: Production                                     │
│   └─── Resource Group: rg-integration                               │
│   └─── Resource: specific-function-app                              │
│                                                                     │
│        ↓                                                            │
│                                                                     │
│   EVALUATION                                                        │
│   (When to check)                                                   │
│   └─── On resource create/update                                    │
│   └─── On assignment                                                │
│   └─── Periodic (daily/weekly)                                      │
│   └─── On demand                                                    │
│                                                                     │
│        ↓                                                            │
│                                                                     │
│   EFFECTS                                                           │
│   (What happens)                                                    │
│   └─── Deny: Block non-compliant resources                          │
│   └─── Audit: Log non-compliance                                    │
│   └─── DeployIfNotExists: Auto-remediation                          │
│   └─── Modify: Add/remove tags                                      │
│   └─── AuditIfNotExists: Check nested resources                     │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Built-in Policies

# List policies for integration services
az policy definition list \
  --query "[?contains(displayName, 'Function') || contains(displayName, 'Storage')]"

# Get specific policy
az policy definition show \
  --name "Function App should use latest TLS version"

# List initiatives
az policy initiative list

Creating Custom Policies

Policy Structure

{
  "name": "require-https-for-servicebus",
  "type": "Microsoft.Authorization/policyDefinitions",
  "properties": {
    "displayName": "Service Bus should require HTTPS",
    "description": "Enforce HTTPS for Service Bus namespace to ensure data in transit is encrypted",
    "mode": "All",
    "metadata": {
      "version": "1.0.0",
      "category": "Network Security"
    },
    "parameters": {
      "effect": {
        "type": "String",
        "defaultValue": "Deny",
        "allowedValues": ["Deny", "Audit", "Disabled"],
        "metadata": {
          "displayName": "Effect",
          "description": "The effect determines what happens when the policy rule is evaluated"
        }
      }
    },
    "policyRule": {
      "if": {
        "anyOf": [
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.ServiceBus/namespaces"
              },
              {
                "field": "Microsoft.ServiceBus/namespaces/requiresGuestAccessAPIs",
                "equals": "true"
              }
            ]
          }
        ]
      },
      "then": {
        "effect": "[parameters('effect')]"
      }
    }
  }
}

Integration-Specific Policies

{
  "policies": [
    {
      "name": "functions-use-managed-identity",
      "description": "Azure Functions should have managed identity enabled",
      "effect": "deployIfNotExists",
      "resourceType": "Microsoft.Web/sites",
      "condition": "type == 'Microsoft.Web/sites' AND kind contains 'function'",
      "deployment": {
        "properties": {
          "mode": "incremental",
          "parameters": {
            "functionAppName": {
              "value": "[field('name')]"
            }
          },
          "template": {
            "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
            "resources": [
              {
                "type": "Microsoft.Web/sites",
                "identity": {
                  "type": "SystemAssigned"
                }
              }
            ]
          }
        }
      }
    },
    {
      "name": "servicebus-queue-lifecycle-policy",
      "description": "Service Bus queues should have TTL set",
      "effect": "auditIfNotExists",
      "resourceType": "Microsoft.ServiceBus/namespaces/queues",
      "condition": "type == 'Microsoft.ServiceBus/namespaces/queues'",
      "existenceCondition": {
        "field": "properties.defaultMessageTimeToLive",
        "greater": "P1D"
      }
    }
  ]
}

Policy Initiatives

Initiative Definition

{
  "name": "integration-security-baseline",
  "type": "Microsoft.Authorization/policyInitiatives",
  "properties": {
    "displayName": "Integration Security Baseline",
    "description": "Security baseline for integration workloads",
    "metadata": {
      "version": "1.0.0",
      "category": "Security"
    },
    "policyDefinitions": [
      {
        "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/function-app-should-use-managed-identity",
        "parameters": {}
      },
      {
        "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/storage-uses-minimum-tls",
        "parameters": {
          "minimumTlsVersion": {
            "value": "TLS1_2"
          }
        }
      },
      {
        "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/servicebus-enable-private-endpoint",
        "parameters": {
          "effect": {
            "value": "Deny"
          }
        }
      },
      {
        "policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/api-management-disallow-public-access",
        "parameters": {}
      }
    ]
  }
}

Assign Initiative

# Assign initiative to subscription
az policy assignment create \
  --name "integration-security-baseline" \
  --display-name "Integration Security Baseline" \
  --description "Security controls for integration workloads" \
  --policy-set-definition "integration-security-baseline" \
  --scope "/subscriptions/xxx" \
  --params '{
    "listOfResourceTypesToAudit": {
      "value": ["Microsoft.Web/sites", "Microsoft.ServiceBus/namespaces", "Microsoft.Storage/storageAccounts"]
    }
  }'

# Assign to specific resource group
az policy assignment create \
  --name "integration-compliance" \
  --policy-set-definition "integration-security-baseline" \
  --scope "/subscriptions/xxx/resourceGroups/rg-integration"

Remediation

Automatic Remediation

{
  "remediationTask": {
    "name": "enable-storage-https",
    "properties": {
      "policyAssignmentId": "/subscriptions/xxx/providers/Microsoft.Authorization/policyAssignments/storage-compliance",
      "policyDefinitionGuid": "xxx-xxx-xxx",
      "resourceDiscoveryMode": "ExistingNonCompliant"
    }
  }
}
# Create remediation task
az policy remediation create \
  --name "fix-https-requirement" \
  --policy-assignment "storage-compliance" \
  --resource-group "rg-integration"

# List non-compliant resources
az policy state list \
  --query "[?complianceState=='NonCompliant']"

# View remediation status
az policy remediation show \
  --name "fix-https-requirement"

Enterprise Governance

Governance at Scale

{
  "enterpriseGovernance": {
    "managementGroups": {
      "root": {
        "displayName": "Contoso",
        "children": [
          {
            "displayName": "Production",
            "policyAssignments": ["prod-security-baseline"]
          },
          {
            "displayName": "Development",
            "policyAssignments": ["dev-security-baseline"]
          }
        ]
      }
    },
    "exemptions": {
      "allowByDefault": false,
      "requireJustification": true,
      "maxDurationDays": 90,
      "reviewRequired": true
    }
  }
}

Policy as Code

# GitHub workflow for policy management
name: Policy Pipeline

on:
  push:
    paths:
      - 'policies/**'
    branches:
      - main

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Validate policy JSON
        run: |
          for file in policies/*.json; do
            jq empty "$file" || exit 1
          done
      
      - name: Check policy structure
        run: |
          # Validate required fields
          az deployment validate \
            --location eastus \
            --template-file policies/test-template.json

  deploy:
    needs: validate
    runs-on: ubuntu-latest
    steps:
      - name: Deploy policies
        run: |
          for file in policies/definitions/*.json; do
            az policy definition create \
              --name $(basename "$file" .json) \
              --mode All \
              --policy "$file"
          done

Best Practices

Implementation Checklist

PracticeDescription
Start with auditUse audit effect before deny
Use initiativesGroup related policies
Enable remediationAuto-fix where possible
Use exemptions wiselyDocument and limit duration
Version policiesTrack changes in source control
Review complianceRegular compliance reports

Policy Design Guidelines

┌─────────────────────────────────────────────────────────────────────┐
│                  POLICY DESIGN GUIDELINES                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ✓ Name clearly: describe-what-where-when                          │
│   ✓ Document: displayName, description, category                    │
│   ✓ Version: track changes in metadata                              │
│   ✓ Test first: use audit mode before deny                          │
│   ✓ Keep simple: one policy, one purpose                            │
│   ✓ Parameterize: make policies configurable                        │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Related Topics


Azure Integration Hub - Architect Level Security Architecture & Zero Trust