Problem Context

Your security team mandates that all storage accounts must have public access disabled, all resources must be tagged with CostCenter and Owner, and no VMs may use non-approved SKUs. You enforce this through documentation and code review — which means it only applies to new resources, and only when reviewers catch violations.

Policy as Code bakes compliance rules into your infrastructure platform itself. Rules are defined in code, version-controlled, tested, and enforced automatically — both at deployment time (preventing non-compliant resources) and continuously (detecting and remediating existing violations).

🤔 Sound familiar?
  • Security audit found 40 storage accounts with public access enabled — all created before the policy
  • New engineers deploy without tags, breaking your cost allocation reports
  • You're in a regulated industry and need continuous compliance evidence, not point-in-time audits
  • Policy docs sit in Confluence and nobody reads them before deploying

Policy as code shifts compliance left — enforce it at the platform level, not the review layer.

Azure Policy: The Native Approach

Azure Policy evaluates resources against rules and takes actions: Deny (block deployment), Audit (flag violations), Modify (add/change properties automatically), or DeployIfNotExists (deploy remediation resources).

Custom policy definition

{
  "mode": "Indexed",
  "policyRule": {
    "if": {
      "allOf": [
        { "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
        { "field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess", "equals": "true" }
      ]
    },
    "then": {
      "effect": "Deny"
    }
  },
  "parameters": {}
}

Deploy the policy with Bicep

// policies/deny-storage-public-access.bicep
targetScope = 'subscription'

resource policyDef 'Microsoft.Authorization/policyDefinitions@2021-06-01' = {
  name: 'deny-storage-public-access'
  properties: {
    displayName: 'Deny Storage Accounts with Public Blob Access'
    description: 'Blocks creation of storage accounts that allow public blob access'
    mode: 'Indexed'
    policyRule: {
      if: {
        allOf: [
          { field: 'type', equals: 'Microsoft.Storage/storageAccounts' }
          { field: 'Microsoft.Storage/storageAccounts/allowBlobPublicAccess', equals: 'true' }
        ]
      }
      then: { effect: 'Deny' }
    }
  }
}

resource policyAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = {
  name: 'assign-deny-storage-public-access'
  properties: {
    policyDefinitionId: policyDef.id
    displayName: 'Deny Storage Accounts with Public Blob Access'
    enforcementMode: 'Default'
  }
}

Policy Initiatives (Policy Sets)

An initiative groups multiple policies into a single assignment. Use initiatives to package compliance frameworks (e.g., “CIS Azure Benchmark”, “Internal Security Baseline”):

resource initiative 'Microsoft.Authorization/policySetDefinitions@2021-06-01' = {
  name: 'security-baseline'
  properties: {
    displayName: 'Company Security Baseline'
    policyDefinitions: [
      {
        policyDefinitionId: denyPublicStoragePolicy.id
        policyDefinitionReferenceId: 'deny-public-storage'
      }
      {
        policyDefinitionId: requireTagsPolicy.id
        policyDefinitionReferenceId: 'require-tags'
        parameters: {
          requiredTags: { value: ['CostCenter', 'Owner', 'Environment'] }
        }
      }
    ]
  }
}

Open Policy Agent (OPA) / Conftest

For tool-agnostic policy (Terraform, Kubernetes, GitHub Actions, Dockerfile), OPA with Conftest lets you write policies in Rego that run in CI before deployment:

# policy/terraform/deny_public_storage.rego
package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "azurerm_storage_account"
  resource.change.after.allow_blob_public_access == true
  msg := sprintf("Storage account '%v' must not allow public blob access", [resource.address])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "azurerm_storage_account"
  not resource.change.after.tags["CostCenter"]
  msg := sprintf("Storage account '%v' missing required tag: CostCenter", [resource.address])
}
# Run policy checks against terraform plan output
terraform show -json tfplan > plan.json
conftest test plan.json --policy policy/terraform/

GitHub Actions integration

- name: Terraform Plan
  run: terraform plan -out=tfplan

- name: Export Plan JSON
  run: terraform show -json tfplan > plan.json

- name: Policy Check with Conftest
  uses: instrumenta/conftest-action@v1
  with:
    files: plan.json
    policy: policy/terraform/

Checkov: IaC Security Scanning

Checkov is a static analysis tool for Terraform, Bicep, ARM, CloudFormation, and Kubernetes manifests. It ships with 1000+ pre-built policies and integrates directly into CI:

# Scan Terraform directory
checkov -d ./infra --framework terraform

# Scan Bicep files
checkov -d ./infra --framework bicep

# Output only failures, block on high severity
checkov -d ./infra --framework terraform \
  --compact --quiet \
  --check HIGH \
  --output cli --output junitxml \
  --output-file-path results/

Terraform Sentinel

For Terraform Cloud/Enterprise users, Sentinelis HashiCorp's policy-as-code framework. Policies run between plan and apply and can hard-stop deployments:

# sentinel.hcl
policy "deny-public-storage" {
  source = "./policies/deny-public-storage.sentinel"
  enforcement_level = "hard-mandatory"
}

policy "require-tags" {
  source = "./policies/require-tags.sentinel"
  enforcement_level = "soft-mandatory"   # Advisory — can override with justification
}

Production Checklist

  • Store all policy definitions alongside infrastructure code — same repo, same PR process
  • Run Checkov/Conftest in CI on every PR that touches infrastructure
  • Start with Audit effect before switching to Deny — build a compliance baseline first
  • Use initiatives to package related policies; assign initiatives, not individual policies
  • Create remediation tasks for existing non-compliant resources after enabling Deny
  • Test policy logic with az policy state trigger-scan and validate outputs before production assignment
  • Export compliance reports on a schedule for audit evidence