azureblog.co.uk
← cd ~/learn
// hands-on lab · intermediate

Fix existing resources with Azure Policy remediation

Assign the built-in policy that copies a tag from the resource group, give it a managed identity, run a remediation task to fix what's already there, and check compliance from the CLI.

time
45 minutes
level
Intermediate
cost
Free. Azure Policy has no charge, and the test resources in this lab cost nothing.
You'll need
  • An Azure subscription where you can create a resource group
  • Owner on that resource group, or Resource Policy Contributor plus User Access Administrator, because the assignment creates a role assignment for its managed identity
  • Azure CLI signed in with az login, or Cloud Shell
0 of 8 steps done
Ticks are saved in this browser only. Checked 9 Oct 2026: confirm details in Microsoft's documentation before using in production.
  1. 01

    Create a tagged resource group with untagged resources

    The policy only adds a tag that the resource group already has, so start with a tagged group and a few resources that don't carry the tag. and empty are free, which makes them handy for policy labs.

    shell6 lines
    rg=rg-policy-lab
    az group create -n $rg -l uksouth --tags CostCentre=CC-1234
    az network nsg create -g $rg -n nsg-lab-01
    az network nsg create -g $rg -n nsg-lab-02
    az network vnet create -g $rg -n vnet-lab-01 --address-prefixes 10.50.0.0/16
    rgId=$(az group show -n $rg --query id -o tsv)
  2. 02

    Look at the built-in definition

    The built-in policy Inherit a tag from the resource group if missing has a fixed ID in every tenant. It uses the modify effect to add the tag, takes one parameter, tagName, and lists the role its needs. That role is Contributor.

    shell2 lines
    az policy definition show --name ea3f2387-9b95-492a-a190-fcdc54f7b070 \
      --query "{name:displayName, mode:mode, effect:policyRule.then.effect, roles:policyRule.then.details.roleDefinitionIds}"

    The mode is Indexed, so it only evaluates resource types that support tags and location. It also leaves a tag alone if the resource already has it with a different value. If you want resources forced to match the group, the related built-in Inherit a tag from the resource group replaces the value instead.

  3. 03

    Assign it with a managed identity

    A modify or deployIfNotExists assignment needs a managed identity, because changes resources as that identity rather than as you. In the portal, go to Policy → Assignments → Assign policy, set the scope to the resource group, and pick the definition. On Parameters, set Tag Name to CostCentre. On Remediation, leave Create a remediation task unticked for now, keep System assigned managed identity selected and choose a location. The portal grants the identity the roles from the definition for you.

    From the CLI you have to ask for the role assignment yourself with --role and --identity-scope:

    shell7 lines
    az policy assignment create --name inherit-costcentre \
      --display-name "Inherit CostCentre tag from resource group" \
      --policy ea3f2387-9b95-492a-a190-fcdc54f7b070 \
      --scope $rgId \
      --params '{ "tagName": { "value": "CostCentre" } }' \
      --mi-system-assigned --location uksouth \
      --identity-scope $rgId --role Contributor

    The identity's location doesn't affect how it works. It's just where the identity is recorded.

  4. 04

    Check compliance

    A new assignment takes about five minutes to apply, then Azure Policy evaluates the existing resources. Rather than wait for the next automatic cycle, start a scan of the resource group, then summarise and list what's non-compliant:

    shell4 lines
    az policy state trigger-scan -g $rg
    az policy state summarize -g $rg -a inherit-costcentre
    az policy state list -g $rg -a inherit-costcentre \
      --filter "complianceState eq 'NonCompliant'" --query "[].resourceId" -o tsv

    You should see the two NSGs and the VNet. In the portal, the same view is Policy → Compliance, then select the assignment.

  5. 05

    See the policy fix new resources by itself

    The modify effect runs whenever a resource is created or updated, so anything new gets the tag straight away:

    shell1 line
    az network nsg create -g $rg -n nsg-lab-03 --query "NewNSG.tags"

    Existing resources are different. They stay non-compliant until someone updates them or you run a , which is the next step.

  6. 06

    Create a remediation task

    In the portal, go to Policy → Remediation. The Policies to remediate tab lists every modify and deployIfNotExists assignment. Select yours, check the list of resources, and select Remediate. Progress appears on the Remediation tasks tab.

    From the CLI:

    shell5 lines
    az policy remediation create -g $rg -n remediate-costcentre \
      --policy-assignment inherit-costcentre
    az policy remediation show -g $rg -n remediate-costcentre \
      --query "{state:provisioningState, status:deploymentStatus}"
    az policy remediation deployment list -g $rg -n remediate-costcentre -o table

    By default a task fixes the resources already recorded as non-compliant. Add --resource-discovery-mode ReEvaluateCompliance to have it re-evaluate first, which is useful if the last scan is out of date.

  7. 07

    Confirm the result

    When the task shows Succeeded, every resource should carry the tag, and a fresh scan should report nothing non-compliant:

    shell4 lines
    az resource list -g $rg --query "[].{name:name, costCentre:tags.CostCentre}" -o table
    az policy state trigger-scan -g $rg
    az policy state list -g $rg -a inherit-costcentre \
      --filter "complianceState eq 'NonCompliant'" --query "length(@)"

    If a deployment failed, open the task in the portal, select the resource and use Related events to see the error. A missing role assignment for the identity is the usual cause.

  8. 08

    Clean up

    Remove the identity's role assignment before the policy assignment. Deleting the assignment deletes its system-assigned identity, and a role assignment left behind shows up as an unknown identity in Access control (IAM).

    shell4 lines
    principalId=$(az policy assignment show --name inherit-costcentre --scope $rgId --query identity.principalId -o tsv)
    az role assignment delete --assignee $principalId --role Contributor --scope $rgId
    az policy assignment delete --name inherit-costcentre --scope $rgId
    az group delete -n $rg --yes --no-wait

    In production, assign this at a or subscription with a tag such as CostCentre or Owner, and pair it with a policy that requires the tag on resource groups. Inheritance only works if the group has the tag to give.