Azure Policy (EPAC)¶
Azure Policy is how the platform states rules for every resource in the tenant, and checks them continuously: required tags, allowed configurations, required logging. All policy is managed as code with Enterprise Policy as Code (EPAC), deployed from the
azure-governance-epacrepository.
- Tagging Standard
- Exceptions Register
- Regulatory Compliance
- Microsoft Learn: Enterprise Policy as Code
Status: in place, audit-only
Every policy assignment currently runs with enforcementMode: DoNotEnforce. Policies report non-compliance but block nothing and fix nothing. This applies even where an assignment's name or effect says "Deny" or "DeployIfNotExists". Moving selected guardrails to enforcement is planned, and tracked in ADR-0001.
How it is organised¶
EPAC (v11.5.1) keeps every policy artefact as JSONC in Definitions/:
| Folder | Contains |
|---|---|
policyDefinitions/ |
Custom policy definitions |
policySetDefinitions/ |
Custom initiatives, one per domain (psd-*) |
policyAssignments/ |
Assignments, grouped by domain: backup, compute, general, key vault, monitoring, network, regulatory compliance, security, SQL, storage and tags |
policyExemptions/ |
Time-limited exemptions |
global-settings.jsonc |
The tenant, root scope and lifecycle settings |
All assignments target mg-intermediate-root, so they apply to every management group and subscription beneath it. EPAC runs with strategy: full: it removes any policy assignment in its scope that is not in the repository. The one exception is Defender for Cloud's own automatic assignments (keepDfcSecurityAssignments: true).
The three-layer model (ADR-0001)¶
| Layer | What | Allowed effects |
|---|---|---|
| Detective standards | Microsoft Cloud Security Benchmark, ISO 27001, ENS | Audit and AuditIfNotExists only. They are never raised to Deny. |
| Preventive guardrails | Custom psd-* initiatives per domain |
Deny, DenyAction, Modify |
| Configuration remediation | Defender for Cloud and diagnostic settings | DeployIfNotExists |
The layers are kept apart on purpose. A Microsoft-owned benchmark of about 240 policies is never switched to blocking. Anything that must block is added to the matching custom guardrail initiative instead.
How changes are deployed¶
The GitLab pipeline (shared ci-templates/epac-pipeline.yml) runs the standard EPAC stages:
Build-DeploymentPlanscomparesDefinitions/with the live tenant and writes a plan.Deploy-PolicyPlancreates or updates definitions, initiatives, assignments and exemptions.Deploy-RolesPlanassigns roles to the managed identities thatDeployIfNotExistsandModifypolicies need.Build-PolicyDocumentationregenerates the assignment documentation from the live state.
Tier classification
Step 3 creates role assignments at mg-intermediate-root, so the deploy identity holds Role Based Access Control Administrator there. Under the Role Tier Mapping, that is Tier 0 by mechanism, and it is applied from CI. This is recorded in the Exceptions Register.
From audit to enforcement¶
ADR-0001 sets the rule for making any control blocking:
- Ship as
Auditfirst. Leave it for at least one plan cycle, so the number of non-compliant resources is visible. - Fix or exempt what is non-compliant.
- Promote to
DenyorModifyin a separate MR, and set that assignment'senforcementModetoDefault. - Handle exceptions with exemptions, never by weakening the effect.
For DeployIfNotExists assignments, enforcementMode: Default also makes new resources compliant automatically. Existing resources still need one remediation task.
Exemptions¶
An exemption is the only approved way to exclude something from a policy. The rules, from Definitions/policyExemptions/README.md:
| Rule | Detail |
|---|---|
| Expiry | Mandatory, no more than 90 days away. Renewing needs a new MR with a new justification. |
| Scope | As narrow as possible: a resource or resource group, not a subscription. |
| Members | Exempt specific initiative members rather than a whole assignment. |
| Category | Mitigated if a compensating control exists, Waiver if the risk is simply accepted |
| Approval | A CODEOWNER for the exemptions folder approves. Self-approval is not allowed. |
| Review | Monthly, against each exemption's ticket |
There are no active exemptions today. Controls that can never apply by design, such as "more than one subscription owner", are disabled rather than exempted, in azure-mcsb-overrides. See Defender for Cloud Secure Score.