How to Monitor Azure Resource Tags with Azure Monitor

Azure resource tags are the foundation of cost allocation, chargeback, and governance. Without tags, Cost Management shows you what Azure costs but cannot tell you which team, project, or environment is generating the spend. Without enforced tag standards, the tags you do have are inconsistent (some resources say environment: prod, others say Environment: Production, others have no tag at all), and any report based on them is unreliable.

Monitoring tag compliance means knowing, at any given time, which resources are missing required tags and which resources have tags that do not conform to the defined values. This guide covers how to build that visibility using Azure Policy, Azure Monitor, and Log Analytics.

Define your tagging standard first

Before monitoring, the standard must exist. Define the required tags for your organisation:

Environment: dev, staging, prod (exact values, enforced) Owner: the team or individual responsible (e.g., platform-team, data-engineering) CostCentre: the cost code for chargeback Project: the project or workload the resource belongs to ManagedBy: terraform, bicep, manual (useful for governance)

The standard should be documented and version-controlled. Tag values should be constrained to an allowed list where consistency matters (Environment and ManagedBy, for example) or defined as free-text for values that cannot be enumerated (Owner, Project).

Enforce tagging with Azure Policy

Azure Policy is the enforcement mechanism. Without enforcement, tags drift: resources are created without tags, existing tags are modified or removed, and the tag data becomes unreliable.

Append policy: Automatically appends a tag with a default value when a resource is created without it. Useful for tags with a sensible default that should always be present.

Deny policy: Prevents creation of resources that do not have required tags. This is the strongest enforcement but requires teams to understand the tagging requirement before provisioning, which can cause friction if the policy is introduced on an existing estate.

Modify policy (remediation): Adds or updates tags on existing non-compliant resources. Use this for remediating existing resources that pre-date the tagging policy.

A practical policy set for a new tagging initiative: 1. Audit non-compliant resources (without denying) for 30 days to understand the baseline 2. Apply Append policies for tags with sensible defaults 3. Apply Modify policies with remediation tasks to tag existing resources 4. Transition to Deny policies for new resource creation once the existing estate is compliant

To create an audit policy for the Environment tag:

az policy definition create \
  --name "require-environment-tag" \
  --display-name "Require Environment tag on all resources" \
  --description "Audits resources missing the Environment tag" \
  --rules '{
    "if": {
      "field": "tags[Environment]",
      "exists": "false"
    },
    "then": {
      "effect": "Audit"
    }
  }' \
  --mode "All"

az policy assignment create \
  --name "require-environment-tag-assignment" \
  --scope "/subscriptions/{subscriptionId}" \
  --policy "require-environment-tag"

Query tag compliance in Log Analytics

Azure Resource Graph queries provide instant tag compliance visibility. Run these in the Resource Graph Explorer (search for Resource Graph Explorer in the portal) or via the CLI:

Find resources missing any required tag:

resources
| where type !in ("microsoft.resources/subscriptions", "microsoft.resources/resourcegroups")
| where tags !contains "Environment" 
   or tags !contains "CostCentre"
   or tags !contains "Owner"
| project name, type, resourceGroup, tags
| order by type asc

Find resources with non-standard Environment tag values:

resources
| where isnotempty(tags["Environment"])
| where tags["Environment"] !in ("dev", "staging", "prod")
| project name, type, resourceGroup, 
          nonStandardValue = tags["Environment"]

Count of untagged resources by resource group:

resources
| where tags !contains "Environment"
| summarize UntaggedCount = count() by resourceGroup
| order by UntaggedCount desc

Export these queries as Azure Monitor alerts (scheduled query alerts on Resource Graph results) to receive daily or weekly compliance reports.

Track tag changes in the Activity Log

When a tag is added, removed, or modified on a resource, the change is recorded in the Azure Activity Log. Monitoring tag changes lets you detect when tags are being removed (which breaks cost allocation) or when non-standard values are introduced.

Route Activity Log to Log Analytics and query:

AzureActivity
| where OperationNameValue contains "tag"
| where ActivityStatusValue == "Success"
| project TimeGenerated, Caller, ResourceId, OperationNameValue, Properties
| order by TimeGenerated desc

Set an alert on this query filtered for changes to critical cost allocation tags. A tag change to CostCentre or Owner on a high-spend resource is worth knowing about immediately.

Cost allocation reports by tag

Once tags are consistently applied, Cost Management can break down costs by tag dimension. Navigate to Cost Management > Cost Analysis and group by a tag key (e.g., Environment). This produces a cost breakdown per environment, per owner, or per project.

For automated reporting, export Cost Management data to Storage and query with custom KQL or Power BI. The export includes tag values for each resource alongside cost data, enabling fully tagged cost allocation reports.

For chargeback reports, export costs grouped by CostCentre and Owner and distribute to the respective teams. This is the practical output of a well-maintained tagging programme.

Automate tag remediation for untagged resources

A runbook that identifies untagged resources and applies default tags provides continuous remediation:

Connect-AzAccount -Identity

$resources = Get-AzResource | Where-Object { -not $_.Tags.ContainsKey("Environment") }

foreach ($resource in $resources) {
    $currentTags = $resource.Tags
    if (-not $currentTags) { $currentTags = @{} }

    $currentTags["Environment"] = "unknown"  # flag for manual review
    $currentTags["TaggedBy"] = "AutoRemediation"
    $currentTags["TaggedAt"] = (Get-Date -Format "yyyy-MM-dd")

    Set-AzResource -ResourceId $resource.ResourceId -Tag $currentTags -Force
    Write-Output "Tagged: $($resource.Name)"
}

Using Environment: unknown as the auto-remediation value flags the resource for review rather than silently assigning an incorrect tag. Track resources with Environment: unknown as a compliance metric that should trend to zero.

Where Critical Cloud comes in

Tag governance on its own is not a glamorous problem, but it is the prerequisite for every meaningful cost and governance report. An estate without consistent tagging cannot produce accurate chargeback, cannot demonstrate which resources belong to which environment in an audit, and cannot enforce policy at the right granularity. We implement and operate Azure tagging governance for regulated and technology-led businesses, with tag compliance monitoring as a standing operational control. If your Azure cost reports cannot answer "who owns this spend," see how Critical Support works.