Skip to content

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-epac repository.

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:

  1. Build-DeploymentPlans compares Definitions/ with the live tenant and writes a plan.
  2. Deploy-PolicyPlan creates or updates definitions, initiatives, assignments and exemptions.
  3. Deploy-RolesPlan assigns roles to the managed identities that DeployIfNotExists and Modify policies need.
  4. Build-PolicyDocumentation regenerates 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:

  1. Ship as Audit first. Leave it for at least one plan cycle, so the number of non-compliant resources is visible.
  2. Fix or exempt what is non-compliant.
  3. Promote to Deny or Modify in a separate MR, and set that assignment's enforcementMode to Default.
  4. 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.