Outcome
By the end of this lesson, you will be able to trace any dollar of cloud spend back to a team using tags, and identify the four tags every cloud estate needs as the minimum viable tag set.
New to the cloud? The optional track Cloud, from zero covers the basics first.
| Prerequisites | L1, L2 |
| Time | 10 minutes |
1. Concept
A cloud bill without tags is a pile of money with nobody’s name on it.
A tag is a label you attach to a resource (any one thing you rent from the cloud: a server, a disk, a database): a key and a value, such as team = billing. The provider copies those labels onto every charge that resource generates. Tag well and any amount on the bill can be traced to an owner with one query. On AWS there is one extra step: activate each tag key as a cost allocation tag in the Billing console. It then appears on charges from that point on, unless you request a backfill of earlier months. Tag badly and the bill becomes something teams argue about rather than act on.
Google Cloud calls them labels; AWS (Amazon Web Services) and Microsoft Azure call them tags. The mechanics are the same, and so is the minimum set worth having.
The minimum viable tag set (MVT)
Four tags. Every resource carries them. The values are governed, meaning only an agreed list of values is allowed.
TAG KEY WHO USES IT EXAMPLE VALUES────────────────────────────────────────────────────────────environment Finance, Eng, Sec dev | test | stage | prodteam Finance, Eng billing | identity | growth | datacost_center Finance, Procurement eng-platform | eng-product | gtm | corpowner Sec, Ops an email address or group aliasMore than these four is optional. Fewer is a gap you will feel within a month. Together they answer the four questions that actually get asked:
- Which environment is spending the most? (group by environment)
- Which team owns this resource? (look at team)
- What cost center does this charge against? (look at cost_center)
- Who do we email when this misbehaves? (look at owner)
The cost-attribution problem
Attribution means working out who a cost belongs to. Without tags, the only thing you can attribute by is the cloud account (the billing container a provider gives you; a company usually has many). One account, one team. That holds at small scale. It breaks the first time a team uses a shared services account (one that hosts tools several teams use), a sandbox (a test account for experiments) is passed between teams, or two teams co-own an application.
With tags, attribution comes from the resource itself, so a shared database can be split between the teams using it. Anything untagged collects in a bucket usually labelled Unattributed: visible, but unowned.
That bucket is worth watching as a number in its own right. It should be shrinking. If it is not, tagging is a policy nobody is enforcing.
The tag-drift problem
Tags decay unless somebody governs them. Three forces do the damage.
- Manual tagging. Somebody creates a resource by hand in the console (the provider’s website for managing an account) and skips the tags. The resource exists, the bill is running, and the owner field is empty.
- Reorganisations. A team called
growthbecomesmonetization. New resources carry the new name, old ones keep the old one, and one team’s spending now appears as two. - Case and spelling. A policy says
environmentmust be lowercase, but existing resources carryEnvironment=Production. Reports treat “Production” and “prod” as two different places.
Tag drift is this slow decay. The fix is tag policy as code: the rules are written into the infrastructure-as-code tools teams use to create resources from files rather than by hand (Terraform, CDK, Pulumi), checked before any change is accepted, and audited continuously so drift gets flagged.
A note on hierarchies
Tags are flat. There is no built-in parent and child, no team.subteam. If you need hierarchy you either add a second key, team plus subteam, or encode it in the value, team=identity-auth and team=identity-mfa.
Either works. Mixing the two does not, and it is very hard to unpick a year later.
2. Demo
The same query, three providers, attributing yesterday’s compute spend (rented servers) to teams. You do not need to run these. The AWS one queries the CUR, AWS’s detailed billing file; the Google one queries the BigQuery billing export, Google’s billing data in its data warehouse; the Azure one uses the Azure command-line tool.
AWS:
-- CUR querySELECT resource_tags_user_team AS team, SUM(line_item_unblended_cost) AS spendFROM cur.consolidated_2026_05WHERE line_item_product_code = 'AmazonEC2' AND DATE(line_item_usage_start_date) = DATE '2026-05-19'GROUP BY resource_tags_user_teamORDER BY spend DESC;GCP:
-- BigQuery billing exportSELECT labels.value AS team, SUM(cost) AS spendFROM `billing_export.gcp_billing_export_v1_*`, UNNEST(labels) AS labelsWHERE labels.key = 'team' AND service.description = 'Compute Engine' AND DATE(usage_start_time) = '2026-05-19'GROUP BY teamORDER BY spend DESC;Azure:
az consumption usage list \ --start-date 2026-05-19 \ --end-date 2026-05-20 \ --query "[?contains(meterCategory, 'Virtual Machines')].{team:tags.team, cost:pretaxCost}" \ -o json | jq 'group_by(.team) | map({team:.[0].team, cost: (map(.cost | tonumber) | add)})'Sample output:
team spend─────────────────identity $1,247.30growth $ 893.10data $ 612.40(null) $ 211.80 ← Unattributed. This bucket should trend to zero.3. Hands-on (8 min)
This needs access to your company’s cloud bill. If you do not have it, read the steps and picture them against the Demo output.
- Pick the four tag keys:
environment,team,cost_center,owner. - Query your own bill for last month. Group by
team(or label-equivalent). - Note the Unattributed bucket. What percent of total spend has no team tag?
- Pull a list of the top 20 untagged resources (sort by spend descending).
- For the top 5 untagged resources, find the actual owner. Use the cloud console, Slack, or a CMDB (a register of the company’s systems and owners).
- If the owner can be determined, apply the four MVT tags through your infrastructure-as-code files or the console. If the owner cannot be determined, the resource is a candidate for the orphan list (resources nobody owns, often left behind): see lesson 5.
Time-bounded version: do this for the top 3 untagged resources only. Eight minutes, not eight hours.
4. Knowledge check
Q1
A FinOps Analyst reports: “We can’t attribute 18% of our cloud bill.” The most accurate first response is:
A. The cloud provider is hiding cost data
B. The tag coverage is incomplete
C. Tags don’t work at scale; use account boundaries
D. Buy a more expensive FinOps tool
Show answer
Correct: B. Find the untagged resources by spend, then assign owners. 18% Unattributed is high but not unusual. The fix is mechanical: list the untagged resources by spend, find the owners, apply the four MVT tags, and re-run the report.
Q2
A reorg renames “Growth” to “Monetization.” The team field on existing resources is not updated. What is the immediate cost-reporting consequence?
A. The cost of the old Growth team continues to show up under the old name, while new resources show under the new name
B. No consequence
C. Tags become invalid
D. The bill double-counts the spend, since both the old and the new tag value are now present on the resource
Show answer
Correct: A. The combined team’s spend appears split. Tag values are not retroactive. The old team=growth resources carry that value until updated. Reports will show two separate buckets that are the same team. The fix is a bulk tag update on existing resources at the time of the reorg.
Q3
A new tag policy requires environment to be lowercase. The CI policy is added today. What needs to happen?
A. Nothing; new resources comply
B. Run a one-time bulk update of existing resources (rename Environment to environment, normalize values to lowercase)
C. Wait three months for all of the old resources to be replaced naturally through the normal deployment cycle
D. Just live with the inconsistency
Show answer
Correct: B. Then the policy holds. New policies on new resources do not retroactively fix old resources. A one-time reconciliation is required at the moment a policy changes.
5. Apply
ZopNight, the cost platform these lessons are built around, has a Reports page that exposes the cost-attribution surface in three ways:
- Costs → Reports → Teams splits spend by the
teamtag (and shared services by share count). The Unattributed bucket appears as its own row. - Costs → Reports → Tags lets any tag key (a tag set in the cloud itself, or an accepted Smart Tag) drive the breakdown. Stacked trend chart, expandable rows for provider and resource-type breakdowns.
- Tag Coverage card on Costs → Reports → Tags shows the share of cost that carries tags as a percentage and a bar, with the untagged cost and the number of untagged resources beside it. A sensible target is 95% or better.
Smart Tags closes the gap from the other side. You write a tagging policy that enforces a tag key and derives its value from the resource’s own fields (provider, region, type, instance_type, name), and the derived value counts toward attribution inside ZopNight once accepted, without anything being written to your cloud account. A derived value that already matches the resource’s live cloud tag is accepted automatically. Accepted tags carry dimension_source=auto so they stay distinguishable from cloud-native ones.
Open ZopNight Costs → Reports → Tags (deep link)
Related lessons
- L5: The ten cost mistakes that show up on every bill (next)
- T2.M2.8: Smart Tags
- T4.M4.5: Showback design
- T3.M3.1: Tagging strategy that survives reorgs
Glossary terms touched
Tag · Unattributed spend · Tag coverage · Minimum viable tag set