CloudTrail Trails
Note: This documentation is also available in a rendered format here.
Deploys CloudTrail trails for data events with KMS-encrypted log delivery to an existing audit bucket. Optionally includes management events. Use this module when you need to track who accessed or modified objects in your S3 buckets, or invoked other supported resources such as Bedrock AgentCore runtimes and Lambda functions, for security auditing and compliance requirements.
Deployed Resources
This module deploys and integrates the following resources:
CloudTrail Audit Trail - CloudTrail containing S3 Data Events configured to write to an audit bucket.

Related Modules
- Audit — Deploy the audit S3 bucket and KMS key that this trail writes to
- Data Lake — Enable S3 data event logging for data lake bucket access auditing
- Lake Formation Settings — Configure Lake Formation admin roles whose actions are captured by CloudTrail
Security/Compliance Details
This module is designed in alignment with MDAA security/compliance principles and CDK nag rulesets. Additional review is recommended prior to production deployment, ensuring organization-specific compliance requirements are met.
- Encryption at Rest:
- Trail logs encrypted with existing audit KMS key referenced via SSM parameter
Configuration
MDAA Config
Add the following snippet to your mdaa.yaml under the modules: section of a domain/env in order to use this module:
audit-trail: # Module Name can be customized
module_path: '@aws-mdaa/audit-trail' # Must match module NPM package name
module_configs:
- ./audit-trail.yaml # Filename/path can be customized
Module Config Samples and Variants
Copy the contents of the relevant sample config below into the ./audit-trail.yaml file referenced in the MDAA config snippet above.
Minimal Configuration
Required properties only — a CloudTrail trail with audit bucket and KMS key references. Start here for a basic S3 data event trail writing to an existing audit bucket.
# Contents available via above link
# Minimal Audit Trail module configuration.
# Contains only required properties for a CloudTrail trail.
trail:
# S3 bucket name where CloudTrail audit logs are stored
cloudTrailAuditBucketName: ssm:/sample-org/shared/audit/bucket/name
# KMS key ARN for encrypting CloudTrail logs written to S3
cloudTrailAuditKmsKeyArn: ssm:/sample-org/shared/audit/kms/cmk/arn
Comprehensive Configuration
Covers all available options including management events, scoped event selectors targeting specific buckets and prefixes, and multiple named trails via the trails property. Demonstrates both the legacy single trail and additional named trails coexisting in one config.
sample-config-comprehensive.yaml
# Contents available via above link
# Comprehensive sample config for the Audit Trail module.
# Covers EVERY non-excluded property from config-schema.json at full depth.
# Deploys multiple CloudTrail trails: one via the legacy 'trail' property and
# additional named trails via 'trails'. Both can coexist in the same config.
# Legacy single-trail configuration (backward-compatible).
# Produces a trail named '{org}-{env}-{domain}-{module}-s3-audit'.
trail:
# (Required) S3 bucket name where CloudTrail audit logs are stored.
# Accepts bucket names or SSM parameter references.
cloudTrailAuditBucketName: ssm:/sample-org/shared/audit/bucket/name
# (Required) KMS key ARN for encrypting CloudTrail logs written to S3.
# Accepts key ARNs or SSM parameter references.
# Direct ARN example: arn:{{partition}}:kms:{{region}}:{{account}}:key/audit-key-id
cloudTrailAuditKmsKeyArn: ssm:/sample-org/shared/audit/kms/cmk/arn
# (Optional, boolean) If true, management/control plane events will be
# included in trail. Otherwise, only S3 Data Events will be included.
includeManagementEvents: true
# (Optional) Scoped S3 event selectors. If omitted, all S3 data events
# in the account are captured. Each entry targets a specific bucket and
# optional key prefix.
eventSelectors:
# Selector targeting a specific bucket and prefix
- bucketName: ssm:/sample-org/datalake/raw-bucket/name
# (Optional) S3 key prefix to narrow event capture
objectPrefix: sensitive-data/
# Selector targeting an entire bucket (no prefix)
- bucketName: ssm:/sample-org/datalake/curated-bucket/name
# Named trail configurations for deploying multiple independent trails.
# Each key becomes the trail's resource name segment
# (e.g., '{org}-{env}-{domain}-{module}-analytics-audit').
trails:
# Trail scoped to analytics workloads
analytics-audit:
cloudTrailAuditBucketName: ssm:/sample-org/analytics/audit/bucket/name
cloudTrailAuditKmsKeyArn: ssm:/sample-org/analytics/audit/kms/cmk/arn
includeManagementEvents: false
eventSelectors:
- bucketName: ssm:/sample-org/analytics/reports-bucket/name
objectPrefix: daily/
Trails Only
Uses only the trails property without the legacy trail — for deployments that exclusively use named trails without needing the default s3-audit trail.
sample-config-trails-only.yaml
# Contents available via above link
# Trails-only sample config for the Audit Trail module.
# Demonstrates using only the 'trails' property without the legacy 'trail' property.
# Use this pattern when deploying multiple named trails without a default 's3-audit' trail.
trails:
# Trail scoped to datalake buckets
datalake-audit:
# (Required) S3 bucket name where CloudTrail audit logs are stored.
cloudTrailAuditBucketName: ssm:/sample-org/shared/audit/bucket/name
# (Required) KMS key ARN for encrypting CloudTrail logs written to S3.
cloudTrailAuditKmsKeyArn: ssm:/sample-org/shared/audit/kms/cmk/arn
includeManagementEvents: false
eventSelectors:
- bucketName: ssm:/sample-org/datalake/raw-bucket/name
objectPrefix: sensitive-data/
Data Events (non-S3 resource types)
Uses dataEventSelectors to capture CloudTrail data events for any supported resource type — Bedrock AgentCore, Lambda, DynamoDB, and so on — rather than S3 only. A separate config because the two selector styles cannot be combined on one trail (see below).
sample-config-data-events.yaml
# Contents available via above link
# Data events sample config for the Audit Trail module.
# Demonstrates 'dataEventSelectors', which captures CloudTrail data events for any
# supported resource type via advanced event selectors.
#
# This is a separate sample config rather than part of the comprehensive one because
# CloudTrail accepts either basic event selectors ('eventSelectors', S3-only) or advanced
# event selectors ('dataEventSelectors', any resource type) on a trail, never both.
# Setting both on one trail fails at synth.
#
# Cost warning: data events are billed per event and can be high volume on a busy
# resource. Scope with 'resourceArns' rather than capturing a whole resource type
# wherever practical.
trails:
# Trail capturing Bedrock AgentCore runtime activity. This is the prerequisite for the
# AgentCore Runtime module's 'eventBridgeAlerts' rules: EventBridge only receives
# 'AWS API Call via CloudTrail' events if a trail in this account and region logs them.
agentcore-audit:
# (Required) S3 bucket name where CloudTrail audit logs are stored.
cloudTrailAuditBucketName: ssm:/sample-org/shared/audit/bucket/name
# (Required) KMS key ARN for encrypting CloudTrail logs written to S3.
cloudTrailAuditKmsKeyArn: ssm:/sample-org/shared/audit/kms/cmk/arn
# (Optional, boolean) Capture management events alongside the data events below.
# Keep this true when alerting on control plane calls such as UpdateAgentRuntime or
# DeleteAgentRuntime: advanced event selectors REPLACE a trail's default selectors,
# so a trail carrying only data selectors captures no control plane events at all.
includeManagementEvents: true
# (Optional) Data event selectors, rendered as advanced event selectors.
# Each key becomes the selector's name on the trail.
# Mutually exclusive with 'eventSelectors'.
dataEventSelectors:
# Runtime invocations. This is what makes InvokeAgentRuntime — and the
# AccessDenied on a denied invocation — visible to EventBridge.
agentcore-runtime:
# (Required) The CloudTrail resources.type to capture. CloudTrail accepts exactly
# one resource type per selector, so covering the runtime endpoint as well means
# a second entry below rather than a second value here.
resourceType: AWS::BedrockAgentCore::Runtime
# (Optional) Scope to specific resources, matched as ARN prefixes. Omit to capture
# every resource of the type in the account.
#
# The path is <runtime-module-name>/agentruntime/<agentRuntimeName>/arn, where the
# first segment is the runtime module's name in mdaa.yaml and the last is its
# agentRuntimeName lowercased. Replace both placeholders below.
resourceArns:
- ssm-domain:/bedrock-agentcore-runtime/agentruntime/my-agent-runtime/arn
# (Optional) Capture read events, write events, or both. Omit for both.
# 'All' is the explicit form of the default.
readWriteType: All
# Runtime endpoint activity, captured as a separate selector per the one
# resources.type per selector rule noted above.
# Pointing this at the RUNTIME ARN is deliberate: an endpoint ARN is
# <runtime ARN>/runtime-endpoint/<name>, so the runtime ARN prefixes it.
agentcore-runtime-endpoint:
resourceType: AWS::BedrockAgentCore::RuntimeEndpoint
resourceArns:
- ssm-domain:/bedrock-agentcore-runtime/agentruntime/my-agent-runtime/arn
# Trail capturing Lambda invocations, showing that this feature is not AgentCore
# specific — any resources.type CloudTrail supports for data events works here.
lambda-audit:
cloudTrailAuditBucketName: ssm:/sample-org/shared/audit/bucket/name
cloudTrailAuditKmsKeyArn: ssm:/sample-org/shared/audit/kms/cmk/arn
# Management events are not needed on this trail; the AgentCore trail above already
# captures them for the account and region, and duplicating them doubles the cost.
includeManagementEvents: false
dataEventSelectors:
# Whole-resource-type capture, with no resourceArns scoping. Convenient, but the
# broadest and most expensive option — every Lambda invocation in the account.
all-lambda-functions:
resourceType: AWS::Lambda::Function
# Only mutating calls, halving volume where reads are not of interest.
readWriteType: WriteOnly
Data Event Selectors
eventSelectors captures S3 data events only. To capture data events for any other resource type, use dataEventSelectors, which renders CloudTrail advanced event selectors.
The two are mutually exclusive on a single trail — CloudTrail accepts either basic or advanced event selectors, never both. Setting both on one trail fails at synth. To use both styles, split them across separate trails.
Behaviors worth knowing before configuring these:
- One resource type per selector. CloudTrail rejects a selector naming more than one
resources.type, so capturing several types means severaldataEventSelectorsentries. For an AgentCore runtime, that usually means bothAWS::BedrockAgentCore::RuntimeandAWS::BedrockAgentCore::RuntimeEndpoint. includeManagementEventsmatters more here. Advanced event selectors replace a trail's default selectors outright, so a trail withdataEventSelectorsand noincludeManagementEvents: truecaptures no control plane events at all. Set it totruewhen the same trail should also cover lifecycle calls such asUpdateAgentRuntimeorDeleteAgentRuntime.resourceArnsis matched as a prefix (StartsWith), so a parent ARN also covers resources beneath it — an AgentCore runtime ARN prefixes its runtime endpoints. Resource types with flat ARNs, such as Lambda functions and DynamoDB tables, have no such hierarchy, so a partial ARN also matches any other resource sharing that prefix: give the full ARN unless a prefix match is what you want. Omitting it captures every resource of the type in the account, which is the most expensive option.- Data events are billed per event and can be high volume. Scope with
resourceArns, and usereadWriteTypewhere only one direction is of interest. - Valid
resourceTypevalues are validated by CloudTrail at deploy, not at synth. An unsupported value is rejected when the trail is created. The supported values are listed in the CloudTrail data events documentation. - Event delivery to EventBridge lags by a few minutes. A test that triggers an event and immediately asserts on delivery needs to poll rather than check once.
Enabling EventBridge alerting on AgentCore invocations
EventBridge rules matching AWS API Call via CloudTrail events only fire if a trail in the same account and region logs those events. Management events are logged by any trail with management logging enabled, but data events — including InvokeAgentRuntime and the AccessDenied it records on a denied invocation — are off by default. Without a trail configured for them, rules scoped to invocation failures deploy cleanly and never fire.
The Data Events sample config above deploys such a trail. Point resourceArns at the runtime being audited, and keep includeManagementEvents: true so control plane rules keep working on the same trail.