# Tokenomics at scale: Track and cap Amazon Bedrock spend per IAM principal with CUR 2.0 and AWS Budgets

> Source: <https://aws.amazon.com/blogs/machine-learning/tokenomics-at-scale-track-and-cap-amazon-bedrock-spend-per-iam-principal-with-cur-2-0-and-aws-budgets/>
> Published: 2026-11-12 19:29:55+00:00

## [Artificial Intelligence](https://aws.amazon.com/blogs/machine-learning/)

# Tokenomics at scale: Track and cap Amazon Bedrock spend per IAM principal with CUR 2.0 and AWS Budgets

Generative AI spend is hard to attribute. Before an organization expands AI access, CFOs and CTOs usually want three answers: what do we spend per person or per team, can we cap it, and how much work does it take to get there.

In our previous post, [Tokenomics at Scale: How Jamf Built Real-Time Spend Enforcement for Amazon Bedrock](https://aws.amazon.com/blogs/machine-learning/tokenomics-at-scale-how-jamf-built-real-time-spend-enforcement-for-amazon-bedrock/), we explained a custom enforcement loop built on [AWS IAM Identity Center](https://aws.amazon.com/iam/identity-center/), [Amazon Athena](https://aws.amazon.com/athena/) over [Amazon Bedrock](https://aws.amazon.com/bedrock/) [model invocation logs](https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html), and [AWS Lambda](https://aws.amazon.com/lambda/). This approach gives near-real-time control, but it requires code you then own and operate.

This post shows how to get per-identity Amazon Bedrock cost visibility and enforceable spend limits using only AWS-native features, with no application code to write for the common case. The approach relies on three things: [IAM principal attribution](https://docs.aws.amazon.com/bedrock/latest/userguide/cost-mgmt-iam-principal-tracking.html), which Amazon Bedrock records automatically; [AWS Cost and Usage Reports](https://docs.aws.amazon.com/cur/latest/userguide/table-dictionary-cur2.html) (CUR) 2.0 or [AWS Cost Explorer](https://aws.amazon.com/aws-cost-management/aws-cost-explorer/) for reporting; and [AWS Budgets](https://aws.amazon.com/aws-cost-management/aws-budgets/) with budget actions for enforcement. By the end, you will have per-identity cost reporting and an automatic guardrail that restricts Amazon Bedrock access when a threshold is crossed.

## Solution overview

The solution has three parts: attribute, report, and enforce.

**Attribute.** Amazon Bedrock captures the AWS Identity and Access Management (IAM) principal identity, the [IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) or [role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html), for every inference request. This happens automatically, with no change to your Amazon Bedrock model inference calls. If several people share one role, you add per-user detail with session tags passed at role assumption time. If each person or application has its own role, principal tags on the role are enough.

**Report.** Activate those tags as cost allocation tags, then read the data either in Cost Explorer for a console-only view, or in a CUR 2.0 data export for detailed line-item analysis and SQL queries.

**Enforce.** Create an AWS Budget scoped to Amazon Bedrock and filtered to one identity or tag value, then attach a [budget action](https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-controls.html) that applies a Deny IAM policy when the threshold is crossed.

The following sections show each part in more detail, alongside the implementation steps.

## Prerequisites

To deploy this solution, you need:

- An [AWS account](https://aws.amazon.com/account/) with Amazon Bedrock model access enabled.
- Permissions to tag [IAM principal](https://docs.aws.amazon.com/bedrock/latest/userguide/cost-mgmt-iam-principal-tracking.html#cost-mgmt-iam-principal-tracking-principal-types) , activate[cost allocation tags](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html) ,[create data exports](https://docs.aws.amazon.com/cur/latest/userguide/dataexports-create.html) , and create budgets and budget actions.
- Amazon Bedrock accessed through IAM roles or IAM users.
- [AWS Command Line Interface](https://aws.amazon.com/cli/) (AWS CLI) configured with appropriate credentials.
- A CUR 2.0 data export and an [Amazon Simple Storage Service](https://aws.amazon.com/s3/) (*Amazon S3* ) bucket to receive it.*(optional, for detailed line-item analysis)* .
- Amazon Athena. *(optional, for querying the export)* .

[AWS Billing Console](https://aws.amazon.com/aws-cost-management/aws-billing/) access is managed separately from most IAM permissions. Confirm you have it before starting.

## Walkthrough

Before configuring anything, check what you already have. Amazon Bedrock records caller identity for every inference request automatically. Open Cost Explorer, filter Service to Amazon Bedrock, and group by the available identity or tag dimension. For accounts where each application or person already uses a distinct IAM role, this may be all the visibility you need.

To configure additional visibility:

- Step 1 gives you identity-level visibility.
- Steps 2 through 4 add tag dimensions and detailed reporting.
- Step 5 adds enforcement.

### Step 1: Decide how identity maps to your users

How you tag depends on how your IAM principals are organized.

**One role per person or per application.** Attach principal tags directly to each IAM role or user. These are static and apply to every request from that principal. Use the IAM console, or the AWS CLI:

**A shared role used by several people, or a gateway.** Principal tags cannot distinguish users on a shared role, because every session carries the same tags. Use session tags instead, passed when the role is assumed. Session tags are dynamic and can vary per session. If a session tag and a principal tag share a key, the session tag wins for that session.

The role’s trust policy must allow `sts:TagSession`. Session tags can be passed three ways:

- `sts:AssumeRole` with`--tags` , for a gateway assuming a role per user or tenant.
- `AssumeRoleWithWebIdentity` , with tags in the https://aws.amazon.com/tags claim of the OpenID Connect (OIDC) ID token (this is a claim name, not a URL you browse to).
- `AssumeRoleWithSAML` , by mapping`PrincipalTag:*` attributes in the identity provider’s Security Assertion Markup Language (SAML) assertion.

If your organization already captures a claim like `saml:sub` for other purposes, that alone does not make it available here. Your identity provider must separately map that attribute to a `PrincipalTag:*` SAML attribute statement, and the role’s trust policy must allow `sts:TagSession`, before it flows through as a session tag. The two integrations draw from the same SAML assertion but are configured independently. Common tag keys are user, team, Department, CostCenter, and Environment. You can apply up to 50 principal or session tags per IAM user or role.

### Step 2: Activate cost allocation tags

Tags do not appear in billing data until you activate them.

1. Open the [AWS Billing and Cost Management Console](https://aws.amazon.com/aws-cost-management/billing-and-cost-management-console-home/) .
2. Choose **Cost allocation tags** .
3. Filter by type **IAM principal** .
4. Select your tag keys and choose **Activate** .

In an [AWS Organization](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_introduction.html), activate IAM principal cost allocation tags in the [management account](https://docs.aws.amazon.com/managedservices/latest/userguide/management-account.html) (payer). Member accounts cannot activate them independently.

Two things to expect:

- A tag key only appears in this list after the principal has made at least one Amazon Bedrock call, so make a test call first if the list looks empty.
- It might take up to 24 hours for a newly applied tag key to appear for activation, then up to another 24 hours after you activate it before it begins populating billing data.

Activation does not apply retroactively by default, so activate tags before you need the data.

### Step 3: Create a CUR 2.0 data export that includes caller identity

Skip this step if Cost Explorer is enough for your reporting.

1. In the Billing and Cost Management console, choose **Data Exports** .
2. Choose **Create** and create a CUR 2.0 (Standard) export.
3. Under **Additional export content** , select**Include caller identity (IAM principal) allocation data** .
4. Under **Time granularity** , select Hourly.
5. Under **Report data integration** , select**Amazon Athena** . This sets the file format to Parquet and, importantly, delivers a setup script to your S3 bucket alongside the export data.
6. Choose the destination Amazon S3 bucket.
7. Run the delivered setup script (or the Glue crawler/DDL it defines) once. This is what actually creates the Glue Data Catalog table you’ll query in Step 4. The preceding console flow does not create it for you automatically.

If you already have a CUR 2.0 export created before enabling identity attribution, it will not include identity data retroactively. Create a new export with the caller identity option selected. In the export, principal and session tags appear as columns prefixed `iamPrincipal/`, and resource tags as `resourceTags/`. For example, a tag key team appears as `iamPrincipal/team`. AWS publishes a ready-made query set for this exact pattern in [aws-samples/sample-cur-iam-principal-bedrock-tracking](https://github.com/aws-samples/sample-cur-iam-principal-bedrock-tracking).

### Step 4: Read the data

The following diagram compares the two ways to read the data: a console-only path through Cost Explorer, and a SQL path over the CUR 2.0 export with Amazon Athena.

**Console path, no custom code.** Cost Explorer, filtered to Amazon Bedrock and grouped by your tag key, gives per-team or per-user spend as a chart and table. This covers most recurring reporting needs and requires no query authoring.

**SQL path, low code.** Point Amazon Athena at the CUR 2.0 export to group spend by `iamPrincipal/team` and usage type:

Things to note:

- **Service code** : Amazon Bedrock spend can be recorded under more than one`product_servicecode` value, for example,`AmazonBedrock` and`AmazonBedrockService` .
- **Table name** : The table name (`cur2_view` ) is whatever the setup script from Step 3 named it when it created the Glue table. Check the script or your Glue catalog if it differs. Column names match the CUR 2.0 Parquet schema regardless of table name.
- **Billing-period column.** Confirm how your export exposes the period. Some exports use`billing_period` (YYYY-MM). Others partition on a start-date or year/month column.

Two notes worth carrying into any query you write.

- **Count all token types** : Amazon Bedrock bills four token types, each at a different rate: input, output, cache read, and cache write. Each is a separate line item with its own usage type and unit price, so summing only input and output undercounts. The gap is largest for workloads that use prompt caching heavily. Refer to the AWS document[Understanding Amazon Bedrock pricing in CUR](https://docs.aws.amazon.com/bedrock/latest/userguide/cost-mgmt-understanding-cur-data.html#cost-mgmt-cur-pricing) for the latest information.
- **Match the rate to the line item, not the model** : Amazon Bedrock pricing in CUR is determined by three things: the token type, the service tier, and whether the request was routed through cross-Region inference. In-Region and cross-Region requests are priced differently, and service tier is reflected in the usage type as well. The usage type carries a suffix indicating cross-Region routing and service tier, so if your workloads mix these, match the rate to each line item rather than applying one rate per model. Refer to the[CUR usage type examples](https://docs.aws.amazon.com/bedrock/latest/userguide/cost-mgmt-understanding-cur-data.html#cost-mgmt-cur-usage-type-examples) for examples.

### Step 5: Add enforcement with AWS Budgets and budget actions

AWS Budgets performs the attach, so there is no custom code for this workflow. Budget acti
