← Agent Frameworks 🕐 13 min read
Agent Frameworks

CloudTrail Lake for Bedrock AI Workload Analysis (2026)

AWS CloudTrail Lake is a managed audit data lake that ingests CloudTrail events into Apache ORC columnar storage and exposes them through a SQL query interface — without the operational overhead of S3

AWS CloudTrail Lake is a managed audit data lake that ingests CloudTrail events into Apache ORC columnar storage and exposes them through a SQL query interface — without the operational overhead of S3 bucket partitioning, Glue crawlers, or custom ETL. For Bedrock workloads, it provides the only native path to retroactive, immutable, queryable records of every InvokeModel call: who called it, which model, from which identity, at what time. This matters for two distinct reasons: (1) FinOps teams that cannot explain per-team Bedrock spend without call-level attribution, and (2) compliance officers at financial services and healthcare organizations who need a tamper-evident, queryable audit trail meeting 7-year retention mandates.

Note on availability: As of June 1, 2026, CloudTrail Lake is available only to existing customers who enrolled before May 31, 2026. New accounts must use S3-delivered CloudTrail logs with Athena for equivalent analysis. Existing customers should evaluate lock-in carefully before expanding event data stores.


1. CloudTrail Lake vs. S3-Delivered CloudTrail Logs

Architecture comparison

Dimension S3 + Athena CloudTrail Lake
Storage format JSON (compressed, partitioned by account/region/date) Apache ORC columnar (managed)
Retention model S3 Lifecycle rules — your responsibility Built-in: 1-year (extendable to 10) or 7-year tiers
Query interface Athena SQL — requires Glue table, partition management Native SQL via console/API/SDK — no table setup
Partition pruning Manual: WHERE year=2025 AND month=06 AND day=15 against S3 prefix Automatic: time-range filters push down to ORC stripes
ETL requirement Partition projection or Glue crawler required None — events land query-ready
Cross-account aggregation Requires S3 bucket policy + per-trail config Organization-level event data store covers all member accounts in one query
Data integrity S3 bucket owner controls deletion Immutable by default; no delete API
Athena join capability Native — CUR and CloudTrail in same Athena workspace Via Lake query federation (Glue Data Catalog registration)
Maximum retention Unlimited (S3 object lifecycle) 10 years (1-year tier) or 7 years (7-year tier)

When S3 + Athena wins

S3-delivered logs remain superior for organizations that already have a mature data lake, want to join CloudTrail events to non-AWS log sources in a single Athena query, or have existing Glue/Lake Formation governance infrastructure. The per-GB query cost is identical ($5/TB scanned). The operational difference is setup complexity, not query capability.

When CloudTrail Lake wins

CloudTrail Lake is the faster path to production for multi-account Bedrock auditing: one event data store covers the entire AWS Organization, queries run against pre-indexed ORC columns rather than raw JSON, and there is no partition maintenance to perform. The immutability guarantee matters for compliance functions that need evidence records that cannot be modified after creation.


2. CloudTrail Lake Pricing for Bedrock Data Events

Two retention tiers

One-year extendable retention (recommended below 25 TB/month ingested)

Component Price
Ingestion — CloudTrail management, data, and network activity events $0.75/GB uncompressed
Ingestion — other AWS data sources (Config, non-AWS) $0.50/GB uncompressed
Retention beyond year 1 (up to 10 years) $0.023/GB/month
Query analysis $0.005/GB scanned

First year of retention is included in the ingestion price. ORC compression ratios are typically 8–12x over raw JSON, so 100 GB uncompressed Bedrock events costs $75 to ingest but occupies roughly 8–12 GB of managed storage.

Seven-year retention (recommended above 25 TB/month)

Component Price
Ingestion — first 5 TB/month $2.50/GB uncompressed
Ingestion — next tiers Volume tiering applies
Retention 7 years included, no extension beyond 7 years
Query analysis $0.005/GB scanned

Bedrock data event volume estimation

InvokeModel data events are compact: the request body (prompt content) and response body are not included in the CloudTrail record for security reasons — only metadata fields are captured. A typical InvokeModel event serializes to approximately 1–3 KB uncompressed. An organization running 1 million Bedrock calls per month generates roughly 1–3 GB of uncompressed event data, costing $0.75–$2.25/month to ingest under the 1-year tier. This is low enough that ingestion cost is rarely the budget concern; retention extension for 7-year compliance mandates is the cost driver.

7-year retention cost example

For a financial services team ingesting 5 GB/month of Bedrock events under the 1-year tier, extending to year 7 costs:

Year 1:  included in ingestion ($3.75/month at $0.75/GB × 5 GB)
Years 2–7: 5 GB/month × 12 months × $0.023/GB/month × 6 years
         = 5 × 12 × 0.023 × 6 = $8.28 additional storage cost over 6 years
         (this is cumulative, not monthly — volume grows with each month retained)

At steady state with 5 GB ingested monthly and 7 years retained, the total stored volume grows to ~420 GB. At $0.023/GB/month that is ~$9.66/month in extended retention costs — modest relative to the compliance value.


3. Configuring CloudTrail Lake to Capture Bedrock InvokeModel Data Events

Resource types available for Bedrock data events

CloudTrail requires explicit data event configuration — Bedrock runtime events are not captured by default. The relevant resource types are:

Resource type Events captured
AWS::Bedrock::Model InvokeModel, InvokeModelWithResponseStream
AWS::Bedrock::AgentAlias InvokeAgent
AWS::Bedrock::KnowledgeBase Retrieve, RetrieveAndGenerate
AWS::Bedrock::FlowAlias InvokeFlow
AWS::Bedrock::Prompt RenderPrompt
AWS::Bedrock::AsyncInvoke StartAsyncInvoke, GetAsyncInvoke

AWS CLI: create a Bedrock-focused event data store

aws cloudtrail create-event-data-store \
  --name "bedrock-ai-audit-store" \
  --retention-period 365 \
  --advanced-event-selectors '[
    {
      "Name": "CaptureBedrockInvokeModel",
      "FieldSelectors": [
        {
          "Field": "eventCategory",
          "Equals": ["Data"]
        },
        {
          "Field": "resources.type",
          "Equals": ["AWS::Bedrock::Model"]
        }
      ]
    },
    {
      "Name": "CaptureBedrockAgentInvocations",
      "FieldSelectors": [
        {
          "Field": "eventCategory",
          "Equals": ["Data"]
        },
        {
          "Field": "resources.type",
          "Equals": ["AWS::Bedrock::AgentAlias"]
        }
      ]
    },
    {
      "Name": "CaptureBedrockKnowledgeBases",
      "FieldSelectors": [
        {
          "Field": "eventCategory",
          "Equals": ["Data"]
        },
        {
          "Field": "resources.type",
          "Equals": ["AWS::Bedrock::KnowledgeBase"]
        }
      ]
    }
  ]' \
  --region us-east-1

Filtering to specific model ARNs (cost control)

To capture only Anthropic Claude calls and exclude other providers:

{
  "Name": "CaptureClaudeOnly",
  "FieldSelectors": [
    { "Field": "eventCategory",    "Equals": ["Data"] },
    { "Field": "resources.type",   "Equals": ["AWS::Bedrock::Model"] },
    { "Field": "resources.ARN",    "StartsWith": ["arn:aws:bedrock:us-east-1::foundation-model/anthropic."] }
  ]
}

To include all models but exclude test/sandbox accounts by IAM identity prefix:

{
  "Field": "userIdentity.arn",
  "NotStartsWith": ["arn:aws:iam::123456789012:role/sandbox-"]
}

Organization-level deployment

For multi-account coverage, deploy the event data store at the AWS Organizations management account:

aws cloudtrail create-event-data-store \
  --name "org-bedrock-audit-store" \
  --organization-enabled \
  --multi-region-enabled \
  --advanced-event-selectors '[...]'

This creates a single queryable store covering all member accounts — the most efficient pattern for enterprise attribution.


4. CloudTrail Lake SQL Query Interface: Key Fields for Bedrock Events

Core event schema fields

CloudTrail Lake exposes events with a flat-plus-nested schema. The primary fields relevant to Bedrock:

Field Type Content for InvokeModel
eventID string UUID — unique per API call
eventTime timestamp ISO 8601 UTC call timestamp
eventName string InvokeModel, InvokeModelWithResponseStream, Converse, ConverseStream
eventSource string bedrock-runtime.amazonaws.com
awsRegion string Region where model was invoked
sourceIPAddress string Caller IP (Lambda IPs for serverless callers)
userAgent string SDK version string — useful for identifying application sources
errorCode string Null on success; ThrottlingException, ValidationException on failure
errorMessage string Human-readable error detail
userIdentity.type string AssumedRole, IAMUser, AWSService
userIdentity.arn string Full caller ARN — key attribution field
userIdentity.accountId string AWS account ID of caller
userIdentity.sessionContext.sessionIssuer.arn string Role ARN when type=AssumedRole
userIdentity.sessionContext.webIdFederationData struct OIDC claims for federated identities
requestParameters map Model ID, request body metadata
responseElements map Typically null for InvokeModel (response is streaming)
resources array [{"type": "AWS::Bedrock::Model", "ARN": "arn:aws:bedrock:..."}]
additionalEventData map Token counts (see Section 5), bearer token flag
tlsDetails.tlsVersion string TLS negotiated version
requestID string X-Amzn-RequestId — correlates with application logs

Basic event query

SELECT
    eventTime,
    eventName,
    userIdentity.arn          AS caller_arn,
    userIdentity.accountId    AS caller_account,
    awsRegion,
    element_at(requestParameters, 'modelId') AS model_id,
    errorCode,
    requestID
FROM <event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
    AND eventName IN ('InvokeModel', 'InvokeModelWithResponseStream', 'Converse', 'ConverseStream')
    AND eventTime > '2026-01-01 00:00:00'
    AND eventTime < '2026-06-18 00:00:00'
ORDER BY eventTime DESC
LIMIT 1000

Replace <event-data-store-arn> with the ARN returned from create-event-data-store.


5. Token Count Extraction from additionalEventData

Where token counts live

For Bedrock InvokeModel and Converse calls, CloudTrail records token consumption in additionalEventData. The fields present depend on the model family:

additionalEventData.inputTokenCount   -- prompt tokens consumed
additionalEventData.outputTokenCount  -- completion tokens generated
additionalEventData.totalTokenCount   -- sum (not always present)

These fields are populated at the CloudTrail layer by the Bedrock service — they are independent of any application-level logging. This makes them the canonical retroactive source for token attribution even if the application itself never logged token counts.

Token attribution query by team/role

SELECT
    element_at(requestParameters, 'modelId')                       AS model_id,
    userIdentity.sessionContext.sessionIssuer.arn                  AS assumed_role_arn,
    userIdentity.accountId                                         AS aws_account,
    DATE_TRUNC('day', eventTime)                                   AS invocation_day,
    COUNT(*)                                                       AS call_count,
    SUM(CAST(element_at(additionalEventData, 'inputTokenCount')
             AS BIGINT))                                           AS total_input_tokens,
    SUM(CAST(element_at(additionalEventData, 'outputTokenCount')
             AS BIGINT))                                           AS total_output_tokens,
    SUM(CAST(element_at(additionalEventData, 'inputTokenCount')
             AS BIGINT) +
        CAST(element_at(additionalEventData, 'outputTokenCount')
             AS BIGINT))                                           AS total_tokens
FROM <event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
    AND eventName IN ('InvokeModel', 'InvokeModelWithResponseStream', 'Converse')
    AND eventTime > '2026-01-01 00:00:00'
    AND errorCode IS NULL
GROUP BY 1, 2, 3, 4
ORDER BY total_tokens DESC

Applying model pricing to token counts

Token counts from additionalEventData can be multiplied by the Bedrock on-demand pricing table to reconstruct cost per caller. This is the retroactive attribution path when Bedrock invoices show only aggregate account-level spend.

WITH token_usage AS (
    SELECT
        element_at(requestParameters, 'modelId')                   AS model_id,
        userIdentity.arn                                           AS caller_arn,
        userIdentity.accountId                                     AS account_id,
        SUM(CAST(element_at(additionalEventData, 'inputTokenCount')  AS BIGINT)) AS input_tokens,
        SUM(CAST(element_at(additionalEventData, 'outputTokenCount') AS BIGINT)) AS output_tokens
    FROM <event-data-store-arn>
    WHERE
        eventSource = 'bedrock-runtime.amazonaws.com'
        AND eventName IN ('InvokeModel', 'Converse')
        AND eventTime > '2026-05-01 00:00:00'
        AND eventTime < '2026-06-01 00:00:00'
        AND errorCode IS NULL
    GROUP BY 1, 2, 3
)
SELECT
    model_id,
    caller_arn,
    account_id,
    input_tokens,
    output_tokens,
    -- Approximate cost using Claude 3.5 Sonnet pricing as example
    -- Adjust price_per_1k_input and price_per_1k_output per model
    ROUND((input_tokens  / 1000.0) * 0.003, 4) AS estimated_input_cost_usd,
    ROUND((output_tokens / 1000.0) * 0.015, 4) AS estimated_output_cost_usd,
    ROUND((input_tokens  / 1000.0) * 0.003 +
          (output_tokens / 1000.0) * 0.015, 4) AS estimated_total_cost_usd
FROM token_usage
ORDER BY estimated_total_cost_usd DESC

Important caveat: Token counts in additionalEventData reflect what Bedrock billed, but the pricing multiplier must be maintained manually as AWS adjusts on-demand rates. The CUR join (Section 6) is more reliable for actual dollar reconciliation.


6. Joining CloudTrail Lake to CUR in Athena: Cross-Service Cost Attribution

Architecture

CUR data lives in S3 and is queryable via Athena. CloudTrail Lake events are queryable via native CloudTrail Lake SQL or via Athena federation. The join key is line_item_resource_id in CUR (which for Bedrock contains the model ARN or a request-level identifier) correlated against CloudTrail’s requestID or resources[].ARN.

Enabling CloudTrail Lake federation for Athena joins

aws cloudtrail enable-federation \
  --event-data-store <event-data-store-arn> \
  --federation-role-arn arn:aws:iam::123456789012:role/CloudTrailLakeFederationRole

This registers the event data store as a table in AWS Glue Data Catalog under database aws_cloudtrail_lake, making it queryable from Athena alongside CUR tables.

Cross-service join pattern

-- CUR table: cost_and_usage_report (partitioned by year/month in S3)
-- CloudTrail Lake federated table: aws_cloudtrail_lake.bedrock_events (via Athena federation)

WITH bedrock_calls AS (
    SELECT
        requestID,
        eventTime,
        userIdentity.arn                                              AS caller_arn,
        element_at(requestParameters, 'modelId')                     AS model_id,
        CAST(element_at(additionalEventData, 'inputTokenCount')
             AS BIGINT)                                              AS input_tokens,
        CAST(element_at(additionalEventData, 'outputTokenCount')
             AS BIGINT)                                              AS output_tokens
    FROM aws_cloudtrail_lake.bedrock_events
    WHERE
        eventSource = 'bedrock-runtime.amazonaws.com'
        AND eventName IN ('InvokeModel', 'Converse')
        AND eventTime > TIMESTAMP '2026-05-01'
        AND errorCode IS NULL
),
cur_bedrock AS (
    SELECT
        line_item_resource_id,
        line_item_usage_account_id,
        line_item_unblended_cost,
        line_item_usage_start_date,
        product_servicename
    FROM cost_and_usage_report
    WHERE
        product_servicename = 'Amazon Bedrock'
        AND line_item_line_item_type = 'Usage'
        AND year = '2026'
        AND month = '05'
)
SELECT
    bc.caller_arn,
    bc.model_id,
    DATE_TRUNC('day', bc.eventTime)          AS usage_day,
    COUNT(bc.requestID)                      AS api_calls,
    SUM(bc.input_tokens)                     AS input_tokens,
    SUM(bc.output_tokens)                    AS output_tokens,
    SUM(cur.line_item_unblended_cost)        AS actual_cost_usd
FROM bedrock_calls bc
LEFT JOIN cur_bedrock cur
    ON bc.requestID = cur.line_item_resource_id
GROUP BY 1, 2, 3
ORDER BY actual_cost_usd DESC NULLS LAST

Join key reliability note

The requestID to CUR line_item_resource_id join works when CUR is configured at request-level granularity. By default, CUR reports aggregate Bedrock charges without request IDs. To enable request-level CUR entries, activate Bedrock model invocation logging (separate from CloudTrail) with cost allocation tags — then use tags as the join key. Without request-level CUR, the practical join is at the account + model + day level:

GROUP BY
    bc.caller_account,
    bc.model_id,
    DATE_TRUNC('day', bc.eventTime)

Match this aggregate to CUR grouped at the same granularity. The result is an attribution estimate, not an exact reconciliation — sufficient for chargeback purposes but not for audit-grade financial reporting.


7. Compliance Use Cases: Who Invoked Which Model, When, From Where

Audit trail query for regulated industries

The canonical compliance query — “show me every person or service that invoked a foundation model between these dates” — runs directly against CloudTrail Lake:

SELECT
    eventTime,
    eventName,
    userIdentity.type                                              AS identity_type,
    userIdentity.arn                                               AS caller_arn,
    userIdentity.accountId                                         AS aws_account,
    userIdentity.sessionContext.sessionIssuer.arn                  AS session_role,
    userIdentity.sessionContext.attributes.mfaAuthenticated        AS mfa_authenticated,
    awsRegion                                                      AS invocation_region,
    sourceIPAddress                                                AS source_ip,
    userAgent,
    element_at(requestParameters, 'modelId')                       AS model_invoked,
    element_at(resources[1], 'ARN')                                AS model_arn,
    errorCode,
    requestID
FROM <event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
    AND eventName IN ('InvokeModel', 'InvokeModelWithResponseStream', 'Converse', 'ConverseStream')
    AND eventTime BETWEEN '2026-01-01 00:00:00' AND '2026-06-18 00:00:00'
ORDER BY eventTime DESC

This output satisfies common financial services regulatory requirements (SOX, FINRA, MAS TRM) and healthcare mandates (HIPAA audit controls) that require a queryable record of who accessed AI inference systems and when.

Cross-region invocation detection

Financial services firms in regulated jurisdictions (EU AI Act, MAS, FCA) may need to verify that model invocations did not cross data residency boundaries:

SELECT
    awsRegion,
    userIdentity.accountId,
    element_at(requestParameters, 'modelId') AS model_id,
    COUNT(*) AS call_count
FROM <event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
    AND eventName = 'InvokeModel'
    AND eventTime > '2026-01-01 00:00:00'
    AND awsRegion NOT IN ('us-east-1', 'us-west-2')  -- approved regions
GROUP BY 1, 2, 3
HAVING COUNT(*) > 0
ORDER BY call_count DESC

Failed invocation audit (access denials)

Denied calls that appear in CloudTrail are security-relevant events — an IAM policy may have been misconfigured, or an unauthorized identity attempted access:

SELECT
    eventTime,
    userIdentity.arn   AS caller_arn,
    errorCode,
    errorMessage,
    element_at(requestParameters, 'modelId') AS model_id,
    sourceIPAddress
FROM <event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
    AND errorCode IS NOT NULL
    AND errorCode IN ('AccessDeniedException', 'UnauthorizedException')
    AND eventTime > '2026-06-01 00:00:00'
ORDER BY eventTime DESC

8. CloudTrail Lake Insights: Anomaly Detection for Bedrock Call Patterns

What Insights covers

CloudTrail Insights analyzes management event activity against a rolling baseline and emits an Insights event when a statistically anomalous pattern is detected. As of November 2025, AWS extended Insights to data events, enabling detection of unusual Bedrock invocation volume.

Insights detects:

  • Unexpected call volume spikes (e.g., a role that normally calls InvokeModel 1,000 times/day suddenly calling 50,000 times/hour)
  • Unusual error rate increases (e.g., ThrottlingException rates jump 10x above baseline)
  • Anomalous API call rates from new source IPs or rarely-used roles

Enabling Insights for a CloudTrail Lake event data store

Insights for CloudTrail Lake is configured at the trail level, not the event data store level. Enable it on the trail feeding the event data store:

aws cloudtrail put-insight-selectors \
  --trail-name bedrock-audit-trail \
  --insight-selectors '[
    {"InsightType": "ApiCallRateInsight"},
    {"InsightType": "ApiErrorRateInsight"}
  ]'

Insights events are written to both the configured S3 bucket and optionally to a separate CloudTrail Lake event data store. To query Insights events:

SELECT
    eventTime,
    eventName,
    element_at(additionalEventData, 'insightType')       AS insight_type,
    element_at(additionalEventData, 'baselineAverage')   AS baseline_avg_calls_per_min,
    element_at(additionalEventData, 'insightAverage')    AS anomaly_avg_calls_per_min,
    element_at(additionalEventData, 'insightDuration')   AS duration_minutes
FROM <insights-event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
ORDER BY eventTime DESC

Practical anomaly thresholds for Bedrock

Bedrock usage patterns vary significantly by application type. Batch inference jobs produce high sustained volume; interactive agents produce spiky, low-latency calls. Insights uses statistical baselines automatically, but operators should:

  1. Establish a 2-week baseline before treating Insights events as alerts
  2. Tag IAM roles by use case (batch-inference, interactive-agent, test) so Insights baselines apply per use case rather than account-wide
  3. Forward Insights events to EventBridge for automated alerting to security teams

9. 7-Year Retention: Cost Implications vs. 1-Year Default

Regulatory drivers for 7-year retention

Regulation Retention requirement Applicability
SEC Rule 17a-4 6 years (3 immediately accessible) US broker-dealers
FINRA Rule 4370 3–6 years depending on record type US financial services
SOX Section 802 5–7 years for audit work papers US public companies
EU AI Act Article 12 Records “for the lifetime of the AI system, at least 10 years” for high-risk AI EU-deployed high-risk AI
HIPAA 45 CFR § 164.530 6 years US healthcare covered entities
MAS TRM 2021 No fixed term; “adequate period” typically 5–7 years Singapore financial services

The EU AI Act Article 12 requirement for high-risk AI systems establishes a de facto 10-year standard for EU-deployed Bedrock workloads used in consequential decisions (credit, hiring, medical). The CloudTrail Lake 1-year extendable tier can reach 10 years via retention extension; the 7-year tier caps at 7.

Cost comparison: 1-year extendable vs. 7-year tier at scale

Scenario: 100 GB/month of Bedrock event data ingested, full 7-year retention needed.

1-year extendable tier

Ingestion:    100 GB/month × $0.75/GB = $75/month
Year 1 retention: included
Years 2–7 retention: cumulative stored volume grows by 100 GB each month
  Month 13: 1,200 GB stored × $0.023/month = $27.60/month additional
  Month 84: 8,400 GB stored × $0.023/month = $193.20/month additional
  Average steady-state retention cost ≈ $100–$200/month (after year 3)
Total 7-year cost estimate: ~$6,300 ingestion + ~$4,200 retention ≈ $10,500

7-year tier

Ingestion:    100 GB/month × $2.50/GB = $250/month
7-year retention: included
Total 7-year ingestion cost: $250 × 84 months = $21,000

At 100 GB/month, the 1-year extendable tier is significantly cheaper (~$10,500 vs. ~$21,000 over 7 years) because Bedrock event data volumes are modest. The 7-year tier only breaks even at very high ingestion volumes (>25 TB/month) where the volume discounts on the 7-year tier outweigh the retention surcharge.

Decision rule for Bedrock-only deployments: Use the 1-year extendable tier and set retention to 84 months (7 years). The cost savings are substantial unless Bedrock is part of a broader CloudTrail Lake deployment ingesting many other event types that push monthly volume above 25 TB.


10. CloudTrail Lake Athena Federation vs. Native CloudTrail Lake Query

Two query paths

Native CloudTrail Lake SQL — queries submitted via cloudtrail:StartQuery API against the event data store ARN directly. Results are returned asynchronously; poll with GetQueryResults.

Athena federation — enables CloudTrail Lake event data stores as tables in the Glue Data Catalog, queryable from Athena alongside S3-resident data (CUR, application logs, etc.).

Performance comparison

Dimension Native CloudTrail Lake Athena Federation
Setup None — query ARN directly Federation role + Glue table registration required
Scan optimization Native ORC predicate pushdown; time-range filters map to internal indexes Athena reads ORC via Glue connector; similar pushdown but adds federation overhead
Query latency Typically 5–30s for month-scale scans Similar, with ~2–5s additional federation overhead for metadata resolution
Cross-source joins Not supported — only event data store data Supported — join CloudTrail Lake to CUR, VPC Flow Logs, ALB access logs in one query
Result output S3 or inline (small results) Always written to S3 query results bucket
Pricing $0.005/GB scanned (same) $0.005/GB scanned + Athena query coordination cost (same $5/TB)
Max result rows 1,000 rows inline; unlimited via S3 export Unlimited
Tooling AWS console, SDK, CLI Any Athena-compatible tool (QuickSight, Grafana, dbt, Tableau)

Recommendation

Use native CloudTrail Lake SQL for:

  • Point-in-time audit lookups (e.g., “show me all calls from this ARN on this date”)
  • Scheduled compliance reports against Bedrock-only data
  • Ad-hoc security investigations

Use Athena federation for:

  • Joining Bedrock call records to CUR for chargeback
  • Dashboards in QuickSight or Managed Grafana
  • Engineering teams with existing Athena workflows

The query cost is identical between both paths. The choice is driven by whether you need cross-source joins and what tooling the team already operates.


11. Practical: Bedrock-Focused Event Data Store Setup

Step-by-step with cost controls

Step 1: Estimate data volume before deploying

Before creating the event data store, enable a CloudTrail trail with Bedrock data events to S3 for 48 hours and measure the raw JSON volume. Multiply by your expected steady-state call volume to project monthly ingestion GB.

Step 2: Create the event data store with narrow selectors

Avoid capturing all data events account-wide. Scope to Bedrock specifically:

# Get current account and region
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION="us-east-1"

aws cloudtrail create-event-data-store \
  --name "bedrock-finops-compliance" \
  --retention-period 2557 \
  --advanced-event-selectors '[
    {
      "Name": "BedrockModelInvocations",
      "FieldSelectors": [
        {"Field": "eventCategory", "Equals": ["Data"]},
        {"Field": "resources.type", "Equals": ["AWS::Bedrock::Model"]}
      ]
    },
    {
      "Name": "BedrockAgentInvocations",
      "FieldSelectors": [
        {"Field": "eventCategory", "Equals": ["Data"]},
        {"Field": "resources.type", "Equals": ["AWS::Bedrock::AgentAlias"]}
      ]
    }
  ]' \
  --region $REGION

Retention of 2,557 days = 7 years. Adjust to 365 + extension if using 1-year tier.

Step 3: Tag the event data store for cost tracking

aws cloudtrail add-tags \
  --resource-id <event-data-store-arn> \
  --tags-list \
    Key=CostCenter,Value=ai-platform \
    Key=Compliance,Value=sox-finra \
    Key=Environment,Value=production

Step 4: Enable management event capture for control-plane audit

Management events (CreateFoundationModel, DeleteModelInvocationLoggingConfiguration, etc.) are captured automatically on the default trail. Verify:

aws cloudtrail get-trail-status --name default \
  --query '{IncludeManagementEvents: TrailStatus.IncludeManagementEvents}'

Step 5: Validate capture within 15 minutes

Run a test InvokeModel call and verify the event appears:

SELECT eventTime, eventName, requestID
FROM <event-data-store-arn>
WHERE
    eventSource = 'bedrock-runtime.amazonaws.com'
    AND eventTime > (NOW() - INTERVAL '1' HOUR)
ORDER BY eventTime DESC
LIMIT 10

Step 6: Set a CloudWatch alarm for ingestion anomalies

CloudTrail Lake does not expose a direct ingestion-GB metric. Proxy with call count:

aws cloudwatch put-metric-alarm \
  --alarm-name "bedrock-call-volume-spike" \
  --metric-name CallCount \
  --namespace AWS/CloudTrail \
  --period 3600 \
  --evaluation-periods 2 \
  --threshold 100000 \
  --comparison-operator GreaterThanThreshold \
  --alarm-actions <sns-topic-arn>

Common pitfalls

Pitfall 1: Management events only — no data events captured. The default CloudTrail trail captures management events. InvokeModel is a data event and is not captured unless explicitly configured. Verify with the validation query in Step 5.

Pitfall 2: additionalEventData token fields are null. Token counts are populated for synchronous InvokeModel and Converse calls. Streaming calls (InvokeModelWithResponseStream, ConverseStream) may have null token counts in additionalEventData — use CloudWatch Bedrock metrics or model invocation logs as a supplement for streaming attribution.

Pitfall 3: requestParameters.modelId contains the short ID, not the full ARN. Bedrock returns model IDs like anthropic.claude-3-5-sonnet-20241022-v2:0 in requestParameters. The full ARN appears in resources[].ARN. Use resources[].ARN for joins to CUR line_item_resource_id which uses ARN format.

Pitfall 4: Organization-level event data stores require Delegated Administrator setup. For multi-account CloudTrail Lake, the management account must designate a delegated administrator before member account events flow to the central store.

Pitfall 5: Athena federation IAM permissions. The federation role requires glue:CreateDatabase, glue:CreateTable, lakeformation:RegisterResource, and cloudtrail:GetQueryResults. Missing Lake Formation permissions produce silent failures with empty query results.


Sources