Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Amazon EventBridge Data Plane Logging with AWS CloudTrail

This pattern enables CloudTrail data plane logging for Amazon EventBridge and triggers a Lambda function when PutEvents API calls are detected, providing security and operational visibility into event bus activity.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-cloudtrail-dataplane-cdk

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details.

## Requirements

* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed
* [Node.js](https://nodejs.org/en/download/) installed

## Deployment Instructions

1. Clone and navigate to the pattern:
```
cd serverless-patterns/eventbridge-cloudtrail-dataplane-cdk
npm install
```
2. Deploy:
```
cdk deploy
```

## How it works

- A CloudTrail trail is created with data event logging enabled
- EventBridge data plane API calls (PutEvents) are now logged to CloudTrail (new May 2026 feature)
- An EventBridge rule captures these CloudTrail events matching `aws.events` source with `PutEvents` event name
- A Lambda function processes the events, logging the caller identity, source IP, event bus, and entry count
- This enables security teams to audit who is putting events to which bus

## Testing

```bash
# Put a test event to the default event bus
aws events put-events --entries '[{"Source":"test.app","DetailType":"TestEvent","Detail":"{\"key\":\"value\"}"}]'

# Check Lambda logs (allow ~5 minutes for CloudTrail delivery)
aws logs tail /aws/lambda/$(aws cloudformation describe-stacks \
--stack-name EventbridgeCloudtrailDataplaneStack \
--query 'Stacks[0].Outputs[?OutputKey==`ProcessorFunctionName`].OutputValue' --output text) \
--follow
```

## Cleanup

```
cdk destroy
```

---

Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
7 changes: 7 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { EventbridgeCloudtrailDataplaneStack } from '../lib/eventbridge-cloudtrail-dataplane-stack';

const app = new cdk.App();
new EventbridgeCloudtrailDataplaneStack(app, 'EventbridgeCloudtrailDataplaneStack');
3 changes: 3 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts"
}
40 changes: 40 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"title": "Amazon EventBridge Data Plane Logging with AWS CloudTrail",
"description": "Monitor EventBridge PutEvents API calls using CloudTrail data plane logging with Lambda alerting for security and operational visibility.",
"language": "TypeScript",
"level": "300",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"This pattern enables CloudTrail data plane logging for Amazon EventBridge (launched May 2026).",
"CloudTrail captures PutEvents API calls and delivers them as events to EventBridge.",
"An EventBridge rule matches these CloudTrail events and triggers a Lambda function for alerting.",
"This provides visibility into who is putting events, from where, and how many — essential for security auditing."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-cloudtrail-dataplane-cdk",
"templateURL": "serverless-patterns/eventbridge-cloudtrail-dataplane-cdk",
"projectFolder": "eventbridge-cloudtrail-dataplane-cdk",
"templateFile": "lib/eventbridge-cloudtrail-dataplane-stack.ts"
}
},
"resources": {
"bullets": [
{ "text": "EventBridge Data Plane CloudTrail Logging", "link": "https://aws.amazon.com/about-aws/whats-new/2026/05/amazon-eventbridge-data-aws-cloudtrail/" },
{ "text": "CloudTrail Data Events", "link": "https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html" }
]
},
"deploy": { "text": ["cdk deploy"] },
"testing": { "text": ["See the README for testing instructions."] },
"cleanup": { "text": ["cdk destroy"] },
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS, passionate about serverless and AI/ML.",
"linkedin": "nithin-chandran-r"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as cdk from 'aws-cdk-lib';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as cloudtrail from 'aws-cdk-lib/aws-cloudtrail';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';

export class EventbridgeCloudtrailDataplaneStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// S3 bucket for CloudTrail logs
const trailBucket = new s3.Bucket(this, 'TrailBucket', {
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
enforceSSL: true,
});

// CloudTrail trail with data events for EventBridge
const trail = new cloudtrail.Trail(this, 'EventBridgeDataPlaneTrail', {
bucket: trailBucket,
trailName: 'eventbridge-dataplane-trail',
isMultiRegionTrail: false,
});

// Enable EventBridge data plane events logging
trail.addEventSelector(cloudtrail.DataResourceType.LAMBDA_FUNCTION, ['arn:aws:lambda']);

// Lambda function to process CloudTrail events
const processor = new lambda.Function(this, 'EventProcessor', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('src'),
timeout: cdk.Duration.seconds(10),
loggingFormat: lambda.LoggingFormat.JSON,
});

// EventBridge rule to capture EventBridge PutEvents API calls from CloudTrail
const rule = new events.Rule(this, 'DataPlaneRule', {
eventPattern: {
source: ['aws.events'],
detailType: ['AWS API Call via CloudTrail'],
detail: {
eventSource: ['events.amazonaws.com'],
eventName: ['PutEvents'],
},
},
});

rule.addTarget(new targets.LambdaFunction(processor));

new cdk.CfnOutput(this, 'ProcessorFunctionName', { value: processor.functionName });
new cdk.CfnOutput(this, 'TrailBucketName', { value: trailBucket.bucketName });
new cdk.CfnOutput(this, 'RuleName', { value: rule.ruleName });
}
}
16 changes: 16 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "eventbridge-cloudtrail-dataplane-cdk",
"version": "1.0.0",
"bin": { "app": "bin/app.ts" },
"scripts": { "build": "tsc", "cdk": "cdk" },
"dependencies": {
"aws-cdk-lib": "^2.180.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.21"
},
"devDependencies": {
"typescript": "~5.4.0",
"ts-node": "^10.9.0",
"@types/node": "^20.0.0"
}
}
15 changes: 15 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
exports.handler = async (event) => {
const detail = event.detail || {};
console.log(JSON.stringify({
message: 'EventBridge data plane API call detected',
eventName: detail.eventName,
eventSource: detail.eventSource,
sourceIPAddress: detail.sourceIPAddress,
userAgent: detail.userAgent,
userIdentity: detail.userIdentity?.arn,
eventBusName: detail.requestParameters?.entries?.[0]?.eventBusName || 'default',
entryCount: detail.requestParameters?.entries?.length || 0,
eventTime: detail.eventTime,
}));
return { statusCode: 200 };
};
8 changes: 8 additions & 0 deletions eventbridge-cloudtrail-dataplane-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES2020", "module": "commonjs", "lib": ["es2020"],
"declaration": true, "strict": true, "outDir": "build",
"rootDir": ".", "skipLibCheck": true, "forceConsistentCasingInFileNames": true
},
"exclude": ["node_modules", "build"]
}