Nobody deploys application code to every region at once without a canary. Policy changes are deployed that way constantly, by people who would never allow it anywhere else, and the reason is that the assignment page does not look like a deployment.
A policy change is a production change
Switching an effect from audit to deny alters the behaviour of every deployment path in scope, immediately, for teams who were not consulted and mostly do not know the assignment exists. Adding a definition to an assigned initiative does the same thing with even less visibility, because no assignment was touched. Neither action goes through your change process, appears in a release, or produces an artifact anyone reviews, and yet both can stop production deployments across an estate within minutes.
Microsoft documents a safe deployment framework for Azure Policy specifically, using the same progressive exposure ideas as any other controlled rollout, and in five years of looking at estates I have encountered a handful of organisations using it. The mechanics are not difficult. What is missing is the recognition that this needs a rollout at all.
The four levers
Everything in a staged policy rollout is built from four properties on the assignment, and knowing which one to reach for is most of the skill.
| Lever | What it does | When it is the right one |
|---|---|---|
| Enforcement mode | DoNotEnforce evaluates and reports while suppressing the effect entirely | Remediating effects. A deployIfNotExists or modify assignment that you want measuring the estate without changing anything yet. |
| Effect override | Runs the assignment with a different effect than the definition specifies | Blocking effects. Running a deny rule as audit across a tier before it starts refusing anything. |
| Resource selectors | Narrows which resources the assignment evaluates, without narrowing its scope | Always, during a rollout. This is what makes the tiers. |
| Definition version | Pins the version of a built-in the assignment uses | When Microsoft ships a change to a built-in you have assigned widely and you want to meet it deliberately. |
The distinction between the first two matters and it catches people. Enforcement mode is not a way to soften a deny into an audit, because it does not turn the effect into anything, it turns it off. If you set a deny assignment to DoNotEnforce expecting audit-style reporting on violations, you get an assignment that reports compliance state without ever refusing a request, which sounds the same and is not, because nothing tells you what would have been blocked. The override is the tool for that.
Tiers, and why regions are the usual axis
The documented pattern divides the estate into tiers and rolls through them from least critical to most, with Microsoft’s worked example numbering them from Tier 5 for low traffic locations down to Tier 0 for the busiest. Selectors support three kinds, resource location, resource type and resources without a location, and location is the usual axis because most estates already have an implicit criticality gradient across regions and because it is the one attribute every resource has.
Resource type is the better axis when the risk is concentrated in a service rather than a place, which is common for a rule that touches something like Key Vault or a database platform. I have also used it to separate a rule’s impact on greenfield resource types from its impact on the legacy estate, by rolling through the types the platform team owns before reaching the ones application teams own.
What matters more than the axis is that each stage has a gate. The documented flow puts a compliance check between every widening: gather the results, compare them against what you expected, and stop if they differ. Automating that check is what turns this from a discipline somebody remembers into a pipeline that cannot skip it, and the same applies to the application health signal, because a policy rollout that is technically clean and coincides with a rise in deployment failures is still a bad rollout.
The gate between stages is the entire technique. Without it you have not staged a rollout, you have delayed one.
The shape of a deny rollout
Assign at the highest scope that covers every tier, immediately, with selectors narrowing evaluation to the least critical tier and an override running the effect as audit. Validate. Widen the selector to include the next tier. Validate again, and keep going until the selector covers the estate and the whole thing is still running as audit.
Then, and this is the part that surprises people, reset the selector back to the smallest tier and change the override to deny. You walk the tiers twice: once to learn what the rule matches, once to learn what it refuses. Those are different risks and the second one cannot be discovered by the first. Finally, when the whole estate is enforcing, remove the override so the assignment runs the definition’s own effect.
For a remediating effect the same shape holds with enforcement mode in place of the override, and one addition: at each stage you also trigger a remediation task and confirm it brought resources into compliance the way you intended, because a rule that evaluates correctly can still deploy something wrong.
Versions, and meeting Microsoft’s changes on your terms
Built-in definitions are versioned, and an assignment can pin one rather than following the latest. The wildcard forms are a major version with everything below it floating, or a major and minor with the patch floating. There is no way to pin an exact patch, by design, because patch-level changes are always taken automatically. Initiatives version on the same scheme.
The elegant part is that a version can be rolled out with the same machinery as an effect. An override of kind definition version carries its own selectors, so a new major version of a built-in can run in one region while every other region continues on the version you have been running for a year. That is a canary for somebody else’s code change to your estate, and it is the strongest argument in the product for pinning at all.
Two caveats, both checked in August 2026. The feature’s status is genuinely inconsistent: stable ARM API versions accept the field, the portal labels it as preview, and at least one concept page still describes the capability in the future tense. And the documentation names the override kind two different ways on two different pages, as policy version in one place and definition version in another. That is a documentation defect rather than two features, and the practical response is to read back what you wrote rather than trusting either page.
The command surface helps here more than people realise. Listing assignments can expand both the latest available version of a definition and the version an assignment is effectively running, which turns the question of how far behind the estate has drifted into one query rather than an afternoon.
Timing, and the patience it requires
None of this is instant and the delays are long enough to break a rollout run by somebody impatient. A new or changed assignment takes about five minutes to apply. A newly assigned scope produces its first compliance results in around thirty. New and updated resources appear in evaluation results within roughly fifteen minutes. The standard compliance cycle runs once every 24 hours, and on-demand scans exist through the CLI, PowerShell, REST, the VS Code extension and a GitHub Action, which is what you use during a staged rollout rather than waiting a day per tier.
One exception is worth carrying because it invalidates the technique in a specific place. Not every resource provider participates in on-demand evaluation, and Virtual Network Manager supports neither manual triggers nor the daily compliance cycle. Where a rollout depends on reading compliance between stages, confirm the provider you are governing actually reports before you build a gate that will never turn green.
From here
The build sheet that follows walks one deny rule through the whole sequence on the Northfork estate, with the validation stated before each widening rather than after it, and with the rollback for each stage written next to the step that needs it.
Azure Policy
‹ Previous: [AP 8] Exemptions: The Ledger of Accepted Risk
Next: [AP 9.1] Build Sheet: The Staged Rollout of a Deny ›




