Why
Enforcement reduces violations but will never reach 100% — console deployments, legacy resources, auto-created infrastructure, and services that don’t support tags all create gaps. Without measurement, you don’t know the size of the gap, you can’t hold teams accountable, and you can’t prove improvement over time. Spend-weighted compliance is the metric that matters: a single untagged $50K database matters more than 100 untagged $2 dev VMs.
What
Build a tag compliance measurement system that tracks coverage by team, surfaces trends, and feeds into governance reviews. The output is a dashboard and a weekly report cadence.
How
Define Metrics
Primary metrics (track weekly):
| Metric | Formula | Target |
|---|---|---|
| Tag Coverage (spend-weighted) | tagged spend / total spend × 100, per tag | > 98% |
| Tag Coverage (resource count) | tagged resources / total resources × 100, per tag | > 95% |
| Unallocated Spend ($) | Total spend where cost-center is null | < 2% |
| Tag Compliance Score | Average coverage across all mandatory tags | > 90% |
Secondary metrics (track monthly):
| Metric | Formula | Target |
|---|---|---|
| Tag Quality | resources with valid values / tagged resources × 100 | > 99% |
| Tag Drift Rate | resources that lost tags / total resources per month | < 1% |
| Time to Remediation | Median days from violation to tag applied | < 5 days |
| New Resource Compliance | new resources created compliant / total new × 100 | > 99% |
Build Queries per Provider
AWS — Athena on CUR 2.0
SELECT
line_item_usage_account_id,
SUM(line_item_unblended_cost) AS spend,
CASE
WHEN resource_tags_user_cost_center IS NULL THEN 'untagged'
ELSE 'tagged'
END AS status
FROM cur_database.cost_and_usage
WHERE billing_period = '2026-03'
GROUP BY 1, 3 Azure — Resource Graph + Cost Management
-- Azure Resource Graph (KQL)
Resources
| where isnull(tags["cost-center"])
| summarize count() by subscriptionId Cost Management Tag Inheritance must be enabled so that subscription/RG tags propagate to cost records for spend-weighted queries.
GCP — BigQuery Billing Export
SELECT
project.id,
SUM(cost) AS total_cost,
SUM(CASE
WHEN (SELECT COUNT(*) FROM UNNEST(labels) WHERE key = 'cost_center') = 0
THEN cost ELSE 0
END) AS untagged_cost
FROM `billing_dataset.gcp_billing_export`
WHERE invoice.month = '202603'
GROUP BY 1 Build the Tag Compliance Dashboard
Create a dashboard with these widgets:
| Widget | What It Shows |
|---|---|
| Tag Coverage (spend-weighted) | Big number: % of total spend with all mandatory tags |
| Coverage by Mandatory Tag | Horizontal bar per tag key |
| Coverage by Team / BU | Heatmap or colour-coded table |
| Unallocated Spend ($) | Absolute dollar value that cannot be charged back |
| Trend Over Time | Weekly line chart showing coverage improvement |
| Worst-Performing Teams | Table sorted by lowest compliance (constructive) |
Tooling per provider:
| Provider | Dashboard Tool |
|---|---|
| AWS | QuickSight (native), Grafana with Athena data source |
| Azure | Power BI (native), Azure Workbooks for Policy compliance |
| GCP | Looker Studio (free, native BigQuery connector) |
| Cross-cloud | Grafana (unified), or commercial FinOps platforms |
Establish Reporting Cadence
| Cadence | Owner | Activity |
|---|---|---|
| Weekly | FinOps team | Generate compliance report. Escalate SLA breaches. |
| Monthly | Eng leads + FinOps | Review compliance by BU. Highlight worst performers. |
| Quarterly | Mgmt + FinOps | Taxonomy review. Tighten targets. Refresh vocabs. |
| Annually | VP + FinOps | Strategy review against maturity benchmarks. |
Deliverable Checklist
- Primary and secondary metrics defined with targets
- Billing data queries operational per provider
- Tag compliance dashboard live and accessible
- Weekly reporting cadence established
- Monthly BU-level review scheduled
- Quarterly taxonomy review in governance calendar