Module 6: Finops For Tokens

2. Why a token is the ecosystem's most extreme *usage-based* resource

Description

finops-and-cost-guardrails-guide already taught the central distinction between two ways of billing: PROVISIONED (you reserve capacity ahead of time, you pay whether you use it or not) and PAY_PER_REQUEST (you pay for what actually happens, no upfront reservation) — applied, in that guide, to Shipments, Andes Cargo's DynamoDB table. This lesson picks up exactly that distinction, and takes a step no previous guide needed to take: in PAY_PER_REQUEST, there's still a mode declared in the HCL (billing_mode = "PAY_PER_REQUEST"), a real attribute Terraform knows how to read. With Bedrock on-demand, there isn't even that — no attribute in bedrock.tf represents, even remotely, "one invocation to the model." This is the exact point where this ecosystem's usage-based spectrum ends.

Connection to the module

Lesson 1 previewed this idea in its final section, without developing it. This lesson develops it with complete technical precision, verified against the same terraform providers schema this guide's Module 3 already used to extract aws_bedrock_guardrail's real schema. Lesson 3, right after, writes the first code that enforces the one control point that does exist for a token workload: the cost-allocation tag, not a capacity limit.


Analogy: the taxi meter that doesn't even know how many trips you'll take

A taxi with a meter is already usage-based — you pay for the trip you took, not for having the car reserved all day. But the meter, within a trip, knows something: it knows you're on a trip, it knows when it started, it knows the distance covered in real time, and it can show you a number that climbs as you go. Now imagine a more extreme transportation system: there's no visible meter at any point, there's no counter you can check during the trip, and there isn't even a record of "how many trips you're going to take this month" until, at the end of the month, a single bill arrives with the total. That's Bedrock on-demand, compared to DynamoDB's PAY_PER_REQUEST: DynamoDB, at least, declares in its configuration that it "charges per request" — the HCL has a billing_mode that says so. Bedrock doesn't even have that mode declared anywhere in bedrock.tf, because the invocation isn't an attribute of a resource — it's an event that happens entirely outside what Terraform manages.


The complete spectrum, from "on = costs money" to "not even a declared mode"

   LESS USAGE-BASED                                             MORE USAGE-BASED

   Reserved EC2            DynamoDB              DynamoDB           Bedrock
   (PROVISIONED,            PROVISIONED           PAY_PER_REQUEST    on-demand
    always)                 (read/write_          (billing_mode
                             capacity in HCL)       in HCL, volume
                                                     in usage file)

   read_capacity=5          read_capacity=5      billing_mode =     (no attribute
   in the HCL --            in the HCL --        "PAY_PER_REQUEST"  in bedrock.tf
   represents               represents           -- the HCL         represents an
   CAPACITY, a number        CAPACITY, a number   declares the      invocation --
   that exists before        that exists before   MODE, but no      the invocation
   any invocation            any invocation       attribute         is NOT A RESOURCE,
                                                    declares HOW      it's an EVENT
                                                    MANY requests     at runtime
                                                    will occur
        │                       │                      │                  │
        ▼                       ▼                      ▼                  ▼
   Infracost reads the      Infracost reads the   Infracost needs    Infracost HAS
   number directly           number directly       infracost-usage   NO FIELD AT ALL
   from the HCL              from the HCL          .yml (Module 2,   TO READ -- the
                                                     lesson 6 of      resource doesn't
                                                     finops)           exist

Notice the progression: in the first two cases (PROVISIONED), the number that determines cost is an attribute of the resource itselfread_capacity = 5 is, literally, a line of HCL Infracost can read without needing anything else. In PAY_PER_REQUEST, the mode is still an attribute of the resource (billing_mode = "PAY_PER_REQUEST"), but the volume no longer lives in the HCL — it lives in infracost-usage.yml, a separate file someone has to write by hand, with data no plan can infer (finops-and-cost-guardrails-guide, Module 2, lesson 6). Bedrock on-demand takes the last step: there isn't even a declared mode, because there's no Terraform resource whose purpose is to represent "a call to InvokeModel." The invocation happens entirely outside the control plane Terraform manages — it lives in the application code (handler.py, the boto3.client("bedrock-runtime").invoke_model(...) that no lesson in this guide ever actually executes), not in any infrastructure declaration.


Real verification: which Bedrock resources exist, and which represent capacity versus invocation?

It's worth confirming this with evidence, not just prose explanation — exactly the same discipline this guide's Module 3, lesson 2 already applied when extracting aws_bedrock_guardrail's complete schema from terraform providers schema -json. Run against the same hashicorp/aws provider the rest of this guide uses:

terraform providers schema -json | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
for provider, pdata in data['provider_schemas'].items():
    if 'aws' not in provider:
        continue
    for name in pdata.get('resource_schemas', {}):
        if 'bedrock' in name.lower():
            print(name)
"

What to expect (literal — executed in this same environment, hashicorp/aws v6.x provider):

aws_bedrock_guardrail
aws_bedrock_guardrail_version
aws_bedrock_provisioned_model_throughput

Three resources, none of which represents "an invocation." aws_bedrock_guardrail/aws_bedrock_guardrail_version are the ones this guide's Modules 3 and 4 already declared — a guardrail's configuration, not a call. aws_bedrock_provisioned_model_throughput is the most interesting one for this lesson: it does exist, and it does represent a capacity commitment — Bedrock's equivalent of DynamoDB PROVISIONED's read_capacity/write_capacity. Its real schema, confirmed with the same command:

terraform providers schema -json | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
for provider, pdata in data['provider_schemas'].items():
    if 'aws' not in provider:
        continue
    block = pdata['resource_schemas']['aws_bedrock_provisioned_model_throughput']['block']
    for name, attr in block.get('attributes', {}).items():
        print(name, '| required:', attr.get('required', False))
"

What to expect (literal):

commitment_duration | required: False
id | required: False
model_arn | required: True
model_units | required: True
provisioned_model_arn | required: False
provisioned_model_name | required: True
region | required: False
tags | required: False
tags_all | required: False

model_units is exactly the conceptual equivalent of read_capacity — a number, declared in the HCL, representing how much capacity you reserve, regardless of how many real invocations you make with it. This confirms this lesson's precise distinction: Provisioned Throughput does have a Terraform resource, because reserving capacity is a declarative act (you decide, ahead of time, how many Model Units to buy) — but on-demand, the option this guide uses by default (Module 2, lesson 2), has no equivalent resource, because there's nothing to reserve ahead of time. This guide's Module 6, lesson 7 revisits aws_bedrock_provisioned_model_throughput with real numbers applied to Andes Cargo.


Why this matters specifically to the inherited cost gate

cost-tags (inherited, finops-and-cost-guardrails-guide's Module 4) evaluates a tfplan.json's resource_changes — its unit of work is always a Terraform resource. Any Rego policy in cost-policy/, including the one this module's lesson 3 writes, can only evaluate what exists in that JSON: resources, their type, their change.after. Since no Bedrock invocation ever appears in that JSON — because no invocation is a resource — no Rego policy, no matter how well written, can limit how many times extract-shipment-manifest-fields calls the model. bedrock-budget.rego (lesson 3) does the only thing it can from that angle: verify that the resource that does exist (aws_bedrock_guardrail) is correctly tagged for cost allocation. The volume budget itself — how many invocations per month — needs a completely different mechanism, outside tfplan.json: lesson 4's calculator, run as an independent pipeline step, not one more Rego policy.

   WHAT cost-policy/ CAN SEE                  WHAT cost-policy/ CAN NEVER SEE

   aws_bedrock_guardrail                      A call to InvokeModel
   (exists in tfplan.json,                    (doesn't exist in tfplan.json --
    has tags, has an ARN)                     doesn't exist in ANY plan,
        │                                     because it isn't a resource)
        ▼                                          │
   bedrock-budget.rego CAN                          ▼
   require Workload=GenAIExtraction           bedrock_cost_estimate.py
   on this resource (lesson 3)                (Python, not Rego) is the
                                               only mechanism that can
                                               reason about a volume
                                               assumption (lesson 4)

Common mistakes

Looking for an "expected volume" attribute in bedrock.tf, expecting it to exist the way billing_mode does in DynamoDB (an expectation mistake). What happens: someone, familiar with PAY_PER_REQUEST, opens bedrock.tf looking for something similar to a declared mode. How to spot it: if your search through this guide's HCL for any field related to "volume" or "requests" in bedrock.tf finds nothing. How to fix it: you won't find it, and this lesson explains why — unlike PAY_PER_REQUEST, which at least declares the mode, Bedrock on-demand has no equivalent attribute on the resource, because the invocation is never a Terraform resource at all.

Confusing aws_bedrock_provisioned_model_throughput with "the resource Infracost could use to calculate the on-demand cost" (reading the name without the schema). What happens: someone, seeing that a resource named "Bedrock ... Throughput" does exist, assumes it solves the problem of estimating on-demand cost. How to spot it: if you expect declaring this resource to give you an on-demand per-invocation cost figure. How to fix it: aws_bedrock_provisioned_model_throughput represents Provisioned Throughput — the pricing model opposite to the on-demand one this guide uses by default (Module 2, lesson 2). Declaring it means buying reserved capacity, not measuring on-demand volume; the two pricing models are mutually exclusive for the same model at the same time.

Concluding that, because cost-policy/ can't limit invocation volume, this module's cost gate "is useless" (over-generalizing). What happens: someone, upon understanding this lesson's limit, dismisses the value of bedrock-budget.rego and the integrated calculator. How to spot it: if your conclusion is "so this module can't prevent anything real." How to fix it: the opposite — precisely because cost-policy/ can't solve this alone, this module builds a second, complementary mechanism, outside Rego: lesson 4's calculator, run as a pipeline step with a declared threshold. Lessons 4 and 6 demonstrate, with executed evidence, that this mechanism does cut off before apply — a different type of gate from Rego's, not a weak gate.


Exercises

Exercise 1 — Place, on this lesson's spectrum, where a hypothetical "Bedrock Reserved Capacity Contract" AWS might announce tomorrow, with a fixed monthly price regardless of usage, would fall. Would it look more like DynamoDB PROVISIONED's read_capacity, or like billing_mode = "PAY_PER_REQUEST"?

See solution

It would look like read_capacity — a fixed number, declared ahead of time in the HCL, independent of real usage. In fact, that hypothetical product already exists today: it's exactly what aws_bedrock_provisioned_model_throughput and its model_units attribute represent, confirmed in this very lesson. Provisioned Throughput falls at the "least usage-based" end of the spectrum — the cost depends on how much capacity you reserved, not on how many times you used it, the same property that makes DynamoDB PROVISIONED's read_capacity predictable from day one.

Exercise 2 — Explain why cost-tags (inherited) can evaluate aws_bedrock_guardrail (with lesson 3's policy) but could never evaluate "how many times the model was invoked this month," no matter how many new Rego policies get written. Use this lesson's "what cost-policy/ can/can't see" diagram to answer precisely.

See solution

Because cost-tags runs conftest test tfplan.json -p cost-policy/ — its only source of information is tfplan.json, a file Terraform generates from declared resources, never from events that happen at runtime. aws_bedrock_guardrail exists in that file because it's a resource declared in bedrock.tf; a real InvokeModel call never appears there, because it happens after apply, inside the application code, entirely outside the control plane Terraform manages. No amount of new Rego policies can change this — the limit isn't the policy's, it's the input file itself that Rego evaluates.

Exercise 3 — If Andes Cargo decided, in the future, to buy Provisioned Throughput for extract-shipment-manifest-fields, would it make sense for bedrock-budget.rego (lesson 3) to also require Workload=GenAIExtraction on aws_bedrock_provisioned_model_throughput? Justify your answer with what you already know about that policy's scope.

See solution

Yes, it would make sense, and in fact it would be a natural extension: aws_bedrock_provisioned_model_throughput is a real resource in tfplan.json, with a real tags attribute (confirmed in this lesson's schema) — exactly the same kind of surface aws_bedrock_guardrail already has, and that bedrock-budget.rego already evaluates. If Andes Cargo migrated to Provisioned Throughput, extending ai_workload_resource_types (the set lesson 3 declares) to also include aws_bedrock_provisioned_model_throughput would be the same kind of incremental change this guide's Module 5, lesson 3, Exercise 3 already practiced with bedrock-least-privilege.rego — adding a resource type to the existing set, without rewriting the policy's logic.


Summary and next step

This lesson developed, with evidence verified live against terraform providers schema, the exact difference between three points on the usage-based spectrum: PROVISIONED (capacity declared in the HCL), PAY_PER_REQUEST (mode declared, volume outside the HCL), and Bedrock on-demand (no attribute declared at all, because the invocation is never a resource). You confirmed that Bedrock does have a resource that represents capacity (aws_bedrock_provisioned_model_throughput, with a required model_units) — but none that represents an individual invocation, the exact technical reason no tfplan.json-based gate can, on its own, limit an on-demand workload's volume.

Before moving on you should be able to: draw the complete spectrum of the four positions (EC2/DynamoDB PROVISIONED, PAY_PER_REQUEST, Bedrock on-demand) without help; name the three Bedrock resources that do exist in the AWS provider, and explain which one represents capacity; and explain why cost-tags can protect a guardrail but never an invocation volume.

Lesson 3 writes this module's first piece of code: bedrock-budget.rego, the policy that enforces the one thing cost-policy/ can see — the cost-allocation tag on the resource that does exist.

Resources

  1. Terraform Registry — aws_bedrock_provisioned_model_throughput — the resource that does represent reserved capacity, contrasted in this lesson with the complete absence of a resource for on-demand invocations.
  2. finops-and-cost-guardrails-guide, Module 1, lesson 5 — the origin of PAY_PER_REQUEST vs. PROVISIONED for DynamoDB, the distinction this lesson extends one step further.
  3. finops-and-cost-guardrails-guide, Module 2, lesson 6 — the origin of infracost-usage.yml, the mechanism that resolves volume for usage-based resources that do have a Terraform resource behind them.
  4. This course, Module 3, lesson 2 — the exact precedent for extracting a real schema with terraform providers schema -json, reapplied here.