Cost Allocation and Chargeback¶
Splitting a shared cloud account across several cost centres, in proportion, and proving the result adds up to the bill.
The Problem¶
An account boundary is the strongest cost boundary a cloud gives you, and RosettaOps uses it: every user and every project gets its own account wherever that is practical, so most cost needs no splitting at all. See The Landing Zone.
Some accounts are legitimately shared anyway. A platform account, a data account, a team account that predates the organization. For those, the question is not who owns this account but how much of it does each cost centre owe, and the usual answers are both wrong:
- Charge it to one owner. Whoever is easiest to name absorbs the whole account. The number is defensible to nobody.
- Leave it unallocated. A report with a mystery bucket beside it invites the reader to distrust the parts that are correct.
RosettaOps splits the account instead.
Cost Centres on Logins¶
Cost centres are carried by the login, not by the resource and not by a tag. A login can belong to several organizations, and each organization can name a cost centre and a weight.
At billing time RosettaHub takes a snapshot of the organization tree, flattened to cost centres, org paths and per-login weighted allocations, and writes it into the FOCUS billing data alongside the cost rows.
Allocation is resolved when the cost is incurred
The snapshot is taken as the billing data is written, not joined on at query time. A cost row keeps what was true in the month it happened.
This is what stops last quarter's report changing when somebody moves team, an organization is renamed, or a cost centre is retired. Reports are reproducible: running the same period twice returns the same numbers.
One exception, and it is temporary. Months that predate the first snapshot of an estate have no recorded organization tree -- it was never captured. Those months are attributed using the earliest snapshot that exists, so they answer "what would this have cost under today's structure" rather than "what did it cost". Every month from then on carries its own snapshot, so the window is fixed and does not grow.
The Resolution Rule: Reduce, Then Split¶
A login can sit in several organizations at once, so its cost has to be divided between them. RosettaHub applies two steps, in order.
1. Reduce¶
Drop any organization that is implied by one of its own descendants. If a login is a member of both Eng and Eng/Platform, that is one membership stated twice, and only Eng/Platform survives.
Specificity is measured along an ancestor chain. It can establish that Eng/Platform is more specific than Eng, because one contains the other. It cannot establish anything about Eng/Platform versus Design/Research, because those are separate lineages.
2. Split¶
Divide the cost between whichever payers remain, equally unless weights say otherwise.
Why unrelated organizations are split rather than ranked
An earlier design compared organizations from different lineages by depth and broke ties alphabetically. That is not a neutral guess: it charges the entire cost to whichever organization happens to be named earliest in the alphabet. An equal split between genuine peers is the honest answer.
The split is between cost centres, not organizations
Three organizations that all resolve to the same two budget codes is a half each between those two codes, not a third each. The question being answered is who pays, and two of those organizations are the same payer.
Setting the Split¶
A cost centre is inherited¶
A cost centre is a billing code named on an organization. An organization that names none inherits the nearest one above it, so a code set on the root covers the whole institution and is only overridden where a branch names its own.
Giving the root a code is what makes the totals reconcile by construction: any login whose organizations name none would otherwise fall through to the unallocated cost centre.
Weights say how much, in whatever unit suits you¶
Without weights a login's cost divides equally between its payers. A weight overrides that, and the units are yours:
| What you enter | What it means |
|---|---|
60 / 40 |
percentages |
3 / 2 |
days a week |
0.6 / 0.4 |
fractions |
4 / 1 |
headcount, FTE, anything countable |
Weights are normalised per login before use, so all four rows above produce the same split. Storing the figure you actually entered keeps it meaningful to whoever set it -- 3 and 2 days still reads as days a year later, where 0.6 and 0.4 would not.
One warning, deliberately narrow
RosettaHub flags weights only when they look like fractions and still miss 1 -- 0.7 and 0.2 is a slip worth naming. Percentages, days and headcount all sum to something other than 1 by design and are normalised exactly, so warning about those would train you to ignore the message.
Splitting one cloud account¶
Weights on a login divide that person's cost. Where a whole cloud account is shared -- a project account two departments fund together -- the split can be stated on the account instead, and it takes precedence over the holder's own memberships.
This is the more direct statement of the two: it describes the thing actually incurring the cost, changes only when someone decides that account's money should divide differently, and is the figure a team can be asked to keep accurate.
When nothing resolves¶
Cost that reaches no cost centre is charged to a dedicated unallocated code rather than dropped. It stays in the totals and stays visible, so an estate that is partly configured shows exactly how much is still unattributed instead of quietly reporting a smaller bill.
Chargeback and Showback Are Different Queries¶
Both are provided, they answer different questions, and their totals are not the same number by design.
| Chargeback | Showback | |
|---|---|---|
| Question | What does each cost centre owe? | What is each part of the organization spending? |
| Method | Unnests the weighted allocations, so a login split across two cost centres contributes to both in proportion | Rolls each organization path up its own ancestry |
| Sums to the bill? | Yes | No, deliberately |
Showback figures do not add up to the bill, and should not
A parent organization contains its children, so their spend is counted inside it. An account that sits in two branches shows in full under each. That is what showback means.
If you place the two totals side by side in a report without saying so, a reader will assume one of them is broken. Label them.
Showback exists so that a team with no budget code of its own can still see its spend. Chargeback exists so that finance can invoice.
Reconciliation¶
Every allocation run reports three numbers:
| Figure | Meaning |
|---|---|
| Allocated | The total that landed on a cost centre |
| Total | The account total for the period |
| Difference | Anything that did not land anywhere |
A non-zero difference produces a worklist of the cost that has not been attributed, so it can be chased rather than rounded away.
Aim for a difference of zero
The point of the reconciliation is not the number itself, it is that the number is visible. A chargeback report that quietly leaves a percentage unallocated costs the FinOps team more credibility than the missing percentage is worth.
Amortised Cost, Not Billed Cost¶
Allocation is calculated on effective cost, the amortised figure, throughout.
- Billed cost puts an upfront commitment in the month it was purchased.
- Effective cost spreads it across the term it covers.
Amortised is the fairer basis for charging a team, because it stops a single commitment purchase landing as a spike on whichever cost centre happened to own that month. Using it consistently is also what makes the sum of the linked accounts equal the account total in every month, including a purchase month.
Reconciling against the provider invoice
Billed-cost figures are retained separately for exactly this purpose. Amortised figures will not match an invoice line for a given month, and are not meant to.
Credits are the exception: they stay on billed cost. There is no term over which to spread a credit, and they are reported as consumption rather than netted off, because the budget a credit draws down belongs to the institution rather than to whichever account happened to redeem it.
Provider Coverage¶
| Provider | Cost centre allocation |
|---|---|
| AWS | Available |
| Azure | Available. The same allocation is applied; only the plumbing underneath differs |
Allocation itself is provider-independent. The split is decided once from the organization tree, and both providers carry it into a FOCUS dataset alongside their cost rows -- the same three columns, holding the resolved cost centre, the organization paths and the weighted allocations.
What differs is how each provider identifies the thing being charged.
| AWS | Azure | |
|---|---|---|
| A charge is attributed to | the linked account | the resource group |
| Reading the split back | unnesting the allocations | OPENJSON over the allocations |
The first row is the one that matters when a figure looks wrong. An Azure charge is attributable only if its resource group is one RosettaHub knows about -- exactly as an AWS charge needs its account to be known. Cost matching neither is charged to the unallocated cost centre, where it stays visible rather than disappearing.
Related Topics¶
- Cost Management -- real-time tracking, budgets and enforcement
- The Landing Zone -- why most cost needs no splitting in the first place
- Organizations -- building the tree that allocation is derived from
- Projects -- project-level cost roll-up