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.
- 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
- 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. Network security groups and empty virtual networks are free, which makes them handy for policy labs.
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) - 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
modifyeffect to add the tag, takes one parameter,tagName, and lists the role its managed identity needs. That role is Contributor.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. - 03
Assign it with a managed identity
A
modifyordeployIfNotExistsassignment needs a managed identity, because Azure Policy 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 toCostCentre. 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
--roleand--identity-scope: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 ContributorThe identity's location doesn't affect how it works. It's just where the identity is recorded.
- 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:
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 tsvYou should see the two NSGs and the VNet. In the portal, the same view is Policy → Compliance, then select the assignment.
- 05
See the policy fix new resources by itself
The
modifyeffect runs whenever a resource is created or updated, so anything new gets the tag straight away: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 remediation task, which is the next step.
- 06
Create a remediation task
In the portal, go to Policy → Remediation. The Policies to remediate tab lists every
modifyanddeployIfNotExistsassignment. Select yours, check the list of resources, and select Remediate. Progress appears on the Remediation tasks tab.From the CLI:
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 tableBy default a task fixes the resources already recorded as non-compliant. Add
--resource-discovery-mode ReEvaluateComplianceto have it re-evaluate first, which is useful if the last scan is out of date. - 07
Confirm the result
When the task shows
Succeeded, every resource should carry the tag, and a fresh scan should report nothing non-compliant: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.
- 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).
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-waitIn production, assign this at a management group or subscription with a tag such as
CostCentreorOwner, and pair it with a policy that requires the tag on resource groups. Inheritance only works if the group has the tag to give.