Azure Governance

Remediate Non-Compliant Resources: Remediation Tasks and Managed Identity Setup

You assigned a policy that says “every storage account must have a diagnostic setting sending logs to Log Analytics.” You picked the DeployIfNotExists effect so Azure would create the setting for you. You waited. The compliance dashboard still shows 312 non-compliant storage accounts, and not one of them sprouted a diagnostic setting. Nothing is broken — this is Azure Policy working exactly as designed, and it is the single most common surprise people hit with governance at scale. DeployIfNotExists and Modify policies evaluate during a deployment. They fire when a resource is created or updated through Resource Manager. The 312 accounts that already existed before you assigned the policy were never re-deployed, so the policy never got a chance to act on them. They sit there, flagged non-compliant, untouched, forever — until you do one more thing.

That one more thing is a remediation task: an on-demand sweep you trigger that finds every resource a DeployIfNotExists or Modify assignment marked non-compliant and runs the policy’s deployment (or operations) against each one, now, without anyone having to redeploy anything. It is the bridge between “I wrote a policy that auto-fixes drift” and “the drift that already existed actually got fixed.” And it has one hard prerequisite that trips up nearly everyone the first time: the assignment needs a managed identity with the right RBAC role at the right scope, because something has to authenticate to Azure and actually write the change. A policy definition with no identity, or an identity missing the role, produces a remediation task that runs, reports success on the orchestration, and silently changes nothing — or fails every resource with a 403.

This article is the end-to-end implementation. You will learn the mechanics — what a remediation task is, how the identity and role are derived from the policy’s roleDefinitionIds, and the --resource-discovery-mode that decides whether Azure trusts the compliance cache or rescans. Then the centerpiece: a full hands-on lab that assigns a real DeployIfNotExists policy, provisions the managed identity and role three ways (portal, az CLI, Bicep), deliberately leaves a resource non-compliant, runs the remediation task, and confirms the fix landed — with the exact command and expected output at every step. By the end, a dashboard full of red “non-compliant” rows is something you clear in minutes, not something you stare at wondering why your policy did nothing.

What problem this solves

Azure Policy has two distinct jobs that people conflate. The first is prevention and detection at deployment time: when someone deploys a resource, the policy engine evaluates the request and can block it (Deny), flag it (Audit), rewrite it in flight (Modify), or schedule a follow-on deployment (DeployIfNotExists). The second is fixing what is already there — the resources that were deployed before the policy existed, or that drifted out of compliance, or that were created by a path the deployment-time hook didn’t cover. Those two jobs use the same policy definition but completely different execution moments, and remediation is the only thing that does the second job.

What breaks without it is subtle because nothing throws an error. You assign a well-written DeployIfNotExists policy across a subscription with 400 existing resources. New resources start coming out compliant — the hook fires on every fresh deployment — but the 400 already there stay non-compliant, and your compliance score is stuck at 12%. An auditor asks why, and the honest answer is “the policy only fixes things going forward, and we never ran remediation.” Meanwhile the security control the policy was supposed to enforce — diagnostic logging, a required tag, TLS 1.2 minimum, a backup configuration — is simply absent on the majority of your estate, even though the dashboard makes it look covered.

The people who hit this hardest are teams adopting governance on a brownfield subscription — an environment full of resources that predate any policy. (If that’s you, pair this with Import Existing Azure Resources: Brownfield Terraform Adoption, the IaC-state version of the same “it already exists” problem.) It also bites anyone who assumes “DeployIfNotExists = auto-remediation” without realising the auto part only applies to the deployment-time path. And it bites on the identity wiring most of all: people assign a Modify or DINE policy, skip or fumble the managed identity, and get remediation tasks that fail every resource with AuthorizationFailed — because the identity that would have done the writing either doesn’t exist or was never granted the role.

Here is the whole field on one page — the four moments a policy can act, and which one each effect actually uses:

Moment What triggers it Effects that act here Touches existing resources?
Deployment-time evaluation A PUT/PATCH through Resource Manager Deny, Audit, Modify, DeployIfNotExists, Append No — only the resource being deployed
Periodic compliance scan Azure’s ~24h background evaluation All effects (updates the complianceState) Read-only — it flags, never fixes
On-demand compliance scan You run az policy state trigger-scan All effects (refreshes flags now) Read-only — flags only
Remediation task You create a remediation task Modify, DeployIfNotExists only Yes — this is the one that fixes the back catalogue

Learning objectives

By the end of this article you can:

Prerequisites & where this fits

You should already understand what an Azure Policy definition, assignment and effect are. If “DeployIfNotExists” and “Modify” are not yet concrete to you, read Azure Policy Effects Decoded: Deny vs Audit vs Modify vs DeployIfNotExists first — this article is the hands-on sequel to it, the part where the auto-remediation those effects promise actually gets applied to resources that already exist. You should also know what an initiative is versus a single definition; Azure Policy Initiatives vs Definitions: When to Group covers that, and it matters here because an initiative with several DeployIfNotExists policies needs an identity that holds every role across all of them.

On the identity side, you need a working mental model of managed identities. Managed Identity: System-Assigned vs User-Assigned Patterns is the reference; the short version is that the assignment will carry an identity that Azure authenticates as when it runs the remediation deployment, and that identity needs roles granted via Azure RBAC. A basic grasp of Azure RBAC Fundamentals: Roles, Scope, Assignments is essential, because the single most common remediation failure is a role assignment missing or scoped too narrowly. Finally, because policy and remediation usually live at management group or subscription scope, Azure Management Groups: Hierarchy Design Fundamentals gives you the scope picture.

This sits in the Governance track, downstream of “how do I write a policy” and upstream of “how do I report compliance to an auditor.” Where it fits operationally:

You are here if… The upstream skill The downstream skill
You wrote a DINE/Modify policy and existing resources stay red Policy effects & definitions Reading the compliance dashboard for reporting
You’re rolling governance onto a brownfield subscription Management-group scope design Continuous compliance / drift alerting
Your remediation task ran but nothing changed RBAC + managed identity basics Auditing role assignments for least privilege
You want remediation to be IaC, not clicks Bicep/Terraform fundamentals A policy-as-code pipeline

Core concepts

Six ideas make every later step obvious.

A policy effect decides when a policy can write, not just what it writes. Deny and Audit never write. Append and Modify alter the resource payload as it is being deployed. DeployIfNotExists (DINE) runs a separate ARM deployment (the deployment block in then) after the triggering resource is created, to deploy a related resource — a diagnostic setting, a backup config, a defender plan. The crucial detail: Modify and DINE only get their chance during a Resource Manager write. No write, no effect — which is why pre-existing resources, never re-written since the assignment, are skipped.

A remediation task is a manual replay of the effect against resources that already exist. When you create one for a DINE or Modify assignment, Azure enumerates the resources the assignment flagged non-compliant and, for each, executes the policy’s remediation action now — the same deployment (DINE) or operations (Modify) it would have run at deployment time. It is not a new policy; it is the existing assignment’s logic fired on demand against the back catalogue. Only Modify and DeployIfNotExists are remediable; Audit and Deny have no write action to replay.

The managed identity is who Azure becomes to do the writing. A remediation deployment has to authenticate and call PUT, and it does that as the managed identity attached to the assignment. Azure Policy requires a DINE/Modify assignment to have an identity (system- or user-assigned), and that identity must hold the RBAC roles the policy declares. Without the identity you can’t create the assignment cleanly; with the identity but no role, every remediation deployment fails with 403 AuthorizationFailed.

The roles come from the policy, not from you guessing. Every DINE/Modify definition declares, in then.details.roleDefinitionIds, the exact role IDs the remediation needs — a diagnostic-settings policy declares Monitoring Contributor and Log Analytics Contributor; a backup policy declares Backup Contributor and Virtual Machine Contributor. Grant those to the identity at a scope covering the resources being remediated. Grant less and remediation fails; grant Owner and you’ve over-privileged a robot.

Scope is two things that must line up. The assignment scope (MG, subscription, or RG) defines which resources are eligible for remediation. The role-assignment scope defines where remediation deployments are allowed to act. If the role is scoped to one resource group but the policy is assigned at subscription scope, remediation succeeds only in that group and 403s everywhere else. The portal toggle handles the common case; at scale you grant the role at the assignment scope yourself.

Compliance state is a cache, and remediation reads it. The “non-compliant” flags come from periodic (~24h) and on-demand scans stored as compliance state. A remediation task by default targets the resources currently flagged non-compliant (existingNonCompliant). Remediate immediately after assigning — before a scan has run — and the cache may be empty, so the task finds nothing. The fix is an on-demand scan first, or --resource-discovery-mode ReEvaluateCompliance so the task re-checks each resource as it goes.

The vocabulary in one table

Term One-line definition Where it lives Why it matters here
Policy definition The rule + effect + (for DINE/Modify) the remediation action Subscription / management group Declares roleDefinitionIds you must grant
Assignment A definition applied at a scope, with parameters + identity A scope (MG/sub/RG) Carries the identity; is what you remediate
Effect What the policy does (Deny/Audit/Modify/DINE/…) In the definition Only Modify & DINE are remediable
Remediation task On-demand replay of the effect on existing non-compliant resources A child of the assignment The thing that fixes the back catalogue
Managed identity The identity Azure authenticates as to write the fix Attached to the assignment No identity → no remediation
roleDefinitionIds Roles the remediation needs, declared by the policy then.details of the definition The exact roles to grant the identity
Compliance state Cached per-resource compliant/non-compliant flag Policy Insights store Remediation targets non-compliant by default
Resource discovery mode Whether the task trusts the cache or re-evaluates Remediation task setting Fixes “task found nothing” surprises
existingNonCompliant Default discovery mode: act on currently flagged resources Remediation task The normal mode
ReEvaluateCompliance Discovery mode: re-check each resource live, then act Remediation task Use right after assigning

Why existing resources stay non-compliant

This is the conceptual heart, so it gets its own section. A DINE or Modify policy is wired to the Resource Manager request pipeline. When a PUT/PATCH arrives for a resource in the assignment’s scope, the engine evaluates the if; if it matches and the effect is DINE, it schedules the deployment defined in then. That scheduling is attached to the deployment event — and there is no deployment event for a resource that already exists and isn’t being touched, so there is nothing to attach to. The periodic scan will flag that resource non-compliant (it evaluates the if against current state), but a scan is read-only: it updates the dashboard, it never calls PUT.

So a freshly-assigned DINE policy produces exactly the pattern people find baffling: every new resource is compliant (the hook fired on its deployment), while every old resource stays non-compliant (no deployment, no hook). The table below maps each path a resource can take and whether the DINE/Modify effect actually runs:

How the resource reaches the policy Does the DINE/Modify effect run automatically? Why
Created after assignment, via ARM/Bicep/portal/CLI Yes Deployment-time hook fires on the PUT
Updated after assignment (any PUT/PATCH) Yes The update is a deployment event
Existed before the assignment, never touched since No No deployment event to hook
Flagged non-compliant by the periodic scan No Scans are read-only; they flag, never write
Targeted by a remediation task Yes The task replays the effect on demand
Changed outside ARM (rare; e.g. some data-plane paths) No The control plane never saw a write

The practical consequence: after you assign any DINE or Modify policy to an existing environment, you must run at least one remediation task to bring the back catalogue into line. From then on the deployment-time hook keeps new resources compliant, and you only re-run remediation when something drifts or a new batch of pre-existing resources comes into scope (for example when you move a subscription under a management group that already has the assignment). A common misconception is that DINE “continuously enforces” — it does not loop and re-fix drift on its own; if a diagnostic setting is deleted later, the next scan flags that resource non-compliant again, and you (or an automated pipeline) must remediate again. Enforcement of the back catalogue is always an explicit action.

Anatomy of a remediation task

A remediation task is a first-class Azure resource (Microsoft.PolicyInsights/remediations) and a child of a policy assignment. You specify, at minimum, which assignment it remediates; optionally you narrow it to one policy definition reference inside an initiative, restrict its scope, and tune how it discovers and paces work. It enumerates the target resources and, for each, kicks off the policy’s remediation deployment, tracking a per-resource result you can query. The fields you set, what they do, and their defaults:

Field (az flag / property) What it controls Default When to change
--policy-assignment (policyAssignmentId) Which assignment to remediate required Always
--definition-reference-id (policyDefinitionReferenceId) Which policy inside an initiative all remediable in the initiative Initiatives with multiple DINE/Modify policies
--resource-group / --scope Narrow remediation to a sub-scope the assignment’s scope Pilot on one RG before a full sweep
--resource-discovery-mode Trust the cache vs re-evaluate live ExistingNonCompliant Right after assigning, or after drift
--failure-threshold (failureThreshold.percentage) Abort the task if too many deployments fail 100% (never aborts) Stop a misconfigured sweep early
--parallel-deployments How many resource deployments run at once service-managed (~10) Rarely; throttle to ease load
--resource-count Cap total resources remediated in this task service limit (~500) Batch very large sweeps
--location-filters Only remediate resources in these regions all regions Region-phased rollouts

Two deserve emphasis. --resource-discovery-mode defaults to ExistingNonCompliant — “act on whatever the compliance store currently flags non-compliant” — which is right normally but wrong immediately after assignment, before a scan has populated the store; in that window the task finds zero resources. ReEvaluateCompliance re-checks each in-scope resource live: slower and more evaluations, but correct when the cache is cold or stale. --failure-threshold is the safety valve: set it to 10 and the task aborts if more than 10% of deployments fail, so a wrong role assignment doesn’t burn through 500 failed deployments before you notice.

The per-resource result is where you confirm what really happened. Each remediated resource gets a deployment whose status you list — and “the task completed” is not the same as “every resource succeeded”:

Remediation task / deployment status What it means Your next move
Task Evaluating Discovering which resources are in scope Wait
Task Accepted / InProgress Deployments are running Watch deployment results
Task Succeeded The orchestration finished Check per-resource deployments — some may have failed
Task Failed The orchestration itself failed Read the error; usually identity/scope
Task Canceled You (or the failure threshold) stopped it Inspect failures before re-running
Deployment Succeeded (per resource) That resource was fixed Validate the actual setting exists
Deployment Failed (per resource) That resource was not fixed Read the deployment error (often 403)
Deployment Conflict Concurrent write / locked resource Re-run; check resource locks

Wiring the managed identity and roles

The identity is where remediation lives or dies, so be deliberate. A DINE or Modify assignment must carry a managed identity holding the roles the policy declares, and the choice of type has real operational weight.

A system-assigned identity is tied to the assignment’s lifecycle — born when the assignment is created, deleted with it — and is the simplest path and portal default. A user-assigned identity is a standalone resource you create once and attach to many assignments; it survives independently, so you grant it roles once and reuse it across assignments, subscriptions and re-creations. For a single policy in one subscription, system-assigned is fine. For an initiative with several remediable policies, or a fleet of subscriptions sharing a baseline, user-assigned is far less painful — grant the union of all required roles once and point every assignment at it.

Aspect System-assigned User-assigned
Lifecycle Born and dies with the assignment Independent; you manage it
Role grants Re-granted each time the assignment is recreated Granted once, reused everywhere
Best for A single policy in one scope Initiatives, multi-sub baselines, IaC
Re-create assignment New identity → re-grant all roles Same identity → roles persist
Portal default Yes (it offers to create roles too) Must pre-create and select it
Cleanup risk Auto-cleaned with the assignment Orphan identities if not tracked
Cross-assignment reuse No Yes — the main reason to choose it

Whichever you pick, the roles are dictated by the policy. You read them from the definition and grant exactly those. Common built-in policies and the roles they declare (always confirm against the live definition, since Microsoft can revise them):

Policy intent (built-in family) Effect Roles the policy declares (roleDefinitionIds)
Deploy diagnostic settings to Log Analytics DeployIfNotExists Monitoring Contributor; Log Analytics Contributor
Deploy diagnostic settings to Event Hub / Storage DeployIfNotExists Monitoring Contributor
Configure backup on VMs DeployIfNotExists Backup Contributor; Virtual Machine Contributor
Deploy Microsoft Defender for Cloud plans DeployIfNotExists Security Admin / Contributor (varies by plan)
Add or replace a tag on resources Modify Contributor (Tag Contributor for tag-only)
Enforce HTTPS / minimum TLS on storage Modify Storage Account Contributor
Associate a subnet with an NSG / route table DeployIfNotExists Network Contributor
Deploy the Azure Monitor Agent DeployIfNotExists Virtual Machine Contributor; Monitoring Contributor

The reliable way to discover the roles for your policy, not a remembered list, is to read them straight from the definition. Pull the assignment, find its policy definition, and extract the role IDs the remediation needs:

# 1) Get the policy definition id behind an assignment
DEF_ID=$(az policy assignment show \
  --name deploy-diag-storage \
  --scope "/subscriptions/$(az account show --query id -o tsv)" \
  --query policyDefinitionId -o tsv)

# 2) Read the roleDefinitionIds the policy's remediation needs
az policy definition show --name "$(basename "$DEF_ID")" \
  --query "policyRule.then.details.roleDefinitionIds" -o tsv
# → /providers/Microsoft.Authorization/roleDefinitions/749f88d5-...  (Monitoring Contributor)
#   /providers/Microsoft.Authorization/roleDefinitions/92aaf0da-...  (Log Analytics Contributor)

Those GUIDs are the role definition IDs you grant to the identity. Map a few of the most common so you recognise them on sight:

Role Role definition ID (GUID) Typical remediation use
Monitoring Contributor 749f88d5-cbae-40b8-bcfc-e573ddc772fa Create diagnostic settings
Log Analytics Contributor 92aaf0da-9dab-42b6-94a3-d43ce8d16293 Wire diagnostics to a workspace
Backup Contributor 5e467623-bb1f-42f4-a55d-6e525e11384b Configure VM backup
Virtual Machine Contributor 9980e02c-c2be-4d73-94e8-173b1dc7cf3c Install agents / configure VMs
Network Contributor 4d97b98b-1d4f-4787-a291-c67834d212e7 Associate NSGs / route tables
Storage Account Contributor 17d1049b-9a84-46fb-8f53-869881c3d3ab Enforce TLS / HTTPS on storage
Tag Contributor 4a9ae827-6dc8-4573-8ac7-8239d42aa03f Add/replace tags (Modify, tag-only)

Architecture at a glance

Trace the path a single existing storage account takes from “flagged red” to “fixed and green.” It begins on the control plane: you (or your IaC) created a policy assignment at subscription scope with the DeployIfNotExists effect, carrying a managed identity that was granted Monitoring Contributor and Log Analytics Contributor at the subscription scope — the two roles the diagnostic-settings policy declared in its roleDefinitionIds. So far nothing has touched the 312 existing accounts; the periodic compliance scan has merely flagged them non-compliant in the Policy Insights store.

Now you create a remediation task. It reads the compliance store, enumerates the non-compliant accounts, and for each launches the policy’s remediation deployment — an ARM deployment that runs as the managed identity. Because the identity holds the right roles at the right scope, each deployment calls PUT on a new diagnostic setting pointing at the Log Analytics workspace; the resource’s compliance state flips to compliant on the next evaluation and the dashboard count drops. The three places this breaks — no identity, missing role, cold cache — are marked on the diagram as numbered badges so you can see which hop fails and why.

Left-to-right architecture of Azure Policy remediation: a control-plane policy assignment with a managed identity granted Monitoring Contributor and Log Analytics Contributor; a remediation task that reads the Policy Insights compliance store and launches per-resource ARM deployments as the identity; those deployments writing diagnostic settings onto existing storage accounts and VMs pointed at a Log Analytics workspace; numbered failure badges on the identity (no identity), the role grant (AuthorizationFailed 403), and the compliance cache (task found nothing).

Real-world scenario

Northwind Logistics runs a single Azure subscription with about 280 resources accumulated over three years — storage accounts, VMs, SQL databases, key vaults — almost none created with any governance in place. A new security lead, preparing for an ISO 27001 audit, needs diagnostic logging on every storage account and SQL database, shipped to a central Log Analytics workspace, and backup configured on every production VM. She assigns two built-in DeployIfNotExists initiatives at the subscription scope, parameterised to the central workspace, expecting compliance to climb over the next day.

It doesn’t. Twenty-four hours later the new initiatives show 9% and 14% compliant — the only compliant resources are a handful created that week, everything older is red. Her first instinct is that the policies are misconfigured, and she spends an afternoon re-reading definitions before realising they’re fine: DeployIfNotExists never ran on the 250 pre-existing resources because none had been redeployed since the assignment. The dashboard is honest; the back catalogue was simply never swept.

She moves to remediation and hits the second wall. She creates a remediation task for the diagnostics initiative in the portal; it runs and reports Succeeded — but the per-resource deployments are nearly all Failed with AuthorizationFailed. The assignment had a system-assigned identity (the portal created it), but the role assignments the portal offered to create had been skipped in the wizard, so the identity held no roles and 403’d on every PUT. She grants Monitoring Contributor and Log Analytics Contributor to the identity at subscription scope (reading the exact roles from the policy’s roleDefinitionIds), re-runs, and this time every deployment succeeds.

The third subtlety appears on the VM backup initiative. The task finds the VMs, but several deployments fail because the identity has Backup Contributor while the policy also declares Virtual Machine Contributor, which the team forgot to grant — backup configuration touches the VM itself, not just the vault. Granting the second role clears it. She also finds that running remediation immediately after assignment returned “0 resources” until she switched the discovery mode to ReEvaluateCompliance for the first pass. After a week she scripts the whole thing — assignment, user-assigned identity, both role grants at subscription scope, an initial remediation per initiative — as Bicep, so the company’s next two subscriptions onboard with the back catalogue fixed from day one. Diagnostics compliance settles at 100% of in-scope resources, and the audit evidence is a clean remediation history rather than a wall of red.

The lesson Northwind takes away: DeployIfNotExists buys you compliance going forward; a remediation task with a correctly-roled identity buys you compliance for everything that already exists — and the two are separate, explicit acts.

Advantages and disadvantages

Advantages Disadvantages
Fixes the entire back catalogue without redeploying anything Requires a managed identity + correct RBAC — the main failure source
Reuses the policy’s existing remediation logic (no new code) Only Modify and DeployIfNotExists are remediable; not Audit/Deny
Runs on demand and at scale (hundreds of resources per task) Not continuous — drift requires re-running remediation
Per-resource deployment results give precise success/failure visibility “Task succeeded” can hide many per-resource 403 failures
Fully scriptable as IaC (assignment + identity + role + task) Cold/stale compliance cache can make a task find “0 resources”
Scope-narrowing lets you pilot on one RG before a full sweep Role granted too broadly (Owner) over-privileges a robot identity
Same identity reused across an initiative (user-assigned) Initiatives need the union of all policies’ roles — easy to miss one

When the advantages dominate: brownfield adoption, audit prep, and any “we have a good policy but the dashboard is stuck” situation — remediation is the only tool that closes the gap, and scripting it makes onboarding new subscriptions trivial. When the disadvantages bite: if you treat remediation as fire-and-forget. Drift after remediation is silent until the next scan, “Succeeded” tasks can mask widespread 403s, and a sloppy role grant either fails everything (too little) or hands a non-human identity sweeping write access (too much). The mitigation is discipline: read the roles from the policy, grant exactly those at the assignment scope, set a failure threshold, and check per-resource deployments — never trust the task-level status alone.

Hands-on lab

This is the centerpiece. You will assign a real built-in DeployIfNotExists policy that deploys a diagnostic setting to a Log Analytics workspace, deliberately create a storage account that the policy leaves non-compliant, then provision the managed identity and roles, run a remediation task, and confirm the diagnostic setting actually lands. Everything uses cheap/free resources (a Log Analytics workspace and a Standard LRS storage account; delete both at the end). You’ll do it three ways: portal, az CLI (the primary path, fully scripted), and Bicep.

What you need: an Azure subscription where you have Owner or User Access Administrator (you must be able to create role assignments), az CLI ≥ 2.50 (Cloud Shell is ideal — it has everything), and about 30 minutes. All resource names below assume you substitute your own where they must be globally unique (the storage account).

The shape of the lab:

Step What you do How you confirm it
0 Set variables, pick the region and subscription az account show
1 Create a resource group + Log Analytics workspace az monitor log-analytics workspace show
2 Create a storage account before the assignment (the non-compliant one) az storage account show
3 Assign the built-in DINE diagnostics policy with an identity az policy assignment show shows an identity
4 Read the roles the policy needs; grant them to the identity az role assignment list shows both roles
5 Trigger a compliance scan; confirm the account is non-compliant az policy state list shows NonCompliant
6 Create and run a remediation task az policy remediation showSucceeded
7 Confirm the diagnostic setting now exists on the account az monitor diagnostic-settings list returns it
8 Teardown resources deleted

Step 0 — Set variables

# Pick your subscription and region
az account set --subscription "<your-subscription-id>"
SUB_ID=$(az account show --query id -o tsv)
LOCATION="eastus"
RG="rg-policy-remediation-lab"
LAW="law-remediation-lab"
# Storage account name must be globally unique, 3-24 lowercase alphanumerics
SA="stremlab$RANDOM"
ASSIGN="deploy-diag-storage-lab"

echo "Subscription: $SUB_ID"
echo "Storage account: $SA"

Expected output: your subscription GUID and a generated storage account name like stremlab28344. Note the $SA value — you’ll need it throughout.

Step 1 — Create the resource group and Log Analytics workspace

az group create --name "$RG" --location "$LOCATION"

az monitor log-analytics workspace create \
  --resource-group "$RG" --workspace-name "$LAW" \
  --location "$LOCATION"

LAW_ID=$(az monitor log-analytics workspace show \
  --resource-group "$RG" --workspace-name "$LAW" --query id -o tsv)
echo "Workspace resource id: $LAW_ID"

Expected output: the workspace JSON, ending with a "provisioningState": "Succeeded", and a LAW_ID like /subscriptions/.../resourceGroups/rg-policy-remediation-lab/providers/Microsoft.OperationalInsights/workspaces/law-remediation-lab. This workspace is where the diagnostic logs will land.

Step 2 — Create the non-compliant storage account first

Order matters. Create the storage account before assigning the policy, so it is a pre-existing resource the DINE effect will never touch on its own — exactly the situation this article is about.

az storage account create \
  --name "$SA" --resource-group "$RG" --location "$LOCATION" \
  --sku Standard_LRS --kind StorageV2 \
  --min-tls-version TLS1_2 --allow-blob-public-access false

SA_ID=$(az storage account show --name "$SA" --resource-group "$RG" --query id -o tsv)
echo "Storage account id: $SA_ID"

Expected output: storage account JSON with "provisioningState": "Succeeded". Right now this account has no diagnostic settings — verify that baseline so the “after” is meaningful:

az monitor diagnostic-settings list --resource "$SA_ID" -o table
# Expected: an empty result (no diagnostic settings yet)

Step 3 — Assign the built-in DeployIfNotExists diagnostics policy

We’ll use the built-in policy “Configure diagnostic settings for Storage Accounts to Log Analytics workspace” (a DeployIfNotExists policy). First find its definition ID, then assign it at subscription scope with a managed identity. The --mi-system-assigned flag (with --location, which system-assigned identities require) creates the system-assigned identity on the assignment.

# Find the built-in DINE policy that configures storage diagnostic settings to LAW
DEF_ID=$(az policy definition list --query \
  "[?policyType=='BuiltIn' && contains(displayName,'diagnostic settings for Storage Accounts to Log Analytics')].id | [0]" -o tsv)
echo "Definition: $DEF_ID"

# Assign it at subscription scope, with a system-assigned identity, passing the workspace
az policy assignment create \
  --name "$ASSIGN" \
  --display-name "Deploy diag settings to LAW (lab)" \
  --policy "$DEF_ID" \
  --scope "/subscriptions/$SUB_ID" \
  --mi-system-assigned --location "$LOCATION" \
  --params "{\"logAnalytics\":{\"value\":\"$LAW_ID\"}}"

Expected output: assignment JSON. Critically, confirm it carries an identity — this is what remediation will run as:

az policy assignment show --name "$ASSIGN" --scope "/subscriptions/$SUB_ID" \
  --query "{name:name, identityType:identity.type, principalId:identity.principalId}" -o json
# Expected:
# { "name": "deploy-diag-storage-lab",
#   "identityType": "SystemAssigned",
#   "principalId": "00000000-aaaa-bbbb-cccc-1111..." }

Capture that principalId — it is the object ID of the identity you’ll grant roles to:

PRINCIPAL_ID=$(az policy assignment show --name "$ASSIGN" \
  --scope "/subscriptions/$SUB_ID" --query identity.principalId -o tsv)
echo "Identity principalId: $PRINCIPAL_ID"

Common parameter pitfalls at this step, so the assignment doesn’t fail:

Symptom at assignment time Cause Fix
--location is required System-assigned identity needs a location Add --location "$LOCATION"
InvalidPolicyParameters Missing required logAnalytics param Pass --params with the workspace id
Assignment has no identity Forgot --mi-system-assigned Recreate with the identity flag
Wrong definition picked Display-name filter matched another policy Print $DEF_ID and verify the name

Step 4 — Read the required roles, then grant them to the identity

Don’t guess the roles — read them from the policy definition, then grant exactly those at subscription scope so the identity can write diagnostic settings across the subscription.

# Roles the policy's remediation declares it needs
az policy definition show --name "$(basename "$DEF_ID")" \
  --query "policyRule.then.details.roleDefinitionIds" -o tsv
# Expected (two lines):
#   /providers/Microsoft.Authorization/roleDefinitions/749f88d5-cbae-40b8-bcfc-e573ddc772fa  (Monitoring Contributor)
#   /providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293  (Log Analytics Contributor)

# Grant both to the assignment's identity at subscription scope
for ROLE in "Monitoring Contributor" "Log Analytics Contributor"; do
  az role assignment create \
    --assignee-object-id "$PRINCIPAL_ID" \
    --assignee-principal-type ServicePrincipal \
    --role "$ROLE" \
    --scope "/subscriptions/$SUB_ID"
done

Expected output: two role-assignment JSON objects. The --assignee-principal-type ServicePrincipal flag matters: a brand-new managed identity may not have replicated to Microsoft Entra ID yet, and specifying the type avoids the “principal not found” error that retries otherwise hit. Confirm both roles landed on the identity:

az role assignment list --assignee "$PRINCIPAL_ID" \
  --scope "/subscriptions/$SUB_ID" \
  --query "[].{role:roleDefinitionName, scope:scope}" -o table
# Expected: two rows — Monitoring Contributor and Log Analytics Contributor,
# both scoped to /subscriptions/<SUB_ID>

If you see only one role, or none, remediation will partially or fully 403 — fix it here before proceeding. This is the step that silently breaks more remediations than any other.

Step 5 — Trigger a compliance scan and confirm non-compliance

The compliance store may not yet reflect the storage account. Trigger an on-demand scan, wait for it, then confirm the account shows as non-compliant for this assignment — that’s what makes it eligible for remediation.

# Kick an on-demand compliance scan for the subscription (blocks until done)
az policy state trigger-scan --resource-group "$RG"

# Confirm the storage account is NonCompliant for our assignment
az policy state list \
  --resource-group "$RG" \
  --filter "PolicyAssignmentName eq '$ASSIGN'" \
  --query "[?contains(resourceId,'$SA')].{resource:resourceId, state:complianceState}" -o table
# Expected: one row with complianceState = NonCompliant

Expected output: a single row whose complianceState is NonCompliant, for your storage account. If it’s empty, the scan may still be propagating — wait a minute and re-run the policy state list. (This propagation delay is exactly why ReEvaluateCompliance exists for the remediation task; we’ll use the explicit scan here for clarity.)

Step 6 — Create and run the remediation task

Now the actual fix. Create a remediation task for the assignment. Because we already scanned, the default ExistingNonCompliant discovery mode will find the account; we’ll set a failure threshold as a safety valve.

az policy remediation create \
  --name "remediate-diag-storage-lab" \
  --policy-assignment "$ASSIGN" \
  --resource-group "$RG" \
  --resource-discovery-mode ExistingNonCompliant

# Poll the task until it finishes
az policy remediation show \
  --name "remediate-diag-storage-lab" --resource-group "$RG" \
  --query "{state:provisioningState, deployed:deploymentStatus.totalDeployments, success:deploymentStatus.successfulDeployments, failed:deploymentStatus.failedDeployments}" -o json

Expected output (after it completes, ~1-3 minutes):

{
  "state": "Succeeded",
  "deployed": 1,
  "success": 1,
  "failed": 0
}

The key numbers are success: 1, failed: 0. If you instead see failed: 1, the task “succeeded” at the orchestration level but the deployment failed — almost always a missing role from Step 4. Inspect the per-resource deployment to see the real error:

# List the per-resource deployments this task launched, with their status
az policy remediation deployment list \
  --name "remediate-diag-storage-lab" --resource-group "$RG" \
  --query "[].{resource:resourceId, status:status, error:error.code}" -o table

A clean run shows status = Succeeded and an empty error. A 403 here points straight back to RBAC.

If policy remediation is not present in your az build, install the policy-insights extension: az extension add --name policyinsights. On current Azure CLI the commands are built in.

Step 7 — Confirm the diagnostic setting actually exists

The whole point. The pre-existing storage account — which had no diagnostic settings in Step 2 — should now have one, created by the remediation deployment, pointing at the workspace:

az monitor diagnostic-settings list --resource "$SA_ID" \
  --query "[].{name:name, workspace:workspaceId}" -o table
# Expected: one diagnostic setting whose workspaceId is your LAW_ID

Expected output: a row with a diagnostic setting (the policy names it setByPolicy-LogAnalytics or similar) whose workspaceId matches $LAW_ID. That setting was written by the managed identity during remediation — proof the back-catalogue resource was fixed without anyone redeploying the storage account. A follow-up compliance scan will now flip it to Compliant:

az policy state trigger-scan --resource-group "$RG"
az policy state list --resource-group "$RG" \
  --filter "PolicyAssignmentName eq '$ASSIGN'" \
  --query "[?contains(resourceId,'$SA')].complianceState" -o tsv
# Expected: Compliant

The Bicep version

To make this repeatable, here is the assignment and a user-assigned identity wired as IaC. (Bicep can declare the assignment, the identity, and the role assignment; the remediation task itself is best fired by az policy remediation create afterwards or via a deployment script, because it is an action, not desired state.) This uses a user-assigned identity so the roles persist across re-deployments.

targetScope = 'subscription'

@description('Region for the user-assigned identity')
param location string = 'eastus'

@description('Resource id of the Log Analytics workspace diagnostics target')
param workspaceId string

@description('Resource group that holds the identity')
param identityRgName string = 'rg-policy-identity'

// Built-in: Configure diagnostic settings for Storage Accounts to Log Analytics workspace
var policyDefinitionId = subscriptionResourceId(
  'Microsoft.Authorization/policyDefinitions',
  '<built-in-definition-guid>') // resolve via: az policy definition list

// Built-in role definition IDs the policy declares it needs
var monitoringContributor = subscriptionResourceId(
  'Microsoft.Authorization/roleDefinitions','749f88d5-cbae-40b8-bcfc-e573ddc772fa')
var logAnalyticsContributor = subscriptionResourceId(
  'Microsoft.Authorization/roleDefinitions','92aaf0da-9dab-42b6-94a3-d43ce8d16293')

resource identityRg 'Microsoft.Resources/resourceGroups@2024-03-01' = {
  name: identityRgName
  location: location
}

module uami 'modules/uami.bicep' = {
  scope: identityRg
  name: 'uami-policy-remediation'
  params: {
    name: 'id-policy-remediation'
    location: location
  }
}

resource assignment 'Microsoft.Authorization/policyAssignments@2024-04-01' = {
  name: 'deploy-diag-storage'
  location: location
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${uami.outputs.resourceId}': {}
    }
  }
  properties: {
    displayName: 'Deploy diag settings to LAW'
    policyDefinitionId: policyDefinitionId
    parameters: {
      logAnalytics: { value: workspaceId }
    }
  }
}

// Grant the two declared roles to the user-assigned identity at subscription scope
resource roleMon 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, uami.outputs.principalId, monitoringContributor)
  properties: {
    principalId: uami.outputs.principalId
    principalType: 'ServicePrincipal'
    roleDefinitionId: monitoringContributor
  }
}

resource roleLaw 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, uami.outputs.principalId, logAnalyticsContributor)
  properties: {
    principalId: uami.outputs.principalId
    principalType: 'ServicePrincipal'
    roleDefinitionId: logAnalyticsContributor
  }
}

With that deployed, trigger the initial sweep once:

az policy remediation create --name "remediate-diag-storage" \
  --policy-assignment "deploy-diag-storage" \
  --scope "/subscriptions/$SUB_ID" \
  --resource-discovery-mode ReEvaluateCompliance

The portal version (same flow, clicked)

If you prefer the portal for the assignment and role wiring:

# Portal action Where What to confirm
1 Policy → Assignments → Assign policy Azure Policy blade Scope = your subscription
2 Pick the “Configure diagnostic settings for Storage Accounts to Log Analytics workspace” definition Assignment wizard It’s the DeployIfNotExists one
3 On Parameters, set the Log Analytics workspace Parameters tab Your law-remediation-lab
4 On Remediation, tick “Create a managed identity”, choose System/User-assigned, set the location Remediation tab The role(s) the policy needs are listed here
5 Leave the role-assignment checkbox enabled (the wizard offers to grant the declared roles) Remediation tab This is the step people skip → 403 later
6 Review + create Wizard Assignment shows an identity afterwards
7 After a scan, open the assignment → Create remediation task Assignment → Remediate Or Policy → Remediation → New remediation task
8 Watch the task; open it to see per-resource deployment results Remediation blade Each resource = Succeeded

The portal’s one genuine trap is step 5: the wizard offers to create the role assignments the policy declares, and if you (or a colleague) untick it or click past it, the identity ends up with no roles and every remediation deployment 403s — the exact failure from the Northwind scenario.

Step 8 — Teardown

# Remove the remediation task, role assignments, policy assignment, then the RG
az policy remediation delete --name "remediate-diag-storage-lab" --resource-group "$RG"

for ROLE in "Monitoring Contributor" "Log Analytics Contributor"; do
  az role assignment delete --assignee "$PRINCIPAL_ID" --role "$ROLE" \
    --scope "/subscriptions/$SUB_ID"
done

az policy assignment delete --name "$ASSIGN" --scope "/subscriptions/$SUB_ID"
az group delete --name "$RG" --yes --no-wait

Expected output: the role and policy assignments are removed, and the resource group deletion runs in the background. Deleting the assignment also removes its system-assigned identity automatically (a user-assigned identity, if you used the Bicep path, lives in its own RG and must be deleted separately). Confirm the policy assignment is gone:

az policy assignment show --name "$ASSIGN" --scope "/subscriptions/$SUB_ID" 2>/dev/null \
  || echo "Assignment deleted."

Common mistakes & troubleshooting

The failure modes here are concentrated and predictable — almost all are identity, role, scope, or cache. Scan the playbook, then read the detail for your row.

# Symptom Root cause Confirm (exact path/command) Fix
1 Existing resources stay non-compliant; nothing changes DINE/Modify only fire at deployment time Dashboard: old resources red, new ones green Run a remediation task
2 Remediation task Succeeded but resources unchanged Identity missing the required role → per-resource 403 az policy remediation deployment list shows 403 Grant the policy’s roleDefinitionIds to the identity
3 Every deployment AuthorizationFailed No role assignment at all on the identity az role assignment list --assignee <principalId> empty Create the role assignments at the assignment scope
4 Some resources fixed, some 403 Role scoped too narrowly (one RG, not the sub) Compare role scope vs failing resources’ scope Grant the role at the assignment scope
5 Task finds 0 resources to remediate Cold/stale compliance cache (ExistingNonCompliant) Task shows 0 deployments right after assigning Re-scan, or use --resource-discovery-mode ReEvaluateCompliance
6 Cannot create the assignment (identity error) Forgot --mi-system-assigned/--location Assignment JSON has no identity block Recreate with the identity flag + location
7 Initiative: some policies remediate, one never does Identity missing that policy’s role Read each member policy’s roleDefinitionIds Grant the union of all required roles
8 “No remediation task option” on an Audit policy Audit/Deny are not remediable Effect in the definition is audit/deny Only Modify/DINE can be remediated
9 Per-resource deployment Conflict Concurrent write or a resource lock Check resource locks; activity log Remove/redo; re-run the task
10 principal not found when granting the role New identity not yet replicated in Entra ID Role create retries/fails immediately Pass --assignee-principal-type ServicePrincipal
11 Remediation works once, drift returns later DINE doesn’t loop; it’s not continuous Next scan re-flags the deleted setting Re-run remediation (or automate it on a schedule)
12 Task aborts partway Failure threshold tripped by many 403s Task Canceled; failed % over threshold Fix RBAC, then re-run

The “succeeded but did nothing” trap (rows 2-4)

This is the defining remediation bug. The task status (the orchestration) is independent of whether each per-resource deployment succeeded — a task reports Succeeded while every deployment under it failed with 403, because the orchestration ran fine: it launched the deployments, it just can’t see that your identity lacks permission. The error-to-cause map:

What you see What it actually means The real signal to check
Task provisioningState: Succeeded The orchestration completed Not that resources were fixed
successfulDeployments: 0, failedDeployments: N Every per-resource deploy failed The role/scope is wrong
Per-resource error.code: AuthorizationFailed The identity couldn’t PUT Missing role at this scope
setting still absent after a green task Deployment never wrote Same — RBAC, confirmed by deployment list

Always validate remediation by two signals: the task’s successfulDeployments count and the actual target setting existing on a sample resource. Never declare victory on the task status alone.

Reading the roles correctly for initiatives (row 7)

For a single definition you grant one or two roles. For an initiative, you must grant the union of roleDefinitionIds across every remediable member policy, or the members whose roles you missed 403 while the others succeed — the partial-success pattern that’s maddening to debug. Enumerate them:

# For an initiative assignment, list the role IDs every member DINE/Modify policy needs
SETDEF_ID=$(az policy assignment show --name <initiative-assignment> \
  --scope "/subscriptions/$SUB_ID" --query policyDefinitionId -o tsv)

az policy set-definition show --name "$(basename "$SETDEF_ID")" \
  --query "policyDefinitions[].policyDefinitionId" -o tsv \
  | while read -r d; do
      az policy definition show --name "$(basename "$d")" \
        --query "policyRule.then.details.roleDefinitionIds" -o tsv 2>/dev/null
    done | sort -u
# → the deduplicated set of roles to grant the identity

Grant every role in that deduplicated list to the assignment’s identity at the assignment scope. This is the single most-missed step for initiative remediation.

Best practices

Security notes

The remediation identity is a non-human principal with standing write access to whatever its roles allow across the assignment scope, so it deserves the same least-privilege scrutiny as any privileged service principal. Grant exactly the roles the policy declares in roleDefinitionIds, at the assignment scope and no wider. The shortcut of granting Contributor or Owner “so it just works” turns a narrow diagnostic-settings writer into an identity that can modify or delete most of the subscription — a real privilege-escalation and blast-radius problem if it is ever compromised or misconfigured.

Risk Why it matters Mitigation
Over-privileged identity (Owner/Contributor) Standing broad write access for a robot Grant only the declared roleDefinitionIds
Role scoped too broadly (tenant root MG) Remediation can write across the whole org Scope the grant to the assignment scope
User-assigned identity reused too widely One identity holds the union of many roles Separate identities per governance domain
Orphaned identities after assignment deletion Lingering credentials nobody tracks Tag identities; clean up with the assignment
Remediation writing into prod unaudited Bulk changes with no record Use per-resource deployment history as evidence
Modify policy rewriting security settings A bad Modify could weaken config at scale Review the operations; pilot on one RG first

Two more identity-hygiene points. First, prefer system-assigned when an identity serves a single assignment — its lifecycle is bounded and it’s auto-cleaned, so there’s no orphan to forget; use user-assigned deliberately, for initiatives and reuse, and track it. Second, the remediation deployment history is genuine audit evidence: each per-resource deployment is recorded, so a run is a defensible record of “we brought these N resources into compliance on this date as this identity” — far better material than a dashboard that merely flipped to green. Managed Identity: System-Assigned vs User-Assigned Patterns and Azure RBAC Fundamentals: Roles, Scope, Assignments are the companions to this section.

Cost & sizing

Remediation itself is free — Azure Policy, assignments, compliance evaluation, and remediation tasks carry no per-operation charge. What can cost money is what the remediation deploys and the resources it touches, plus a little operational overhead.

Cost driver Charged? Notes
Azure Policy assignments & evaluation Free No charge for definitions, assignments, scans
Remediation tasks & per-resource deployments Free The orchestration and ARM deployments are free
Managed identity (system or user-assigned) Free Identities themselves cost nothing
What the policy deploys (e.g. diagnostic settings → Log Analytics) Yes Log ingestion + retention is the real bill
Resources the remediation configures (e.g. backup vault) Yes Backup storage, agents, etc. are billed
Log Analytics workspace (lab) Yes (small) Pay-per-GB ingestion; the lab’s volume is negligible
Storage account (lab) Yes (tiny) Standard LRS, no data → effectively free

The number that matters at scale is log ingestion. A diagnostic-settings remediation that switches on logging for 300 storage accounts starts ingesting their logs into Log Analytics — and at roughly the standard pay-as-you-go rate (on the order of a couple of US dollars per GB; varies by region and commitment tier), a large estate can move the monitoring bill meaningfully. That’s a feature, not a surprise, if you anticipate it: size the workspace, set retention deliberately, and consider a commitment tier if volume is high. For this lab the volumes are trivial and teardown removes everything; in production, model the downstream ingestion before remediating hundreds of resources at once. Azure Monitor & Application Insights for Observability covers the ingestion and retention economics.

A sizing rule of thumb: remediation is free to run, so the question is never “can I afford to remediate” but “can I afford the steady-state cost of the control I’m switching on across the estate” — model that, not the remediation.

Interview & exam questions

Q1. Why do DeployIfNotExists policies leave pre-existing resources non-compliant? DINE evaluates at deployment time — it hooks the Resource Manager PUT/PATCH of a resource. Resources that existed before the assignment and aren’t redeployed never trigger that hook. The periodic scan flags them non-compliant but is read-only, so they stay unfixed until a remediation task replays the effect on demand.

Q2. What is a remediation task and which effects can it run? A remediation task is an on-demand replay of an assignment’s remediation action against existing non-compliant resources, launching the policy’s deployment (DINE) or operations (Modify) for each. Only Modify and DeployIfNotExists are remediable; Audit and Deny have no write action to replay.

Q3. Why does a remediation task need a managed identity, and how do you know which roles to grant it? The remediation deployment authenticates to Azure as the assignment’s managed identity to write the fix. The exact roles are declared in the policy definition’s then.details.roleDefinitionIds — you read those and grant precisely them to the identity at the assignment scope. Missing roles cause 403 AuthorizationFailed on every deployment.

Q4. A remediation task reports Succeeded but no resources changed. What happened and how do you confirm? The task status reflects the orchestration, not per-resource success. The deployments almost certainly failed with AuthorizationFailed because the identity lacks the required role. Confirm with az policy remediation deployment list, which shows each resource’s deployment status and error code; the fix is granting the policy’s declared roles.

Q5. When would you choose a user-assigned identity over a system-assigned one for remediation? For initiatives with multiple remediable policies, and for governance baselines spanning many subscriptions or frequent re-creations. A user-assigned identity is granted the union of all required roles once and reused, so you don’t re-grant roles every time an assignment is recreated. System-assigned is simpler for a single policy in one scope.

Q6. What is --resource-discovery-mode and when do you change it from the default? It controls whether the task acts on the cached non-compliant list (ExistingNonCompliant, the default) or re-evaluates each in-scope resource live (ReEvaluateCompliance). Use ReEvaluateCompliance right after assigning a policy — before a scan has populated the compliance cache — otherwise the task may find zero resources.

Q7. For an initiative with several DINE policies, what’s the identity gotcha? The identity must hold the union of roleDefinitionIds across every remediable member policy. If you grant only some, those policies’ remediations 403 while the rest succeed — a partial-success pattern. Enumerate every member’s roles and grant the deduplicated set.

Q8. Does DeployIfNotExists continuously enforce compliance? No. DINE acts at deployment time and via on-demand remediation; it does not loop and auto-fix drift. If a remediated setting is later deleted, the next scan re-flags the resource non-compliant and you must remediate again — drift handling has to be an explicit re-run or an automated pipeline.

Q9. Your remediation fixes resources in one resource group but 403s elsewhere in the subscription. Why? The identity’s role assignment is scoped to that one resource group, while the policy is assigned at subscription scope. Remediation can only write where the identity has the role; grant the role at the assignment scope (the subscription) so it covers all in-scope resources.

Q10. How do you make policy remediation repeatable as infrastructure-as-code? Declare the assignment (with its identity), the user-assigned identity, and the role assignment(s) at the assignment scope in Bicep/Terraform, then trigger the initial remediation via az policy remediation create (or a deployment script). The assignment, identity and roles are desired state; the remediation task is an action you fire after they exist.

Q11. What two signals prove a remediation actually worked? The task’s successfulDeployments count being non-zero with failedDeployments: 0, and the actual target setting existing on a sample resource. The task’s overall provisioningState: Succeeded alone is insufficient because it reflects the orchestration, not per-resource writes.

Q12. (AZ-500 / AZ-104 framing) Where do remediation tasks and identities map on the exams? AZ-104 covers governance: assigning policy, interpreting compliance, and remediation. AZ-500 emphasises the security angle: the remediation identity as a privileged non-human principal, least-privilege role grants, and remediation history as audit evidence. Both expect you to know that DINE/Modify need an identity and that only those two effects are remediable.

Quick check

  1. You assign a DeployIfNotExists policy to a subscription with 200 existing resources. A day later, why are most still non-compliant, and what single action fixes them?
  2. A remediation task shows provisioningState: Succeeded but the target setting is missing on every resource. What is the most likely cause and the one command that confirms it?
  3. You’re remediating an initiative with three DINE policies; two remediate fine, one always 403s. What did you miss?
  4. Right after assigning a policy you run a remediation task and it reports “0 resources.” What setting changes that, and why?
  5. Which two policy effects can a remediation task act on, and where in the definition do you find the roles the identity needs?

Answers

  1. DeployIfNotExists only fires at deployment time, so resources that existed before the assignment were never re-deployed and stay non-compliant; the periodic scan flags them but never writes. The single fix is to create a remediation task for the assignment, which replays the effect against the existing non-compliant resources.
  2. The assignment’s managed identity is missing the required RBAC role, so each per-resource deployment fails with 403 AuthorizationFailed even though the task orchestration “succeeded.” Confirm with az policy remediation deployment list, which shows each deployment’s status and error.code.
  3. You granted only some of the roles. An initiative needs the union of roleDefinitionIds across all remediable member policies; the one that 403s declares a role you didn’t grant the identity. Enumerate every member’s roles and grant the deduplicated set at the assignment scope.
  4. Switch --resource-discovery-mode to ReEvaluateCompliance. The default ExistingNonCompliant uses the cached compliance list, which is empty/stale immediately after assignment because no scan has run yet; ReEvaluateCompliance re-checks each in-scope resource live so the task finds the non-compliant ones.
  5. Modify and DeployIfNotExists only. The roles are declared in the definition at policyRule.then.details.roleDefinitionIds, and you grant exactly those to the assignment’s identity at the assignment scope.

Glossary

Next steps

AzureAzure PolicyRemediationManaged IdentitydeployIfNotExistsGovernanceComplianceRBAC
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading