If you’ve ever had to answer, “Who changed that Azure Policy… and why?” you already know the problem with portal-driven governance:
Changes happen fast (usually under pressure).
The “why” gets lost (or lives in someone’s head).
Reproducing the same governance across tenants becomes a manual grind.
Drift quietly accumulates until an audit—or an incident—forces the cleanup.
This year, I decided to treat Azure governance like engineering, not administration.
I migrated a manual Azure Policy “clickops” deployments into a fully traceable, PR-driven CI/CD model using GitHub and GitHub Actions, and I refactored and successfully migrated 250+ custom policy definitions and their assignments in a single day. The repo now contains 267 policy definition JSON files (excluding assignments, exemptions, parameters, and tenant metadata), deployed consistently across a half dozen Microsoft Azure Entra tenants.
This is the story of what changed, how it works, and why it matters.
The real issue wasn’t policy. It was delivery.
Azure Policy is powerful. But most organizations implement it like this:
Someone exports a JSON.
Someone else edits it.
Someone applies it in the portal.
Weeks later, nobody can explain the difference between “intended state” and “what’s actually running.”
That approach breaks down the moment you have:
multiple management groups,
multiple environments,
multiple tenants,
and real expectations around auditability and change control.
My goal captured the intent perfectly:
Policy-as-Code Meets Infra-as-Code: Automated. Accurate. Auditable
So I focused on one outcome: make governance changes behave like product changes; reviewed, tested, traceable, and repeatable.
The target state: governance with an audit trail
The end state I wanted was simple:
Commit → PR → CI validation → controlled deployment → verified enforcement
Not “someone clicked apply.”
That meant building a real operating model:
a standardized repo structure that can scale across tenants
automation that limits blast radius by deploying only what changed
safe execution modes (including dry-run)
guardrails to prevent accidental assignment sprawl
The repo model: multi-tenant, category-driven, repeatable
At the core is a multi-tenant repository structure designed to be boring (in the best way):
Tenant-scoped layout (e.g.,
tenants/<tenant>/...)Custom categories (e.g.,
tenants/<tenant>/custom/<category>/...)Tenant metadata (e.g.,
tenant.json) to drive deterministic deployments
That sounds like “just folders,” but it solves a huge problem: it turns “tribal knowledge” into discoverable structure. New people can navigate governance without needing a guided tour.