CloudGuru

Open-source tool · AZPaC

Azure Policy as Code (AZPaC)

Manage Azure Policy with Terraform and pull requests.

AZPaC manages Azure Policy with Terraform. You write a policy once, put it in an initiative and decide where it applies, all in files reviewed through a pull request. The Terraform engine is generic: you only edit policy JSON files and one tfvars file per initiative. It runs against a real tenant, and the pipelines deploy it.

Built with
Terraform (HCL)
Pipelines
3 (Azure DevOps)
Modules
definition, initiative, assignment, exemption
License
MIT

Why it exists

Questions it answers

Who created this assignment, and why?

Every change is a pull request with history and a reviewer.

Why is a "temporary" exemption still here years later?

Every exemption needs an expiry date and an approver who is not the requester. The pipeline enforces both.

How do I turn enforcement on safely?

One switch per initiative: mode = "audit" or "enforce", changed in one file and approved by someone else.

What changed outside Terraform?

A nightly drift pipeline finds Portal edits and assignments or exemptions that Terraform cannot see.

Features

What it does

One switch for enforcement

  • Write each effect once, as the final goal (for example Deny)
  • Audit mode downgrades Deny to Audit and DeployIfNotExists to AuditIfNotExists
  • Audit mode also sets the assignment to DoNotEnforce, a second safety layer
  • Promoting is a one-word change

Scopes and exemptions

  • Assign to management groups so new subscriptions inherit
  • Per-subscription parameter overrides only where values differ
  • Exemptions as Waiver or Mitigated, with owner, ticket and expiry
  • Built-in policies referenced by GUID, custom ones from JSON files

Pipelines

  • Definitions: validate, plan per initiative, then apply the saved plan after approval
  • Drift: nightly check for Portal changes and unmanaged policy objects
  • Remediation: weekly tasks that fix existing non-compliant resources
  • Applies run one after another, so a failure stops the chain

Conventions

  • Definitions filed by Azure category, shared between initiatives
  • Names follow <Effect>-<Subject>-<What>, as in the Azure Landing Zone library
  • The filename is the policy name and the reference ID
  • Documented rollback, from seconds to minutes, without terraform destroy

How it fits together

Architecture

  1. 01Policy JSON + initiative tfvars in Git
  2. 02Pull request: validate and plan
  3. 03Approval
  4. 04Apply the saved plan
  5. 05Nightly drift + weekly remediation

Quick start

Try it

Deploy an initiative
cd tf-policy
# set tenant and management group in globals.auto.tfvars
# point backend-config/INE01_backend.conf at your state storage
terraform init -backend-config=backend-config/INE01_backend.conf
terraform plan  -var-file=initiatives/INE01.tfvars
terraform apply -var-file=initiatives/INE01.tfvars
Check built-in GUIDs against your tenant first
az policy definition show --name <guid> --query displayName -o tsv

The full setup guide is in the README on GitHub.

Before you start

Permissions needed

Definitions, initiatives, assignmentsResource Policy Contributor on the management group
Terraform stateStorage Blob Data Contributor on the state storage account
Remediation (when enforcing)User Access Administrator or Role Based Access Control Administrator, ideally on a separate service principal

Good to know

Always start in audit mode: the list of failures is the list of things to fix or exempt before you enforce. Start with the small INE02 tagging initiative. Never use terraform destroy to fix a policy problem, because it removes every control and exemption for that initiative.