Creating a remediating assignment hands a permanent identity the permissions listed in somebody else’s policy definition, across every resource in scope, with no expiry and no approval step after the first one. That is the feature. It is also the largest quiet privilege grant most Azure estates contain.
Two identities, and the split people miss
When a deployIfNotExists policy evaluates, two different security principals are involved and they do different jobs. Policy evaluation, including the existence condition that decides whether the related resource is missing, runs as the identity of whoever made the original request. The template deployment that follows runs as the managed identity attached to the assignment.
Microsoft’s own worked example is the clearest way to hold it. A policy deploys diagnostic settings onto key vaults. When somebody creates a key vault, their identity is used to read the existing diagnostic settings and determine whether any are present. If none are, the assignment’s identity is used to write the new one. So the caller needs read permission on diagnostic settings and the assignment needs write permission, and if the caller lacks the read, the existence check behaves in ways that will occupy an afternoon of your life.
The caller decides whether something is missing. The assignment decides what to do about it. Most remediation problems are one of those two identities lacking a permission nobody thought to check.
The grant, and who is allowed to make it
A policy definition with a remediating effect declares the roles its identity needs, in a roleDefinitionIds array using full resource identifiers rather than role names. Built-ins arrive with the array populated. Custom definitions do not, and a custom remediating definition without it fails at deployment time rather than at authoring time.
When the assignment is created through the portal, Azure Policy grants those roles to the identity automatically. Through an SDK, the CLI or a pipeline, it does not, and you grant them yourself. That difference is worth pausing on, because it means the portal path performs a privilege escalation as a side effect of a wizard, and the automated path makes the same escalation explicit and reviewable. This is one of the few places where I prefer the code path for a governance reason rather than a convenience one.
Making the grant at all requires User Access Administrator, or Owner. Resource Policy Contributor, the role designed for policy work, cannot do it. That separation is deliberate and it is the platform telling you something: authoring a rule and granting an identity the power to enforce it are different acts, and it expects different people to perform them. Most organisations collapse both into one platform engineer and then wonder why their policy estate holds Contributor at the root.
Choosing the identity type
An assignment carries exactly one identity, system-assigned or user-assigned, and that identity can hold several roles. A system-assigned identity is created and destroyed with the assignment, which keeps the lifecycle tidy and means the role grants have to be made again every time the assignment is recreated. It also requires a top-level location on the assignment, which cannot be global and cannot be changed afterwards, so an assignment created in the wrong region has to be deleted and rebuilt.
A user-assigned identity survives the assignment, carries its role grants with it, and can be shared across several assignments. For anything deployed by pipeline I use user-assigned, because the grant becomes a reviewable artifact in the repository rather than a thing that happens each time the pipeline recreates an object. For a single assignment made by hand, system-assigned is less to clean up later.
One trap sits underneath both choices. Changing a policy definition does not update assignments of it, and does not update the identity’s role grants. Add a resource type to a definition’s remediation template and every existing assignment keeps the permissions it was given on the day it was created, so the remediation silently fails on the new resource type and the compliance page reports non-compliance with no obvious cause. Whenever a remediating definition changes, revisit the grants on its assignments as part of the same change.
What happens automatically, and what does not
New and updated resources are remediated as they arrive, without anybody asking. Resources that already existed when the assignment was made are marked non-compliant and left untouched until somebody starts a remediation task. That asymmetry is the single most useful safety property of the whole mechanism, because it means switching on a remediating policy changes the future without touching the present, and you decide separately and deliberately when to reach backwards.
The task itself carries the controls people wish deployment tooling had. It processes 500 resources by default and takes 50,000 at most. It runs between 1 and 30 deployments concurrently, defaulting to 10. It accepts a failure threshold expressed as a percentage, defaulting to 100, meaning that by default a remediation task will keep going no matter how many deployments fail. Setting that threshold to something realistic is the cheapest safety measure available and almost nobody sets it.
Two operational details worth carrying. The throttling controls exist in Azure PowerShell and not in the Azure CLI, so a team standardised on the CLI is running every remediation task at defaults whether they intended to or not. And remediation task objects are deleted by the service 60 days after their last modification, so the record of what a task did is not an audit trail. If you need to know later which resources a task rewrote and when, capture it at the time.
How this goes wrong in practice
The pattern I see repeatedly starts reasonably. A team writes a remediating policy, hits a permission error during testing, and grants the identity Contributor at a scope that makes the error go away. The policy then works, the ticket closes, and an identity attached to a policy assignment now holds Contributor across a management group indefinitely, discoverable by anyone who reads the assignment, and attached to an object that most access reviews do not enumerate because it is not a user, a group or a service principal anyone registered.
The correction is unglamorous. Grant the narrowest role that lets the template succeed, which for a diagnostic settings deployment is a monitoring role rather than Contributor. Scope the grant to where the remediation actually runs rather than to the root. And include policy assignment identities in whatever review process covers privileged access, because they hold standing write authority and outlive every person involved in creating them.
The last thing to say about modify specifically is that it is the effect people underestimate. It rewrites properties and tags on resources that already exist, at scale, through the same remediation machinery, and a mistake in a modify definition is not a blocked deployment but a silent change to production resources that reports as an improvement in your compliance figure. Test modify definitions against a resource group you are willing to lose, and read the template rather than trusting the display name.
From here
The build sheet that follows creates a deployIfNotExists assignment, grants its identity deliberately rather than expediently, runs a remediation task with its thresholds set on purpose, and proves the deployed resource landed where the policy promised. The Landing Zones build sheet on this site already does this for diagnostic settings at management group scope, so this one takes a different worked example and links there rather than repeating it.
Azure Policy
‹ Previous: [AP 4.1] Build Sheet: The Guardrail Set
Next: [AP 5.1] Build Sheet: DeployIfNotExists and the Remediation Task ›




