Skip to content
yisusvii Blog
Go back

FinOps for Centralized AWS Bedrock: Tracking and Allocating AI Costs Across Multiple Products and AWS Accounts

Suggest Changes

Table of Contents

Open Table of Contents

Why Centralized Bedrock Needs FinOps by Design

Amazon Bedrock makes enterprise AI easier to adopt because teams can access multiple foundation models through a managed AWS service. That same simplicity creates a FinOps problem. Once many products, teams, environments, and AWS accounts start sending inference traffic to Bedrock, the monthly bill can become difficult to explain.

Traditional cloud chargeback is usually based on resources: EC2 instances, RDS databases, S3 buckets, EKS clusters, or Lambda functions. Bedrock cost behaves differently. The primary cost drivers are model choice, input tokens, output tokens, image or embedding requests, provisioned throughput, prompt size, retries, evaluation traffic, and user behavior.

Centralized Bedrock platforms need cost allocation before usage scales. Without it, every product team sees AI as free until finance receives the bill.

The goal is simple: every Bedrock request should be attributable to a product, account, environment, feature, team, and business outcome.


The Centralized Bedrock Pattern

Many enterprises start with decentralized Bedrock access. Each application account invokes Bedrock directly. That works for experiments, but it becomes hard to govern once usage grows.

A centralized pattern introduces a shared AI platform layer:

Product Account A       Product Account B       Product Account C
       |                       |                       |
       v                       v                       v
Cross-account IAM role / Private API / VPC endpoint strategy
       |                       |                       |
       +-----------+-----------+-----------+-----------+
                   |
                   v
            Central AI Platform Account
                   |
                   +--> API Gateway or ALB
                   +--> AI Gateway service
                   +--> model routing policy
                   +--> Bedrock Guardrails
                   +--> Bedrock Runtime
                   +--> invocation logs
                   +--> CloudWatch metrics
                   +--> S3 cost telemetry lake
                   |
                   v
        FinOps Account / Management Account
                   |
                   +--> Cost and Usage Report
                   +--> Athena allocation queries
                   +--> QuickSight dashboards
                   +--> Budgets and anomaly detection

The central account does not remove product accountability. It creates one enforcement and telemetry point where product identity must be attached before any model call happens.


Cost Dimensions That Matter for Bedrock

AWS Cost and Usage Reports can show Bedrock spend, but they do not automatically know your product, feature, tenant, or business workflow. Teams need an internal allocation model that maps technical usage to business ownership.

Recommended dimensions:

DimensionExampleWhy It Matters
Productclaims-platformPrimary chargeback owner
Featuredocument-extractionShows which AI feature drives cost
Environmentprod, staging, devPrevents test traffic from hiding inside production spend
AWS account123456789012Maps usage to cloud ownership boundaries
Teaminsurance-automationSupports showback and accountability
Tenant or customer tierenterprise, internal, free-trialEnables margin analysis
Model IDanthropic.claude-3-5-sonnet...Main cost and latency driver
Prompt versionclaims-extract-v7Connects prompt changes to cost changes
Request typechat, embedding, rerank, agent-tool-callSeparates workload economics

The most important design rule: do not depend only on AWS account as the allocation unit. Multiple products often share one application account, and one product may span multiple accounts.


Build an Internal AI Gateway

The AI gateway is the best place to enforce FinOps metadata because every request passes through it before reaching Bedrock. It can validate ownership fields, select the right model, apply guardrails, log token usage, and emit allocation metrics.

from dataclasses import dataclass
from typing import Literal


@dataclass(frozen=True)
class BedrockCostContext:
    product: str
    feature: str
    environment: Literal["dev", "staging", "prod"]
    team: str
    aws_account_id: str
    prompt_version: str
    tenant_tier: str


def validate_cost_context(context: BedrockCostContext) -> None:
    required_values = [
        context.product,
        context.feature,
        context.environment,
        context.team,
        context.aws_account_id,
        context.prompt_version,
        context.tenant_tier,
    ]

    if any(not value.strip() for value in required_values):
        raise ValueError("Bedrock request missing required FinOps context")

This metadata should be required at the API boundary. If a product cannot identify itself, the platform should reject the request before it creates cost.


Bedrock Invocation Wrapper with Allocation Telemetry

A minimal Bedrock wrapper should record request metadata, model ID, latency, token counts when available, cache status, and success or failure. The exact token fields vary by API response and model, so production code should handle missing usage metadata gracefully.

import json
import time
import uuid

import boto3

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
cloudwatch = boto3.client("cloudwatch", region_name="us-east-1")


def invoke_bedrock_with_finops(context, model_id: str, user_prompt: str) -> str:
    validate_cost_context(context)

    request_id = str(uuid.uuid4())
    started = time.perf_counter()

    response = bedrock.converse(
        modelId=model_id,
        messages=[
            {
                "role": "user",
                "content": [{"text": user_prompt}],
            }
        ],
        inferenceConfig={"maxTokens": 700, "temperature": 0.1},
    )

    latency_ms = int((time.perf_counter() - started) * 1000)
    usage = response.get("usage", {})
    input_tokens = usage.get("inputTokens", 0)
    output_tokens = usage.get("outputTokens", 0)

    cloudwatch.put_metric_data(
        Namespace="CentralBedrock",
        MetricData=[
            {
                "MetricName": "InputTokens",
                "Value": input_tokens,
                "Unit": "Count",
                "Dimensions": allocation_dimensions(context, model_id),
            },
            {
                "MetricName": "OutputTokens",
                "Value": output_tokens,
                "Unit": "Count",
                "Dimensions": allocation_dimensions(context, model_id),
            },
            {
                "MetricName": "Latency",
                "Value": latency_ms,
                "Unit": "Milliseconds",
                "Dimensions": allocation_dimensions(context, model_id),
            },
        ],
    )

    log_event = {
        "request_id": request_id,
        "product": context.product,
        "feature": context.feature,
        "environment": context.environment,
        "team": context.team,
        "source_account": context.aws_account_id,
        "prompt_version": context.prompt_version,
        "tenant_tier": context.tenant_tier,
        "model_id": model_id,
        "input_tokens": input_tokens,
        "output_tokens": output_tokens,
        "latency_ms": latency_ms,
    }
    print(json.dumps(log_event))

    return response["output"]["message"]["content"][0]["text"]


def allocation_dimensions(context, model_id: str) -> list[dict[str, str]]:
    return [
        {"Name": "Product", "Value": context.product},
        {"Name": "Feature", "Value": context.feature},
        {"Name": "Environment", "Value": context.environment},
        {"Name": "Team", "Value": context.team},
        {"Name": "ModelId", "Value": model_id},
    ]

CloudWatch metrics are useful for operational dashboards and alerts. For detailed allocation, the same log event should be delivered to S3 through CloudWatch Logs subscription filters, Firehose, or application logging pipelines.


Use AWS Cost and Usage Reports for the Financial Source of Truth

Application telemetry explains who used Bedrock and why. AWS Cost and Usage Reports explain what AWS billed. A mature FinOps setup needs both.

Recommended setup:

CUR gives the finance-grade number. Gateway telemetry gives the product allocation context.


Example Athena Allocation Query

The exact CUR table and column names depend on the organization’s CUR configuration. This pattern shows the allocation approach: calculate Bedrock cost from CUR, calculate product token share from gateway logs, then allocate shared Bedrock spend proportionally.

WITH bedrock_daily_cost AS (
    SELECT
        date(line_item_usage_start_date) AS usage_date,
        line_item_usage_account_id AS payer_or_platform_account,
        product_region AS region,
        line_item_usage_type AS usage_type,
        SUM(line_item_unblended_cost) AS bedrock_cost
    FROM cur_database.cur_table
    WHERE product_product_name = 'Amazon Bedrock'
    GROUP BY 1, 2, 3, 4
),
product_daily_usage AS (
    SELECT
        date(from_iso8601_timestamp(event_time)) AS usage_date,
        product,
        feature,
        environment,
        model_id,
        SUM(input_tokens + output_tokens) AS total_tokens
    FROM ai_platform_logs.bedrock_invocations
    GROUP BY 1, 2, 3, 4, 5
),
daily_token_totals AS (
    SELECT
        usage_date,
        SUM(total_tokens) AS all_tokens
    FROM product_daily_usage
    GROUP BY 1
)
SELECT
    u.usage_date,
    u.product,
    u.feature,
    u.environment,
    u.model_id,
    u.total_tokens,
    SUM(c.bedrock_cost) * (u.total_tokens / t.all_tokens) AS allocated_cost
FROM product_daily_usage u
JOIN daily_token_totals t
    ON u.usage_date = t.usage_date
JOIN bedrock_daily_cost c
    ON u.usage_date = c.usage_date
GROUP BY 1, 2, 3, 4, 5, 6, t.all_tokens
ORDER BY allocated_cost DESC;

This is a starting allocation model, not a perfect one. Different Bedrock models have different token prices, and image, embedding, agent, batch, and provisioned throughput workloads may need separate allocation formulas. The key is to make the model explicit, reviewable, and consistent.


Account Strategy for Multi-Product AI Platforms

There are three common account models.

Model 1: Direct Bedrock Access Per Product Account

Each product account invokes Bedrock directly.

This is simple and aligns AWS cost with product accounts, but governance is fragmented. Every account must implement logging, guardrails, IAM controls, prompt policies, and budgets consistently.

Best for early pilots or highly autonomous product teams.

Model 2: Central Bedrock Platform Account

Applications call a central AI platform service, and that service invokes Bedrock.

This improves governance, model routing, prompt control, and telemetry. The tradeoff is that CUR may show most Bedrock spend in the platform account, so product allocation must come from gateway logs.

Best for enterprises with shared platform engineering and strong governance requirements.

Model 3: Hybrid Access

High-volume or latency-sensitive workloads invoke Bedrock directly from product accounts, while standard chat, RAG, and agent workloads use the central gateway.

This balances autonomy and control, but it requires clear policy boundaries. The platform must define which workloads are allowed to bypass the central gateway and what telemetry they must emit.

Best for mature organizations with advanced FinOps and platform controls.


IAM and Governance Controls

FinOps is not only reporting. It is also prevention. Centralized Bedrock platforms should use IAM policies, service control policies, and permission boundaries to keep usage inside approved paths.

Example IAM condition pattern for requiring calls through an approved role or account boundary:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowApprovedBedrockModels",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-*",
        "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-*"
      ]
    }
  ]
}

Governance should answer these questions:

The policy should be strict enough to prevent surprise spend, but flexible enough to avoid forcing teams into shadow AI tooling.


Budgeting and Anomaly Detection

Bedrock usage can grow quickly when a feature becomes popular or a prompt change increases output tokens. FinOps controls should detect both legitimate adoption and accidental waste.

Recommended alerts:

AWS Budgets and Cost Anomaly Detection are useful at the account and service level. Product-level alerting usually requires gateway metrics because AWS billing data does not know the internal product dimension.


Chargeback and Showback Model

Start with showback before chargeback. Product teams need visibility and trust in the allocation model before finance turns it into internal billing.

Good dashboard sections:

Chargeback should happen only after the platform can explain allocation formulas, known limitations, and exception handling.


Common Failure Modes

Missing Product Metadata

If metadata is optional, it will be missing. Make product, feature, team, environment, and prompt version required fields in the AI gateway contract.

Overusing Large Models

Large models should be reserved for tasks that need them. Classification, summarization, routing, and extraction should be evaluated against smaller models first.

Ignoring Prompt Version Cost

A prompt change can double output length. Track prompt versions like application releases and compare cost before and after rollout.

Treating Tokens as the Only Unit

Tokens are important, but not enough. The better metric is cost per successful business task: approved claim, resolved ticket, completed extraction, answered support question, or prevented incident.

Forgetting Non-Production Spend

Development and staging environments can create meaningful AI cost through load tests, evaluations, retries, and experiments. Budget them explicitly.


Reference Architecture Checklist


Summary

Centralized Bedrock is powerful because it gives enterprises one place to manage model access, governance, prompt policies, and observability. It also shifts AI cost into a shared platform account unless FinOps metadata is designed into every request.

The strongest pattern is a central AI gateway that requires product context, logs token usage, emits CloudWatch metrics, exports detailed invocation records to S3, and reconciles those records with AWS Cost and Usage Reports. That creates a practical allocation model across products and accounts.

FinOps for Bedrock is not monthly reporting after the bill arrives. It is runtime architecture: every model call carries ownership, every prompt version can be measured, and every product team can see the cost of the AI features they ship.


Suggest Changes
Share this post on:

Next Post
AWS AI Technologies Teams Should Start Implementing Based on Cost and Performance