From fe57c2fc70b891d92a826d19b1173f289031f0b9 Mon Sep 17 00:00:00 2001 From: stxkxs <139715017+stxkxs@users.noreply.github.com> Date: Sat, 8 Aug 2026 12:03:42 -0700 Subject: [PATCH] fix: cost-allocation tag activation is backfillable; the stamp is not MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four places in cost-pipeline state that activating a cost-allocation tag is irreversible — "It is not retroactive… There is no backfill", "nothing before it is ever attributed", "Spend inside that window is unattributable permanently". A doc-contract check enforced that wording, so the claim was not merely written down, it was held in place. It is wrong. AWS ships cost-allocation tag backfill: a management account can request up to twelve months, and it updates Cost Explorer, Data Exports and the CUR automatically on their next 24h refresh. One request at a time, one per 24 hours. The API is reachable today — `aws ce list-cost-allocation-tag-backfill-history` returns an empty request list rather than an error. What backfill genuinely cannot do is invent tag values. AWS states the constraint directly: "the resource tag must be historically assigned to the AWS Resource for the backfilled cost data to be available." Backfill applies the tags' current activation status across history; a resource that ran untagged stays unattributable no matter when the switch is flipped. So the advice these documents give is right and the reason they give is not, and the difference decides what you hurry. The switch is repairable at the cost of a backfill request. The stamp is the irreversible half, which is why this component tags its own buckets at install rather than waiting for the first tenant. ──────────────────────── the gate ──────────────────────── check-doc-contracts.sh required the README to contain the string "not retroactive" — a gate whose pass condition was a false statement, and which would have rejected this correction. It now requires the README to say which half is recoverable, and its explanation carries the distinction rather than restating the error. Proven red by removing every occurrence of the word from the README and watching it fail, then green on restore. The first attempt at that proof was itself broken: the placeholder still contained the substring the check greps for case-insensitively, so the gate passed and looked vacuous when the test was the thing at fault. --- scripts/check-doc-contracts.sh | 16 +++++++----- terraform/components/cost-pipeline/README.md | 26 ++++++++++++++------ terraform/components/cost-pipeline/main.tf | 18 ++++++++------ 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/scripts/check-doc-contracts.sh b/scripts/check-doc-contracts.sh index 1eb6530..2515394 100755 --- a/scripts/check-doc-contracts.sh +++ b/scripts/check-doc-contracts.sh @@ -140,8 +140,10 @@ fi # # cost-pipeline declares `check "the_cost_allocation_tags_are_active"`, which # warns on every plan while either tag key is inactive. The requirement it warns -# about — activation is payer-level, account-global, and NOT retroactive, so -# every hour before it is permanently NULL — was documented only in +# about — activation is payer-level and account-global, and while activating late +# can be repaired by a twelve-month backfill, a resource that ran untagged cannot +# be, because backfill applies an activation status and invents no tag values — +# was documented only in # bedrock-account's "Not here" section, a page whose whole job is to say what it # does not own. An operator who applied cost-pipeline and read its README learned # nothing about a warning they were about to see, or about a gap they would @@ -204,10 +206,12 @@ elif ! grep -qi 'PROVENANCE' terraform/components/cost-pipeline/cur-export-schem fail=1 fi -if ! grep -qi 'not retroactive' terraform/components/cost-pipeline/README.md; then - echo "cost-pipeline/README.md does not state that tag activation is not retroactive." - echo "That is the property that makes the cost of missing it unrecoverable: every hour" - echo "before activation is permanently NULL, and it reads as low spend rather than as a gap." +if ! grep -qi 'backfill' terraform/components/cost-pipeline/README.md; then + echo "cost-pipeline/README.md does not state which half of this is recoverable." + echo "Late activation is: a management account can backfill twelve months. A resource" + echo "that ran untagged is not, because backfill applies an activation status to history" + echo "and does not invent tag values. Saying only 'not retroactive' points the urgency at" + echo "the switch when it belongs to the stamp." fail=1 fi diff --git a/terraform/components/cost-pipeline/README.md b/terraform/components/cost-pipeline/README.md index 26f6529..eef96f4 100644 --- a/terraform/components/cost-pipeline/README.md +++ b/terraform/components/cost-pipeline/README.md @@ -93,7 +93,7 @@ Three things about that query are load-bearing: real consumption and hold a runaway tenant under its cap. The switch exists to stop consumption, so it counts consumption. -## Both tag keys must be ACTIVATED, and activation is not retroactive +## Both tag keys must be ACTIVATED, and the tag must already be on the resource The query above reads a cost-allocation tag key. It does not appear in the CUR at all until the key is **activated in Cost Explorer** — stamping the tag on a resource is not enough. This @@ -108,13 +108,23 @@ Two properties make the warning worth acting on the day you see it: - **Activation is payer-level and account-global.** It is not per-cluster, not per-environment, and not something a second apply of this component can fix. -- **It is not retroactive.** Every hour before activation is permanently NULL for that key. There is - no backfill. A tag activated a week after a tenant starts running produces a budget report that is - simply missing that week, and the gap is invisible in the query result — it reads as low spend. - -AWS can take up to 24 hours to *list* a newly observed key, so the key has to be stamped before it -can be activated, and activated before the numbers mean anything. The cost of missing this is paid a -month later, on a partial-month budget report nobody can explain. +- **Late activation is repairable; a missing resource tag is not.** A management account can request + a backfill of up to twelve months, which retroactively applies the tags' *current* activation + status and updates Cost Explorer, Data Exports and the CUR automatically — on their own 24h + refresh, so not the moment it succeeds. One request at a time, and one per 24 hours. + + What backfill cannot manufacture is a tag that was never there: *"the resource tag must be + historically assigned to the AWS Resource for the backfilled cost data to be available"*. Activate + in November and backfill to January, and a tag first applied in June yields values from June + onward and nothing before it. + + So the irreversible half is **stamping**, not activating. A tenant that runs untagged for a week + has a week that no backfill will ever recover, and the gap is invisible in the query result — it + reads as low spend. + +AWS can take up to 24 hours to *list* a newly observed key, and up to 24 hours more to activate it. +The key has to be stamped before it can be activated, and activated before the numbers mean +anything. Missing the activation costs a backfill request; missing the stamp costs the data. `bedrock-account` stamps `PlatformId` and is where the clock starts; see its "Not here" section. diff --git a/terraform/components/cost-pipeline/main.tf b/terraform/components/cost-pipeline/main.tf index f6b9c62..af6d3a3 100644 --- a/terraform/components/cost-pipeline/main.tf +++ b/terraform/components/cost-pipeline/main.tf @@ -772,8 +772,9 @@ resource "aws_glue_catalog_table" "estimates" { # `bedrock-runtime` spend requires invoking through a per-tenant *application # inference profile* whose tags flow to CUR, in place of the raw model or # cross-region profile ID. -# 2. `platformid` must be activated as a cost allocation tag in Billing. -# Activation is not retroactive, so nothing before it is ever attributed. +# 2. `platformid` must be activated as a cost allocation tag in Billing. Late +# activation is repairable by a backfill (twelve months, management account); +# spend from before the tag was on the resource is not. # # Until both land, `cur_truth_usd` is NULL for every row. That is reported as # `match_state = 'no_cur_row'` rather than as a NULL delta, because a @@ -1091,8 +1092,8 @@ resource "aws_ssm_parameter" "reconciliation_view" { } ################################################################################ -# PlatformId cost-allocation tag — account-global, not retroactive, and the reason -# the CUR leg of every budget can read zero while every query succeeds. +# PlatformId cost-allocation tag — account-global, and the reason the CUR leg of +# every budget can read zero while every query succeeds. # # Activating this key is what puts it into the report at all. CUR 2.0 carries one # `tags` column of type map holding every tag source at once, and a key @@ -1199,9 +1200,12 @@ check "the_cost_allocation_tags_are_active" { tenant arrives. For iamPrincipal/PlatformId the trigger is a call, not a resource: the key appears once a role carrying the tag has invoked Bedrock at least once. - Activation is NOT retroactive. Spend inside that window is unattributable permanently, - which is why this is loud on every plan rather than a silence you have to know to look - for. + Activating late is repairable: a management account can backfill up to twelve months, + and that updates Cost Explorer, Data Exports and the CUR on their next refresh. What no + backfill recovers is spend from before the tag was ON the resource — backfill applies an + activation status to history, it does not invent tag values. So the loud warning is about + the stamp, not the switch, and it stays loud because a missing stamp reads as low spend + rather than as an error. EOT } }