Module 6: Finops For Tokens
8. Project: Andes Cargo's *cost gate*, extended to tokens
Description
This project brings together everything lessons 1 through 7 built — bedrock-budget.rego, the calculator with the budget integrated as a step, the AI workload's complete tagging, the proof that the gate cuts off before apply, the numeric On-Demand vs. Provisioned Throughput criterion — and confirms, with real act runs, that finops-and-cost-guardrails-guide's three inherited jobs — cost-estimate, cost-check, cost-tags — correctly evaluate the new Terraform and token budget, running in parallel with Module 5's security gate, never merging with it. No new job. No new lane. This module's complete thesis, put to the final test.
Connection to the module
The same guided-audit exercise every module-closing project in this ecosystem practices: not "did you write a policy and extend a script?", but "can you prove, with executed evidence, that the inherited gate evaluates those new pieces exactly as it evaluates any other?" GENAI-COST-PROFILE.md (Module 2) has a row that assigns this module the responsibility of turning its volume assumption into an executable gate — this project is the proof that row was fulfilled.
Step 1 — The project's state, before running anything
andes-cargo-infra/ with everything from this module integrated:
find . -maxdepth 2 -type f -newer THREAT-MODEL.md -not -path "./.terraform/*" | sort
What to expect (literal — the files this module added or modified):
./.github/workflows/ci.yml
./.github/workflows/token-budget-demo.yml
./cost-policy/bedrock-budget.rego
./locals.tf
./bedrock.tf
./scripts/bedrock_cost_estimate.py
./scripts/test_bedrock_cost_estimate.py
Seven files, five of them already inherited and only touched (ci.yml, locals.tf, bedrock.tf, bedrock_cost_estimate.py, test_bedrock_cost_estimate.py), two genuinely new (cost-policy/bedrock-budget.rego, token-budget-demo.yml). THREAT-MODEL.md, policy/, and any Module 5 security gate file don't appear on this list — this module never touched them.
Step 2 — terraform plan, the complete project with this guide's tagging included
terraform validate -no-color
terraform plan -input=false -no-color -out=tfplan
What to expect (literal — executed to write this lesson):
Success! The configuration is valid.
Plan: 17 to add, 0 to change, 0 to destroy.
Seventeen, the same number Module 5 already confirmed — this module never added or removed any resource, only tags on two that already existed (lesson 5).
Step 3 — conftest, cost-policy/'s complete library
terraform show -json tfplan > tfplan.json
conftest test tfplan.json -p cost-policy/
What to expect (literal — executed to write this lesson):
3 tests, 3 passed, 0 warnings, 0 failures, 0 exceptions
This module's lesson 5's three tests, all passing against the complete project: required-cost-tags.rego's rule (inherited, now clean thanks to lesson 5's complete tagging) and bedrock-budget.rego's two (new in this module). Also confirm, one last time, that policy/ (security) is still completely intact:
comm -12 <(ls policy/ | sort) <(ls cost-policy/ | sort)
What to expect (literal — empty output):
Zero shared files, the same guarantee from lesson 3, verified one last time with the project in its final state.
Step 4 — act pull_request -j cost-tags: green, real, independent
act pull_request -e .github/act-events/pr-event.json -j cost-tags
What to expect (literal — executed to write this lesson, with real Docker and the catthehacker/ubuntu:act-latest image):
[ci/cost-tags] ⭐ Run Set up job
[ci/cost-tags] ✅ Success - Set up job
[ci/cost-tags] ⭐ Run Main Check out andes-cargo-infra
[ci/cost-tags] ✅ Success - Main Check out andes-cargo-infra [10.474493750s]
[ci/cost-tags] ⭐ Run Main Set up Terraform
[ci/cost-tags] ✅ Success - Main Set up Terraform [2.382368s]
[ci/cost-tags] ⭐ Run Main Terraform init
[ci/cost-tags] ✅ Success - Main Terraform init [12.872904625s]
[ci/cost-tags] ⭐ Run Main Terraform plan
[ci/cost-tags] | Plan: 17 to add, 0 to change, 0 to destroy.
[ci/cost-tags] ✅ Success - Main Terraform plan [4.653669500s]
[ci/cost-tags] ⭐ Run Main Convert plan to JSON
[ci/cost-tags] ✅ Success - Main Convert plan to JSON [2.078965667s]
[ci/cost-tags] ⭐ Run Main Install conftest
[ci/cost-tags] ✅ Success - Main Install conftest [1.807026542s]
[ci/cost-tags] ⭐ Run Main Cost tag policy check (conftest -p cost-policy/)
[ci/cost-tags] | 3 tests, 3 passed, 0 warnings, 0 failures, 0 exceptions
[ci/cost-tags] ✅ Success - Main Cost tag policy check (conftest -p cost-policy/) [314.555458ms]
[ci/cost-tags] ⭐ Run Complete job
[ci/cost-tags] ✅ Success - Complete job
[ci/cost-tags] 🏁 Job succeeded
Job succeeded — inside an ephemeral Docker container, terraform init/plan/show -json from scratch, conftest downloaded from scratch, and Step 3's same 3 tests, 3 passed confirmed again. cost-tags never needed any Infracost token or any second checkout — the exact same reason this job is the simplest to verify end to end under local act, already established by finops-and-cost-guardrails-guide Module 4, lesson 7.
Step 5 — cost-check: the new piece, verified where it can actually run
cost-estimate/cost-check (inherited, finops-and-cost-guardrails-guide Module 3) have a real, already documented limitation, not new to this module: cost-estimate's second checkout (ref: ${{ github.event.pull_request.base.ref }}) needs a real Git remote, which local act, with no real GitHub repository behind it, can't satisfy (finops-and-cost-guardrails-guide Module 3, lesson 6). Since cost-check has needs: cost-estimate, running it in isolation with act pull_request -j cost-check also runs cost-estimate first, and fails at the same point:
act pull_request -e .github/act-events/pr-event.json -j cost-check
What to expect (literal — executed to write this lesson; the same limit, already documented, from finops-and-cost-guardrails-guide Module 3, lesson 6):
[ci/cost-estimate] | [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/main*:refs/remotes/origin/main* +refs/tags/main*:refs/tags/main*
[ci/cost-estimate] | The process '/usr/bin/git' failed with exit code 1
[ci/cost-estimate] ❌ Failure - Main Check out the base branch (before the change) [40.539506084s]
[ci/cost-estimate] 🏁 Job failed
Error: Job 'cost-estimate' failed
This failure isn't lesson 4's new step's fault — Enforce the token budget — nor anything this module built: it happens earlier, at the inherited second checkout, exactly the same exact point finops-and-cost-guardrails-guide already documented as a local act limitation without a real remote repository. This module's lesson 6 already resolved this with the exact same technique that sibling guide used: a dedicated test workflow, that isolates the new step's logic without needing the checkout that fails here:
act workflow_dispatch -j token-budget-check-demo -W .github/workflows/token-budget-demo.yml
What to expect (literal — the same result already confirmed in lesson 6):
[token-budget-demo/token-budget-check-demo] ⭐ Run Main Enforce the token budget (scripts/bedrock_cost_estimate.py)
[token-budget-demo/token-budget-check-demo] | TOTAL MONTHLY COST $0.42
[token-budget-demo/token-budget-check-demo] ✅ Success - Main Enforce the token budget (scripts/bedrock_cost_estimate.py) [150.438625ms]
[token-budget-demo/token-budget-check-demo] 🏁 Job succeeded
This piece's honest ledger, in a table:
| Piece | State in this writing environment |
|---|---|
ci.yml extended with the token budget step, complete YAML | Real — the complete file, correct, with no syntax errors |
cost-tags (Step 4) | Literal, real act pull_request -j cost-tags — Job succeeded, with no limitation |
cost-estimate's second checkout (explicit ref: to the base branch) | An inherited, already-documented local act limitation, with no real remote — works fine on real GitHub.com (finops-and-cost-guardrails-guide Module 3, lesson 6) |
The new step (Enforce the token budget) itself | Literal, tested twice with real evidence — directly (Module 6, lesson 4) and via token-budget-demo.yml (Module 6, lesson 6), PASS and FAIL |
infracost scan with a real token | Pending a real Infracost account — the exact same limit finops-and-cost-guardrails-guide Module 2/3 already documented, with no new change from this module |
Step 6 — In parallel with Module 5's security gate, never merged
ci.yml, at this module's close, has six jobs — the same six finops-and-cost-guardrails-guide already left, with the token step added inside one of them:
SECURITY LANE (Module 5) COST LANE (this module)
policy-check cost-estimate
│ │
▼ ▼
iac-scan cost-check
│ (+ the new token step,
▼ Module 6, lesson 4)
verify-artifact
cost-tags
(evaluates bedrock-budget.rego
automatically, with no
YAML change)
No needs:: crossing in EITHER direction between the two lanes
policy-check/iac-scan/verify-artifact (Module 5) don't mention cost-estimate/cost-check/cost-tags at all, and vice versa — the same two-independent-lanes architecture finops-and-cost-guardrails-guide Module 4, lesson 7 already established in real YAML, now extended with the token piece inside the lane that already existed, never as a new third lane. cost-tags (Step 4), the simplest job to verify end to end, already proves it: it runs completely isolated, with no needs: toward any security or cost job, Job succeeded in under thirty seconds.
How to defend this work in an interview
"Why didn't you build a CI job specific to budgeting tokens?" — the answer lives in this module's lesson 1: a well-designed cost gate generalizes to cost dimensions no one had in mind when it was built. cost-tags doesn't distinguish a Terraform resource's type — it evaluates any .rego file it finds in cost-policy/; cost-check is simply the right place for any "does this exceed what's acceptable?" check, regardless of which cost axis it's about. Building a new job would have been the signal the original gate was poorly designed — this project's proof is exactly the opposite.
"How do you know the new policy actually works, not just that it exists?" — the answer lives in lesson 3: a real, honest FAIL, against the project's real state (bedrock.tf still without the tag), followed by a real PASS in lesson 5, after fixing the HCL — never "it should work," always "it ran, and this is what happened."
"What happens if Infracost, like in Module 2, can't resolve an AI workload's cost?" — the answer lives in lesson 2: the exact technical reason (no Terraform resource represents an invocation), and the solution this module built specifically for that gap — a custom calculator, run as an independent step, never dependent on Infracost solving something it structurally can't.
The complete project, at a glance
andes-cargo-infra/
├── cost-policy/
│ ├── required-cost-tags.rego (inherited, finops M4 -- unchanged)
│ └── bedrock-budget.rego ← new in this module (L3)
├── policy/ (inherited, M5 -- no change at all)
├── locals.tf (M6.5: complete CostCenter/Owner +
│ new ai_workload_tags)
├── bedrock.tf (M6.5: tags = local.ai_workload_tags
│ on 2 of its resources)
├── scripts/
│ ├── bedrock_cost_estimate.py (M2.7, extended L4: --monthly-budget)
│ ├── test_bedrock_cost_estimate.py (M2.7, +2 tests L4 -- 14/14)
│ └── check-cost-threshold.sh (inherited, finops M3 -- unchanged)
├── GENAI-COST-PROFILE.md (M2.8 -- referenced, not permanently
│ edited; L6 cites it as a
│ proposed scenario)
└── .github/workflows/
├── ci.yml
│ ├── policy-check (inherited, M5 -- unchanged)
│ ├── iac-scan (inherited, M5 -- unchanged)
│ ├── verify-artifact (inherited, M5 -- unchanged)
│ ├── cost-estimate (inherited, finops M3 -- unchanged)
│ ├── cost-check (finops M3 + 1 new step, L4)
│ └── cost-tags (inherited, finops M4 -- evaluates
│ bedrock-budget.rego automatically)
└── token-budget-demo.yml ← new in this module (L6)
Common mistakes
Adding a fourth lane "just in case" before confirming the three existing cost jobs are enough (anticipating a need that doesn't exist). What happens: someone, while preparing this project, writes a new job in ci.yml "to be safe." How to spot it: if your ci.yml, by the time you reach this project, has more than six jobs. How to fix it: this lesson's Steps 3 through 5 are the proof — really run, not assumed — that the three inherited cost jobs, with exactly one new step, are enough.
Confusing the second checkout's limitation (Step 5) with a mistake in this module, and trying to "fix" it by rewriting cost-estimate (a wrong diagnosis). What happens: someone sees Job failed in cost-estimate and concludes this module broke something. How to spot it: if your instinct upon seeing that FAIL is to modify cost-estimate's YAML. How to fix it: this lesson's Step 5 precisely documents it — it's the same, already-known limitation finops-and-cost-guardrails-guide Module 3, lesson 6 documented before this module existed. It isn't a new problem, and it shouldn't be "fixed" by touching a job no project in this guide modifies.
Presenting only Step 4's result (cost-tags, green) and omitting Step 5's honest ledger (over-polishing the result). What happens: someone, preparing this project as a portfolio piece, shows only the successful run and avoids mentioning cost-estimate's limitation. How to spot it: if your presentation of this project doesn't mention anywhere why act pull_request without -j wouldn't run all six jobs end to end in this environment. How to fix it: Step 5's table exists exactly for this — the same execution-honesty discipline every capstone in this ecosystem requires, never hiding a real limit behind a more favorable partial result.
Exercises
Exercise 1 — Run act pull_request -j cost-tags yourself, and predict the exact number of tests before running it, by counting the rules across cost-policy/'s two files. Check your prediction against the real result.
See solution
Three: one rule in required-cost-tags.rego (inherited) plus two in bedrock-budget.rego (Module 6, lesson 3) — the same count this lesson's Step 3 already confirmed with the real run (3 tests, 3 passed).
Exercise 2 — Explain why cost-check (with the new token step) and cost-tags (with bedrock-budget.rego) are in different jobs, even though both check something related to the AI workload. Revisit the "one responsibility, one job" criterion from Module 6, lesson 4, Exercise 3.
See solution
cost-check answers "is this change going to cost more than acceptable?" (applied to two axes: infrastructure via Infracost, and token volume via the calculator). cost-tags answers a completely different question: "can this spend be attributed to someone?" Even though both questions end up applying, in part, to the same AI workload, they're questions of a different nature — one about magnitude, the other about allocation — the same distinction that already separated cost-check from cost-tags since finops-and-cost-guardrails-guide designed them as independent jobs, with no crossed needs:, in its Module 4.
Exercise 3 — Design, in prose, the act pull_request run you'd expect if someone deliberately introduced lesson 3's mistake (Workload missing) into bedrock.tf before opening this PR. Which job would fail, and would Module 5's security gate be affected?
See solution
cost-tags would fail — specifically at the Cost tag policy check (conftest -p cost-policy/) step, with the same FAIL message this module's lesson 3 already showed (missing required cost allocation tag "Workload"), ending in 🏁 Job failed. Module 5's security gate (policy-check/iac-scan/verify-artifact) would be completely unaffected: with no crossed needs: between the two lanes, those three jobs would keep running, and probably end green, exactly in parallel — the same independence between lanes this lesson's Step 6 already diagrammed, and the exact reason a Pull Request can fail on cost without that obscuring, or delaying, the same change's security verification.
Summary and next step
This project confirmed, with real act and conftest runs, this module's complete thesis: cost-policy/, extended with bedrock-budget.rego, passes clean (3 tests, 3 passed) against the complete project; cost-tags runs green end to end under act pull_request (Job succeeded); the new token budget step, tested twice with real evidence (directly and via token-budget-demo.yml), passes at $0.42 and fails at $8.40; and the complete cost lane keeps running in parallel with Module 5's security gate, with no crossed needs:, exactly as this guide's Module 1 promised. The one real limit — cost-estimate's second checkout under local act — is an inherited limitation, already documented by finops-and-cost-guardrails-guide before this module existed, not a new finding or a flaw in this work.
Before closing this module you should be able to: run act pull_request -j cost-tags against this project without looking at any previous lesson; explain, with executed evidence, why no new job was needed; and defend, against this lesson's three interview questions, why "extend," not "rebuild," was the right decision from start to finish.
With this, genai-on-aws-production-guide's Module 6 is complete: the budget for a usage-based token workload, verified with real Rego, a custom calculator integrated as a deterministic gate, complete allocation tagging (including an honest finding about CostCenter/Owner), proof in both directions that the gate cuts off before apply, the exact numeric criterion for when Provisioned Throughput would actually be the right decision, and the complete cost gate, green in its simplest-to-verify piece, in parallel with Module 5's security gate. Module 7 opens the next layer: sre-and-incident-response-guide's SLI/SLO vocabulary, applied for the first time to metrics specific to an AI workload.
Resources
- nektosact.com — User Guide — complete reference for
act, including running an isolated job with-j. finops-and-cost-guardrails-guide, Module 4, lesson 7 (07-hands-on-wiring-cost-tags-into-ci-yml.md) — the exact origin ofcost-tags, the job this project extends with no YAML changes.finops-and-cost-guardrails-guide, Module 3, lesson 6 — the documented origin of the secondcheckout's limitation, cited again in this lesson's Step 5.- This module, lessons 3 through 6 — the origin of every piece this project integrates into the gate.