Home Blog AWS Architecture Series #49 — The bill does not divide…
AWS Architecture AWS Architecture Series

AWS Architecture Series #49 — The bill does not divide

A finance team asks what each business unit spent. Tagging coverage is reported at 94%, and the answer still does not add up — because there are three different reasons a cost is unallocated, they need three different fixes, and only one of them is a tagging problem at all.

Verified against current vendor documentation on 11 September 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

The previous post was about what logging costs. This one is about the question that follows every cost conversation eventually: whose is it?

A platform team is asked to report spend by business unit. They have a tagging standard, it is enforced by an SCP on resource creation, and a dashboard says coverage is 94%. The report still does not reconcile, the gap is much larger than 6%, and the reflex is to push tagging coverage higher — which will not close it, because the gap is not one thing.

There are three distinct reasons a dollar is unallocated, and they are not variations of each other. One is fixable retroactively, one is permanently lost, and one was never a tagging problem to begin with. Telling them apart is most of the work.

1The tag existed but was not activated

A tag on a resource is not a cost allocation tag until it is activated in the Billing console, and AWS-generated and user-defined tags must each be activated separately. Teams routinely tag for months before anyone activates the key, and the cost data for those months carries no tag values.

This one is recoverable. A management account user can request a backfill of up to twelve months, which retroactively applies the current activation status across that window. It is the only one of the three that gives you history back.

Fix

Backfill, once. You may only submit one request every 24 hours, and not at all while one is running.

2The resource was never tagged

Backfill looks like it should fix this too, and it does not. AWS is unambiguous: “the resource tag must be historically assigned to the AWS resource for the backfilled cost data to be available.” Backfill changes an activation status retroactively; it cannot invent a value that was never on the resource.

AWS's own example makes the boundary concrete. A tag applied in June and activated in November, backfilled from January, produces tag values from June to December — and January to May stay empty, because the tag was not on the resource then.

Fix

None retroactively. Tag now so that this month is recoverable later, and report the historical gap as a gap.

3The cost has no single owner

The largest share of the gap is usually here, and no amount of tagging touches it. A NAT gateway serves eleven teams. A transit gateway serves the estate. Enterprise Support is one line for the organisation. AWS names the category precisely when describing what split charge rules are for: costs “that aren't directly attributed to a single owner”, such as “data transfer costs, enterprise support, and operating costs”.

A tag on the NAT gateway would not help. It has one owner in the tag and eleven in reality, and the honest answer is an allocation rule rather than a label.

Fix

Cost Categories with split charge rules — and read the reporting caveat below before building on them.

Architecture

The three causes map onto three different mechanisms, and the useful discipline is refusing to let one tool be the answer to all of them.

Diagram: three reasons an AWS cost is unallocated and the different mechanism each one needs. The first, a tag that existed on the resource but was never activated in the Billing console, is recoverable by requesting a backfill of up to twelve months which retroactively applies the current activation status. The second, a resource that was never tagged at the time, is not recoverable at all, because AWS states the resource tag must have been historically assigned for backfilled cost data to be available — backfill changes an activation status, not history. The third, a cost with no single owner such as a NAT gateway, a transit gateway or enterprise support, was never a tagging problem and needs split charge rules in Cost Categories, using a proportional, fixed or even allocation method. A panel records the reporting caveat: split charge results appear only on the cost categories details page and do not appear in or affect the Cost and Usage Report, Cost Explorer or other AWS Cost Management tools.
Three causes, three mechanisms, and only one of them gives history back. The third produces the number finance asked for and puts it somewhere the dashboards do not read.

The unallocated bucket is a feature, not a failure

One property makes an honest report possible at all: the cost allocation report includes both tagged and untagged resources, and at the end of the billing cycle its total reconciles with the Bills page. The residue is always visible and always adds up.

That is worth defending against the instinct to hide it. A report that shows 94% allocated and 6% unallocated is more useful to a finance team than one that quietly spreads the 6% around, because the first can be argued with. Show the bucket, name what is in it, and shrink it deliberately.

Containers are the case where tagging genuinely cannot reach

An EC2 instance running forty pods from six teams has one set of tags. Split cost allocation data exists for exactly this: it generates container-level costs by taking the amortized cost of the instance and the percentage of CPU and memory resources consumed by the containers that ran on it, and for EKS it creates aws:eks: tags — cluster name, namespace, node, deployment, workload name and type — enabled for cost allocation by default.

Two details decide whether the result is trustworthy. aws:eks:workload-type is populated only when exactly one workload manages the pod and it is a built-in type, and aws:eks:deployment only for ReplicaSet. Pods outside those shapes are still costed and still less attributable, which is a smaller version of the same unallocated bucket one layer down.

And it lands in the file you pay to store and query

Split cost allocation data adds two usage records per task or pod per hour. AWS's own worked example — 1,000 pods with a lifetime under an hour — is 48,000 new records a day, and 72,000 where accelerators are involved, because those add a third record. The previous post was about what it costs to ingest and keep data; this is a feature that materially enlarges the Cost and Usage Report you are ingesting and querying. Worth switching on, worth sizing first.

Why This Architecture Holds Up

Split charge rules are the right shape for shared cost

The model is small and sound. A source is the pool of shared cost, the targets are the values it is split across, and the allocation method is one of Proportional, weighting by each target's own cost, Fixed, by a percentage you set, or Even split. Ten rules per cost category, and a value used as a source cannot also be a target, which prevents circular allocation by construction.

There is a sequencing requirement that is easy to miss and breaks the result silently: cost category rules evaluate top-down, so AWS recommends putting the values holding shared costs at the top of the rule list. Categorise the shared platform before the business units, or the platform's costs get absorbed into whichever unit matched first and there is nothing left to split.

The caveat that decides where this can be used

AWS states it plainly: “Split charge rules and the total allocated costs are only presented on the cost categories details page. These costs do not appear and don't impact your AWS Cost and Usage Reports, Cost Explorer, and other AWS Cost Management tools.” The allocation you build is visible on one console page and in a CSV you download from it. Every dashboard, chargeback pipeline and QuickSight report reading CUR or Cost Explorer will show the unsplit figures.

What that means in practice

It does not make split charge rules useless — it makes them a reporting mechanism rather than a data-pipeline one. If the deliverable is a monthly shared-cost statement that a finance team reads and signs off, the details page and its CSV are sufficient and the rules are the shortest path to a defensible number.

If the deliverable is a live chargeback dashboard, they are not sufficient, and the allocation has to be re-implemented downstream against CUR — where you own the arithmetic and the method is whatever you write. Deciding which of those two you are building, before configuring anything, is the decision this whole section exists to force.

Key Architecture Decisions

Decision Choice Reasoning
Tags activated late Backfill, up to twelve months The only mechanism that returns history. One request per 24 hours, and none while another is running.
Resources never tagged Accept the gap and report it Backfill applies an activation status, not a tag value. AWS requires the tag to have been historically assigned.
Shared infrastructure Cost Categories with split charge rules A NAT gateway has one owner in a tag and many in reality. An allocation rule is the honest representation.
Rule ordering Shared-cost values at the top Rules evaluate top-down. Categorise the shared platform first or there is nothing left to split.
Where the number is consumed Decide before configuring Split charge results never reach CUR or Cost Explorer. A signed monthly statement is fine; a live dashboard is not.
Containers Split cost allocation data, sized first It is the only way to attribute shared compute, and it adds tens of thousands of CUR records a day.

Two operational details worth knowing before you promise a date

Nothing here is immediate. Tags can take up to 24 hours to appear in the Billing console, and a backfill updates Cost Explorer, Data Exports and CUR automatically but those refresh once every 24 hours, so the result is not visible the moment the request succeeds. A backfill requested on the morning of a reporting deadline will not make that deadline.

Access is narrower than most people expect. Only the management account — or a standalone account outside an organisation — can reach the cost allocation tags manager at all. A member-account team asked to improve their own allocation cannot activate a tag key themselves, and an allocation programme that assumes otherwise stalls on its first ticket.

Closing Thought

Cost allocation gets treated as a hygiene problem — tag everything, chase coverage, the number falls out. That framing survives contact with a real bill for about a week, because the largest unallocated block is usually shared infrastructure that no tag could ever have resolved, and the second largest is history that no tool can recover.

The more useful framing is the one the tooling actually implements: allocation is three separate problems wearing one name. Tagging coverage is a real metric for exactly one of them. Reporting it as though it measures all three is how a programme ends up at 94% coverage and a report finance will not sign.

And when the shared-cost number is finally produced, check where it lives before building on it. A figure that exists on one console page is a perfectly good answer to a monthly question and a poor foundation for a pipeline — which is a distinction worth making deliberately, rather than discovering when the dashboard disagrees with the statement.

Next in this series

Cost & Governance — the commitment layer: how Savings Plans and Reserved Instances are applied across an organisation, why the account that buys them is rarely the account that benefits, and what discount sharing does to the allocation model this post just built.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent
<();