Tag enforcement with policy as code: stopping untagged spend
A tagging policy nobody enforces is a spreadsheet, not a control. Policy as code rejects untagged resources before they deploy, so allocation stays near 100 percent instead of drifting. Here is how to build it.
Quick answer
Enforce tags where resources are created, not after. In CI, run a policy check (OPA/Conftest, Sentinel, or a custom script over the Terraform plan JSON) that fails the pull request when a required tag like owner, cost-center, or environment is missing or malformed. Combine that gate with a scheduled sweep of the live account for pre-policy stragglers. Teams that enforce at deploy time typically move allocation coverage from 60 to 70 percent up to 95 percent or higher within a quarter, which is the difference between a bill you can attribute and one you cannot. The rule is simple: no required tag, no merge.
Almost every organization has a tagging standard written down somewhere. Far fewer have tags actually present on the resources that drive the bill. The gap between the policy and reality is where cost allocation breaks: if 30 percent of spend has no owner tag, then 30 percent of your bill cannot be attributed to a team, and no amount of dashboard work fixes that. The cure is enforcement at the point of creation, and the cleanest way to enforce is policy as code in the deploy pipeline.
Why a written policy is not enough
A tagging convention that relies on people remembering it degrades constantly. New engineers do not know it, copied modules omit it, and one-off console changes skip it entirely. Every gap is permanent until someone hunts it down. Manual audits catch some, but they run monthly at best and the untagged resource has already been billing for weeks. The only way coverage stays high is if the untagged resource never gets created, which means the check has to run before deploy, the same discipline described in the tagging strategy guide.
Enforcing in the pipeline
| Tool | How it enforces |
|---|---|
| OPA / Conftest | Rego policy over Terraform plan JSON, fails CI |
| Sentinel | Policy on Terraform Cloud runs, blocks apply |
| Custom script | Parse plan JSON, exit non-zero on missing tags |
| Provider default_tags | Applies baseline tags automatically |
The mechanics are straightforward. Run terraform plan -out plan.bin, convert it withterraform show -json, and evaluate the resource changes against a policy that lists required tag keys and their allowed values. A resource missing owner, cost-center, orenvironment fails the check and the pull request cannot merge. For AWS, the providerdefault_tags block applies a baseline automatically so shared tags never depend on memory, and the policy check enforces the resource-specific ones on top.
Validating values, not just presence
Presence is not enough. A tag of cost-center = "tbd" or owner = "me" passes a presence check and allocates to nothing. Good policy validates values against an allow-list: cost-center must match a real code, environment must be one of prod, staging, or dev, and owner must be a recognized team handle. This catches the lazy fill-in that otherwise poisons your allocation with a garbage bucket of unattributable spend. Keep the allow-lists in the same repo as the policy so they version alongside it.
Cleaning up the pre-policy backlog
Enforcement stops new untagged resources, but it does nothing about the ones already running. Pair the gate with a scheduled sweep: a script that queries the billing data or the cloud APIs for resources missing required tags, groups them by account, and files tickets to the likely owners. Work the backlog down over a few weeks. Once the deploy gate is live, the backlog only shrinks, because nothing new joins it. This is the same closed loop that moves a team up the FinOps maturity model: prevent at the source, remediate the legacy, and measure coverage as a KPI.
Measuring the result
Track allocation coverage: the share of spend carrying every required tag. Report it weekly. A team that starts at 65 percent and enforces in CI usually reaches 95 percent within a quarter, and the remaining 5 percent is untaggable resources (some shared services and account-level charges) that get split by a rule instead. High coverage is what makes showback and chargeback credible, because a team can no longer dispute a number that traces cleanly to its own tagged resources. Price tagged infrastructure against the resource catalog so cost and owner are both known before deploy.
FAQ
How do I enforce tagging on cloud resources?
Enforce at deploy time with policy as code. In CI, evaluate the Terraform plan JSON against a policy (OPA/Conftest, Sentinel, or a custom script) that fails the pull request when a required tag like owner, cost-center, or environment is missing or has a disallowed value. Combine that gate with the provider's default_tags for baseline tags and a scheduled sweep to clean up resources created before the policy existed.
Why do written tagging policies fail?
Because they rely on people remembering them. New engineers do not know the convention, copied modules omit it, and console changes skip it. Every gap is permanent until someone finds it, and manual audits run too infrequently to keep up. Coverage only stays high when the untagged resource never gets created, which requires an automated check before deploy rather than a document people are supposed to follow.
Should I validate tag values or just presence?
Validate values. A tag of cost-center equals tbd or owner equals me passes a presence check but allocates to nothing, poisoning your data with unattributable spend. Good policy checks values against allow-lists: cost-center must match a real code, environment must be prod, staging, or dev, and owner must be a recognized team. Keep the allow-lists versioned in the same repo as the policy.
How do I clean up existing untagged resources?
Run a scheduled sweep that queries billing data or cloud APIs for resources missing required tags, groups them by account, and files tickets to likely owners. Work the backlog down over a few weeks. Once a deploy-time gate is live, nothing new joins the backlog, so it only shrinks. This closed loop, prevent at the source plus remediate the legacy, is what drives allocation coverage toward 95 percent and higher.
How does C3X help with tag enforcement?
C3X reads Terraform in the pull request and prices the resources against a live catalog, so cost visibility arrives at the same point where a tag policy check runs. Enforcing required tags and pricing the change together in CI means a resource merges only when it is both attributable and understood, turning the deploy gate into a single control for cost accountability rather than two disconnected checks.
What to do next
Make every resource attributable and priced before it merges. C3X reads your Terraform and prices your resources against a live catalog. Start with the quickstart.
Share this post
Try C3X on your own Terraform
Free and open source. No API key required. One command to install, one command to estimate.