CDK Resource Architecture

What exactly does cdk deploy create? This document inventories every AWS resource the Bedrock Budgeteer stack provisions, how they connect to each other, and how the system integrates with AWS Bedrock.

For the conceptual architecture and data flow diagrams, see System Architecture and System Diagrams.

Stack Overview

Entry point: app/app.py creates a single BedrockBudgeteerStack (environment: production).

The stack assembles 9 CDK constructs in strict dependency order:

TaggingFramework          (CDK Aspect -- applies tags to everything below)
     |
SecurityConstruct         (IAM roles & policies)
     |
DataStorageConstruct      (DynamoDB tables)
     |
LogStorageConstruct       (S3 bucket)
     |
ConfigurationConstruct    (SSM parameters)
     |
CoreProcessingConstruct   (Lambda functions, DLQs, schedules)
     |                     depends on: tables, bucket, lambda role
EventIngestionConstruct   (CloudTrail, EventBridge, Firehose)
     |                     depends on: bucket, usage_calculator Lambda
MonitoringConstruct       (SNS, dashboards, alarms)
     |
WorkflowOrchestrationConstruct  (Step Functions, workflow Lambdas)
                           depends on: tables, core Lambdas, SF role, lambda role, SNS topics

After all constructs are created, the stack wires up cross-cutting concerns:

  • Adds service-level permissions to the Lambda execution role (Bedrock, Pricing API, CloudTrail, SSM, IAM read, CloudWatch metrics, DynamoDB, S3, SQS).
  • Creates monitoring for every Lambda, DLQ, table, trail, rule, Firehose stream, and state machine.
  • Sets up notification channels (email, Slack, SMS, webhook) from environment variables.

1. TaggingFramework

Source: app/app/constructs/tagging.py

Creates no AWS resources directly. Registers a single CDK Aspect (UnifiedTaggingAspect) that visits every CloudFormation resource at synth time and applies tags in one pass.

Core tags (all resources)

Tag Value
App bedrock-budgeteer
Owner [email protected]
Project cost-management
CostCenter engineering-ops
BillingProject bedrock-budget-control
Environment production
ManagedBy cdk
Version v1.0.0
AutoShutdown disabled
CostOptimization conservative
ResourceTTL permanent

Compliance tags (by resource type)

Resource Type Extra Tags
DynamoDB Table BackupRequired: true
Lambda Function DataClassification: internal
Step Functions State Machine DataClassification: internal, AuditTrail: enabled
EventBridge Rule MonitoringLevel: standard
KMS Key KeyRotation: enabled
CloudWatch Log Group DataClassification: internal
SNS Topic DataClassification: internal, NotificationLevel: operational

2. SecurityConstruct

Source: app/app/constructs/security.py

IAM Roles (4)

Role Name Service Principal Purpose
bedrock-budgeteer-{env}-lambda-execution lambda.amazonaws.com All Lambda functions share this role
bedrock-budgeteer-{env}-step-functions states.amazonaws.com Both Step Functions state machines
bedrock-budgeteer-{env}-eventbridge events.amazonaws.com EventBridge service role
bedrock-budgeteer-{env}-bedrock-logging bedrock.amazonaws.com Bedrock invocation logging to CloudWatch

Managed Policies (2)

Policy Name Grants
bedrock-budgeteer-{env}-dynamodb-access CRUD on all bedrock-budgeteer DynamoDB tables + GSIs
bedrock-budgeteer-{env}-eventbridge-publish events:PutEvents to EventBridge

Permissions added later by the stack

The Lambda execution role receives additional inline policies after all constructs are created:

  • Bedrock: ListFoundationModels, InvokeModel
  • Pricing API: pricing:GetProducts
  • CloudTrail: Read access
  • SSM: GetParameter, GetParameters, GetParametersByPath
  • IAM: Read-only (GetUser, ListAttachedUserPolicies, etc.)
  • CloudWatch: PutMetricData
  • DynamoDB: Full access to all application tables
  • S3: Read-only access to the logs bucket
  • SQS: SendMessage to all DLQs

Connects to: Every other construct consumes roles from SecurityConstruct.

3. DataStorageConstruct

Source: app/app/constructs/data_storage.py

DynamoDB Tables (4)

Table Partition Key Sort Key GSIs
bedrock-budgeteer-{env}-user-budgets principal_id (S) BudgetStatusIndex (pk: budget_status, sk: created_at)
bedrock-budgeteer-{env}-usage-tracking principal_id (S) timestamp (S) ServiceUsageIndex (pk: service_name, sk: timestamp)
bedrock-budgeteer-{env}-audit-logs event_id (S) event_time (S) UserAuditIndex (pk: user_identity, sk: event_time), EventSourceIndex (pk: event_source, sk: event_time)
bedrock-budgeteer-{env}-pricing model_id (S) region (S)

Provisioned capacity (per table)

Table Read Capacity Write Capacity
user-budgets 5 5
usage-tracking 10 20
audit-logs 10 25
pricing 5 2

All tables auto-scale at 70% target utilization.

Common table settings

Setting Value
Billing mode PROVISIONED
Encryption AWS-managed (or customer KMS if key provided)
Point-in-time recovery Disabled (allows clean rollback)
Removal policy DESTROY

The pricing table uses a TTL attribute (ttl) so stale pricing entries expire automatically.

Connects to: CoreProcessingConstruct and WorkflowOrchestrationConstruct receive the tables dict. Every Lambda reads/writes these tables at runtime.

4. LogStorageConstruct

Source: app/app/constructs/log_storage.py

S3 Buckets (1)

Bucket Purpose Lifecycle
bedrock-budgeteer-{env}-logs CloudTrail logs, Firehose output, error logs Delete objects after 30 days

Configuration

Setting Value
Encryption S3-managed (or KMS if key provided)
Versioning Disabled
Block public access BLOCK_ACLS (configurable via feature flag for enterprise SCPs)
Object ownership BUCKET_OWNER_PREFERRED (allows CloudTrail ACL writes)
Auto-delete objects true
Removal policy DESTROY

Connects to: Passed to CoreProcessingConstruct (Lambda env vars), EventIngestionConstruct (CloudTrail destination, Firehose destination).

5. ConfigurationConstruct

Source: app/app/constructs/configuration.py

SSM Parameters (6)

Parameter Path Default Purpose
/bedrock-budgeteer/{env}/cost/budget_refresh_period_days 30 Days between budget resets
/bedrock-budgeteer/{env}/monitoring/log_retention_days 7 CloudWatch log retention
/bedrock-budgeteer/global/thresholds_percent_warn 70 Warning threshold %
/bedrock-budgeteer/global/thresholds_percent_critical 90 Critical threshold %
/bedrock-budgeteer/global/default_user_budget_usd 1 Default per-user budget (USD)
/bedrock-budgeteer/global/grace_period_seconds 300 Seconds before suspension after budget exceeded

All parameters are STANDARD tier. Environment-scoped parameters include the environment name in the path; global parameters are shared across environments.

Connects to: Lambda functions read these at runtime via the shared ConfigurationManager utility. The monitoring log retention parameter name is passed to MonitoringConstruct.

6. CoreProcessingConstruct

Source: app/app/constructs/core_processing.py

Lambda Functions (7)

Function Name Memory Timeout Trigger Purpose
bedrock-budgeteer-user-setup-{env} 512 MB 5 min EventBridge (IAM key creation) Initialize budget for new Bedrock API users
bedrock-budgeteer-usage-calculator-{env} 1024 MB 10 min Firehose data transformation Parse invocation logs, calculate token costs, update usage
bedrock-budgeteer-budget-monitor-{env} 512 MB 5 min EventBridge schedule (every 5 min) Check thresholds, start grace periods, trigger suspension
bedrock-budgeteer-budget-refresh-{env} 512 MB 5 min EventBridge schedule (daily) Reset budgets at refresh date, trigger auto-restoration
bedrock-budgeteer-audit-logger-{env} 512 MB 5 min EventBridge (budgeteer events) Write audit trail to DynamoDB
bedrock-budgeteer-state-reconciliation-{env} 512 MB 5 min EventBridge schedule (every 4 hr) Verify DynamoDB status matches actual IAM state
bedrock-budgeteer-pricing-manager-{env} 512 MB 5 min EventBridge schedule (daily) Refresh Bedrock model pricing from AWS Pricing API

All functions use Python 3.11 runtime, share the Lambda execution role, and have inline code generated from app/app/constructs/lambda_functions/ and app/app/constructs/shared/lambda_utilities.py.

SQS Dead Letter Queues (6)

One DLQ per function (except pricing_manager):

bedrock-budgeteer-{function_name}-dlq-{env}
  • Retention: 14 days
  • Visibility timeout: 5 minutes
  • Encryption: KMS-managed

Environment Variables (all functions)

Variable Source
ENVIRONMENT Stack environment name
USER_BUDGETS_TABLE DataStorageConstruct
USAGE_TRACKING_TABLE DataStorageConstruct
AUDIT_LOGS_TABLE DataStorageConstruct
PRICING_TABLE DataStorageConstruct
LOGS_BUCKET LogStorageConstruct

Connects to: EventIngestionConstruct receives the usage_calculator function for Firehose data transformation. WorkflowOrchestrationConstruct receives the full functions dict.

7. EventIngestionConstruct

Source: app/app/constructs/event_ingestion.py

This construct builds the pipeline that captures Bedrock API activity and feeds it into the processing layer.

CloudTrail (1 trail)

Resource Configuration
bedrock-budgeteer-{env}-trail Multi-region, file validation enabled, CloudWatch Logs (30-day retention), management events capture Bedrock API calls

EventBridge Rules (3 – rules only, no targets)

EventIngestionConstruct defines the rules and event patterns below, but does not attach targets. Target wiring happens in CoreProcessingConstruct._setup_event_routing().

Rule Event Pattern
bedrock-budgeteer-{env}-bedrock-usage source: aws.bedrock, events: InvokeModel, InvokeModelWithResponseStream, GetFoundationModel, ListFoundationModels
bedrock-budgeteer-{env}-iam-key-creation source: aws.iam, events: CreateUser, CreateServiceSpecificCredential, AttachRolePolicy
bedrock-budgeteer-{env}-iam-bedrock-permissions source: aws.iam, events: AttachUserPolicy, DetachUserPolicy, PutUserPolicy, DeleteUserPolicy

Kinesis Data Firehose Streams (2)

Stream Destination Buffer Transformation
bedrock-budgeteer-{env}-usage-logs S3 (bedrock-usage-logs/year=/month=/day=/hour=/) 5 MB / 300s, GZIP usage_calculator Lambda
bedrock-budgeteer-{env}-audit-logs S3 (audit-logs/year=/month=/day=/hour=/) 3 MB / 60s, GZIP None

CloudWatch Log Group (1) + Lambda Forwarder (1)

Resource Configuration
/aws/bedrock/bedrock-budgeteer-{env}-invocation-logs 7-day retention, KMS encryption (if key provided)
bedrock-budgeteer-{env}-logs-forwarder Lambda Subscription filter on the log group, forwards to Firehose

How Bedrock connects in

Bedrock API call
     |
     +---> CloudTrail (management event) ---> EventBridge rules
     |
     +---> Bedrock invocation logs ---> CloudWatch log group
                                             |
                                        Subscription filter
                                             |
                                        logs-forwarder Lambda
                                             |
                                        Firehose (usage-logs)
                                             |
                                        usage_calculator Lambda (transformation)
                                             |
                                     +-------+-------+
                                     |               |
                               DynamoDB tables    S3 (archive)

Connects to: Receives s3_bucket from LogStorageConstruct and usage_calculator_function from CoreProcessingConstruct. Its trails, rules, streams, and log groups are all passed to MonitoringConstruct for alarm creation.

8. MonitoringConstruct

Source: app/app/constructs/monitoring.py

SNS Topics (3)

Topic Name Purpose
operational_alerts bedrock-budgeteer-{env}-operational-alerts System operational issues
budget_alerts bedrock-budgeteer-{env}-budget-alerts Budget threshold violations
high_severity bedrock-budgeteer-{env}-high-severity Critical failures

Notification Channels (configured via environment variables)

Channel Env Variable Topics
Email OPS_EMAIL (or cdk.json alert-email) All 3 topics
Slack SLACK_WEBHOOK_URL high_severity, operational_alerts
SMS OPS_PHONE_NUMBER high_severity
Webhook EXTERNAL_WEBHOOK_URL budget_alerts

CloudWatch Dashboards (3)

Dashboard Content
bedrock-budgeteer-{env}-system Lambda invocations/errors/duration, DynamoDB capacity, S3 objects
Ingestion pipeline dashboard CloudTrail events, EventBridge rule metrics, Firehose delivery
Workflow dashboard Step Functions executions, state machine errors

CloudWatch Alarms (per monitored resource)

Alarm Pattern Metric Threshold Notification
{function}-errors Lambda Errors >= 1 in 5 min high_severity
{function}-duration Lambda Duration (p99) >= 1000 ms operational_alerts
{table}-read-throttles DynamoDB ThrottledRequests >= 5 operational_alerts
{trail}-errors CloudTrail ErrorCount >= 1 operational_alerts
{rule}-failed EventBridge FailedInvocations >= 1 high_severity
{stream}-delivery Firehose DataFreshness >= 900s operational_alerts
{queue}-depth SQS MessageCount >= 10 operational_alerts

Custom Business Metrics (CloudWatch namespace: BedrockBudgeteer)

MonitoredUsers, BudgetExceededUsers, GracePeriodsStarted, SuspensionWorkflowsTriggered, BudgetRefreshCompleted, AutomaticRestorationsTriggered, AuditEventsProcessed, ReconciledUsers.

Connects to: Receives monitoring parameter name from ConfigurationConstruct. SNS topics are passed to WorkflowOrchestrationConstruct for workflow notifications.

9. WorkflowOrchestrationConstruct

Source: app/app/constructs/workflow_orchestration.py

Step Functions State Machines (2)

Suspension Workflow (bedrock-budgeteer-suspension-{env}, 30-min timeout):

SendGraceNotification --> GracePeriodWait --> SendFinalWarning
     --> ApplyFullSuspension --> UpdateUserStatus --> Success
  • Triggered by EventBridge when budget_monitor publishes Suspension Workflow Required
  • ApplyFullSuspension detaches AmazonBedrockLimitedAccess managed policy from the IAM user

Restoration Workflow (bedrock-budgeteer-restoration-{env}, 10-min timeout):

ValidateAutomaticRestoration --> [valid?]
     --> RestoreAccess --> ValidateAccessRestoration
     --> ResetBudgetStatus --> LogRestorationAudit
     --> SendRestorationNotification --> Success
  • Triggered by EventBridge when budget_refresh publishes Automatic User Restoration Required
  • RestoreAccess re-attaches AmazonBedrockLimitedAccess managed policy

Workflow Lambda Functions (4)

Function Memory Timeout Purpose
bedrock-budgeteer-iam-utilities-{env} 512 MB 5 min Detach/attach IAM policies, validate restrictions
bedrock-budgeteer-grace-period-{env} 256 MB 2 min Send notifications via SNS (initial, final, restoration)
bedrock-budgeteer-policy-backup-{env} 256 MB 1 min Backup/restore original IAM policies
bedrock-budgeteer-restoration-validation-{env} 256 MB 3 min Check if refresh period has elapsed

Workflow DLQs (4)

One DLQ per workflow Lambda, same configuration as core DLQs.

EventBridge Trigger Rules (2)

Rule Event Detail Type Target
bedrock-budgeteer-suspension-trigger-{env} Suspension Workflow Required Suspension state machine
bedrock-budgeteer-automatic-restoration-trigger-{env} Automatic User Restoration Required Restoration state machine

Connects to: Receives dynamodb_tables, lambda_functions, step_functions_role, lambda_execution_role, and sns_topics from other constructs. The iam_utilities Lambda gets additional IAM write permissions (attach/detach policies) beyond the shared Lambda execution role.

End-to-End Integration Map

Bedrock –> Budgeteer (usage capture)

User calls Bedrock API (InvokeModel)
     |
     +--> CloudTrail captures management event
     |         |
     |         +--> EventBridge (bedrock-usage rule)
     |                   |
     |                   +--> Firehose (usage-logs) --> S3 archive
     |
     +--> Bedrock writes invocation log (tokens, model, latency)
               |
               +--> CloudWatch log group (/aws/bedrock/...)
                         |
                         +--> Subscription filter
                                   |
                                   +--> logs-forwarder Lambda
                                             |
                                             +--> Firehose (usage-logs)
                                                       |
                                                  usage_calculator Lambda
                                                       |
                                              +--------+--------+
                                              |                 |
                                        usage-tracking     user-budgets
                                        table (append)     table (update spent_usd)

Budgeteer –> Bedrock Users (enforcement)

budget_monitor Lambda (every 5 min)
     |
     +--> Scans user-budgets table
     |
     +--> spent_usd >= budget_limit_usd?
               |
          [yes]--> Grace period already started?
               |         |
               |    [no]--> Set grace_deadline_epoch, publish "Grace Period Started"
               |         |
               |    [yes, expired]--> Publish "Suspension Workflow Required"
               |                           |
               |                      EventBridge --> Step Functions
               |                           |
               |                      Grace notification (SNS)
               |                           |
               |                      Wait (grace_period_seconds)
               |                           |
               |                      Final warning (SNS)
               |                           |
               |                      iam_utilities Lambda:
               |                        Detach AmazonBedrockLimitedAccess
               |                           |
               |                      Update user-budgets: status = "suspended"
               |
budget_refresh Lambda (daily)
     |
     +--> Scans suspended users where refresh date reached
     |
     +--> Publishes "Automatic User Restoration Required"
               |
          EventBridge --> Step Functions
               |
          Validate eligibility --> Attach AmazonBedrockLimitedAccess
               |
          Reset spent_usd = 0, status = "active"
               |
          Restoration notification (SNS)

CDK deploy-time vs. Lambda runtime

At deploy time, CDK constructs wire AWS services together: IAM roles are assigned to Lambdas, table names are injected as environment variables, EventBridge rules are created with targets, Firehose is configured with the usage_calculator as its data transformer.

At runtime, Lambda functions use shared utilities that are concatenated into each function’s Code.from_inline() source string (not Lambda Layers). The utilities (app/app/constructs/shared/lambda_utilities.py) bundle:

  • ConfigurationManager – reads SSM parameters with local caching
  • DynamoDBHelper – Decimal/float conversion for DynamoDB
  • BedrockPricingCalculator – pricing lookup with 5-min local cache + DynamoDB + AWS Pricing API fallback chain
  • MetricsPublisher – publishes custom CloudWatch metrics
  • EventPublisher – publishes events to EventBridge for cross-Lambda communication

Resource Naming Convention

Resources use two naming patterns depending on service:

bedrock-budgeteer-{environment}-{component}   (tables, SNS, trails, Firehose, S3)
bedrock-budgeteer-{component}-{environment}   (Lambdas, DLQs, Step Functions)
Resource Type Example
DynamoDB table bedrock-budgeteer-production-user-budgets
Lambda function bedrock-budgeteer-usage-calculator-production
SQS DLQ bedrock-budgeteer-user_setup-dlq-production
SNS topic bedrock-budgeteer-production-operational-alerts
CloudTrail trail bedrock-budgeteer-production-trail
EventBridge rule bedrock-budgeteer-production-bedrock-usage
Firehose stream bedrock-budgeteer-production-usage-logs
Step Functions bedrock-budgeteer-suspension-production
S3 bucket bedrock-budgeteer-production-logs
SSM parameter /bedrock-budgeteer/production/cost/budget_refresh_period_days

Resource Count Summary

AWS Service Count Resources
DynamoDB Tables 4 user-budgets, usage-tracking, audit-logs, pricing
Lambda Functions 12+ 7 core + 4 workflow + 1 logs-forwarder (+ conditional Slack/webhook Lambdas if env vars set)
SQS Queues (DLQs) 10 6 core + 4 workflow
IAM Roles 4 lambda-execution, step-functions, eventbridge, bedrock-logging
IAM Managed Policies 2 dynamodb-access, eventbridge-publish
S3 Buckets 1 logs
SNS Topics 3 operational-alerts, budget-alerts, high-severity
CloudTrail Trails 1 trail
EventBridge Rules 5+ 3 ingestion + 2 workflow triggers + schedules
Firehose Streams 2 usage-logs, audit-logs
Step Functions 2 suspension, restoration
CloudWatch Dashboards 3 system, ingestion-pipeline, workflow
CloudWatch Alarms ~20+ Per Lambda, table, trail, rule, stream, and DLQ
SSM Parameters 6 2 env-scoped + 4 global
CloudWatch Log Groups 1+ Bedrock invocation logs (+ Lambda runtime-created function log groups)

Bedrock Budgeteer is an open-source project licensed under MIT.

This site uses Just the Docs, a documentation theme for Jekyll.