Skip to content

Policy Exemptions (Request, Review, Deploy)

Standard change: Policy exemption.

Effect: a resource, resource group, subscription or management group is excluded from an Azure Policy assignment, for a limited time. The exemption is code: a JSON file that is reviewed, approved and deployed like every other change.

This page is the process for the platform team. The JSON format and the folders are described under Policy assignments. The workload team's view is in Policy and exemptions. The governance rule behind it is in Governance.

When an exemption is the right answer

An exemption is the last option. Ask first:

  1. Can the resource change so that it meets the policy? That is always better.
  2. Is the policy assigned at the right scope, with the right parameters? A wrong assignment is a change to the assignment, not an exemption (Policy assignments).
  3. Is the risk accepted or handled in another way? Only then, an exemption.

Categories

Category Meaning Use it when
Waiver The risk is knowingly accepted. The control does not fit the resource, and the owner and security accept the risk.
Mitigated The risk is handled by another control. A compensating control exists, for example a private network path instead of a policy-required private endpoint. Name the control in the description.

In Azure Policy, both categories show the resource as exempt. The difference is the documentation: it tells a later reader whether somebody accepted a risk or fixed it elsewhere.

Request

The requester provides:

Information Description
Policy or initiative The exact name or ID, and the assignment it comes from
Scope Subscription, resource group, or specific resources. Use the smallest scope that works.
Category Waiver or mitigated
Justification Why the resource can't comply
Alternative measures Which other controls reduce the risk
End date When the exemption is reviewed again. Choose a short period, normally at most one year.
Approval The approval of the security officer, with a reference

Process

The exemption process has an approval step before any code is written. A common way to set it up:

flowchart LR
    a["Workload team finds that an exemption is needed"] --> b["Request with justification and end date"]
    b --> c{"Approved by security officer?"}
    c -->|no| d["Change the design"]
    c -->|yes| e["Exemption file in a branch"]
    e --> f["Pull request linked to the ticket"]
    f --> g["Automatic checks of the file"]
    g --> h["Approval of the pull request"]
    h --> i["Pipeline deploys the exemption"]
  1. The workload team posts the request. The platform team checks that the fields are complete.
  2. The security officer (or the body that decides about exemptions in your organisation) approves or rejects the request.
  3. The platform team creates a branch and adds the exemption file in the folder for its scope (policies/policy_exemptions/01_management_groups to 04_resources). The expires_on date is always set.
  4. A pull request, linked to the ticket, goes through the pull request review.
  5. After approval, hub deploys the postdeployment layer. Verify in the portal under Policy → Exemptions.

Keep the register in the repository

The folder of exemption files is the register. Add the ticket reference and the approval in description, and review the files regularly for expiry dates (Operating responsibilities). An exemption without a justification and an end date is hard to defend in an audit.

Review checklist

Check Why
The approval reference is in the ticket and in description The audit trail
expires_on is set and not more than the agreed maximum Exemptions must not live forever
The scope is as small as possible A subscription exemption for one resource hides other findings
policy_definition_ids lists only the policies that are needed. Use [] only when the whole assignment must be exempted A whole-initiative exemption switches off many controls
The assignment is an initiative assignment Exemptions for a single-policy assignment are not supported by the module (Exemptions)
The category fits the justification Waiver for accepted risk, mitigated for a compensating control
The file name and the name are unique, and the name leaves room for the suffix of the mirror (see below) The module fails the plan on a name that is too long

Exemptions and the Defender for Cloud secure score

When Microsoft Defender for Cloud is on for a subscription, Azure assigns the Microsoft cloud security benchmark (MCSB) to the subscription as the assignment SecurityCenterBuiltIn. The portal shows it as ASC Default. The platform does not create this assignment, and deleting it does not help: Defender for Cloud creates it again.

The secure score is calculated only from the assessments of that assignment. The compliance assignments of the platform are separate assignments that the score ignores. An exemption on a platform assignment is correct for compliance reporting, but does not change the score: the default assignment keeps flagging the same resource.

Automatic mirroring

For every eligible exemption, the policy module creates a mirrored exemption on the SecurityCenterBuiltIn assignment of the subscription. You don't change the JSON file for this.

  • The mirror is named <original-name>-mcsb, and its display name gets the suffix (ASC Default).
  • Category and expiry date are copied from the original.
  • Both categories stop the resource from counting as unhealthy. Mitigated treats the resource as healthy for that recommendation, waiver takes the resource out of the assessment altogether.
  • The variable mirror_exemptions_to_mcsb controls it. It defaults to true; false switches the mirroring off for a platform (Feature flags).
Exemption scope Mirrored Reason
Subscription Yes The default assignment exists at subscription scope.
Resource group Yes The subscription is derived from the resource group ID.
Resource Yes The subscription is derived from the resource ID.
Management group No There is no default assignment per management group.

Rules that protect you:

  • An exemption that already targets the default assignment is not mirrored again.
  • An exemption with an empty policy_definition_ids list is not mirrored. It would exempt the subscription from the whole benchmark.
  • Only the policies that are also part of the benchmark are copied to the mirror. If none is, no mirror is created.
  • Exemption names are limited to 64 characters. The mirror adds -mcsb, so a name longer than 59 characters fails the plan with a clear error.

Exceptions at management group scope

A management group exemption does not reach the secure score. In order of preference:

  1. Narrow the scope to the subscriptions or resources it applies to. It then becomes eligible for mirroring.
  2. Disable the recommendation in the parameters of the default assignment: Defender for Cloud → Environment settings → the scope → Security policies → Microsoft cloud security benchmark → switch the recommendation off. It is then removed from the score for that scope.
  3. Assign the benchmark at management group level through the security policy settings of Defender for Cloud, so that the subscriptions follow one central assignment and exemptions are managed in one place.

Operational notes

  • A mirror needs the SecurityCenterBuiltIn assignment to exist. It exists as soon as Defender for Cloud is on for the subscription. On a subscription where Defender for Cloud was never enabled, the apply fails on the missing assignment.
  • Defender for Cloud evaluates every 12 to 24 hours. Allow up to 24 hours before the secure score shows a new or removed exemption.
  • Mirrors are normal exemption resources managed by Terraform. Do not edit them by hand in the portal.

Exemptions that belong to the platform

Besides the exemptions that workload teams request, a platform has exemptions of its own. They exist so that the platform can work, and they are different from the exemptions of a workload: the platform team owns them and keeps one list with the rationale for each (Document exemptions that apply to the platform).

The usual patterns, grouped by reason. The current list of your version is in docs/policy_exemptions/current-platform-exemptions.md and in the postdeployment layer of the code repository. Treat those two as the source of truth, and see Known issues for differences between them.

Reason Typical policies Scope Duration
The platform team works as guests in the customer tenant Guest accounts with write or owner permissions should be removed. A maximum of three owners on a subscription. More than one owner on a subscription. Landing zone subscriptions As long as the guest model exists. Keep access least-privilege with PIM.
The state storage account is not yet built with a hardened module Storage account network restrictions, private link, shared key access, customer-managed key, infrastructure encryption, secure transfer The state storage account Temporary. Remove when the account is deployed with the hardened module.
A control does not fit how the platform is deployed An activity log alert for specific policy operations (CIS 3.0.0) The landing zones management group Review with each version.
Owned by the tenant, not by the platform Guest invitation and guest access restrictions in Entra ID (CIS 3.0.0) Not exempted by platform code The customer tenant governance handles them.

A control that is not exempted keeps showing as non-compliant until it is fixed. For example, the Defender for APIs control stays non-compliant on subscriptions where the plan is Free or not enabled. The platform has a gate for the plan (defender_for_apis.enabled, off by default) with a default plan and per-subscription overrides, so you can enable it deliberately.

The reasoning for each of these exemptions is recorded in the platform's architecture decision records. Do not change the exemption list without reading them.

Optional: a separate exemption repository (older approach)

Older platform versions offered a different way to run exemptions: a separate repository in a separate project, so that the customer controls who can request and approve exemptions, independent of the platform team. The current standard is the JSON files in the configuration repository, deployed by the postdeployment layer. The exemption layer of the older approach still exists in the code repository, but the hub pipeline does not use it (Layers that are not part of the hub pipeline).

If you still run it, these are the building blocks:

Part Purpose
A project for the exemptions, with restricted access Customer-maintained access control. Can later hold other customer-managed files.
A group of approvers Users who may approve a pull request on the main branch.
A group of creators Users who may add exemption files. Member of the contributors group of the project.
A repository with the exemption files and their JSON schemas One folder per scope: management groups, subscriptions, resource groups, resources.
A pipeline that checks the files on a pull request A basic health check. It reads the platform code to check the referenced resources, so it needs a service connection with the same rights as the spoke deployment.
Branch policy on main No direct commits, at least one reviewer who is not the last pusher, reset approval votes on new changes, comments resolved, and the check pipeline as required build validation that expires when main changes.
A deployment pipeline in the configuration repository Runs after a merge to main of the exemption repository, for the production platform environment.
A project setting that allows pipelines to read other repositories Needed because the check pipeline reads the platform code. In Azure DevOps this is Limit job authorization scope to current project for non-release pipelines set to off.

The hub pipeline only deploys the JSON files of the configuration repository. Plan a migration of older platforms with the platform team (What to do when a new version is released).

Verify

  • Azure portal → Policy → Exemptions: the exemption and (for subscription, resource group and resource scope) the -mcsb mirror exist, with the right category and expiry date.
  • Compliance: the exempted resource is shown as Exempt after the next evaluation.
  • Defender for Cloud: allow up to 24 hours for the secure score.

Roll back

Remove the file (or rename it to .json.ignore) and run hub. The exemption and its mirror are destroyed. The resource is evaluated again against the policy.