-
Notifications
You must be signed in to change notification settings - Fork 1k
New pattern - eventbridge-cloudtrail-dataplane-cdk #3100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
NithinChandranR-AWS
wants to merge
2
commits into
aws-samples:main
Choose a base branch
from
NithinChandranR-AWS:NithinChandranR-AWS-feature-eventbridge-cloudtrail-dataplane-cdk
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # AWS AppSync Events with AWS Lambda | ||
|
|
||
| This pattern deploys an AWS AppSync Events API for real-time WebSocket pub/sub with an AWS Lambda event handler. | ||
|
|
||
| Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/appsync-events-lambda-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 | ||
| * [Node.js 22+](https://nodejs.org/en/download/) installed | ||
| * [AWS CDK v2](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) installed | ||
|
|
||
| ## Architecture | ||
|
|
||
|  | ||
|
|
||
| ## How it works | ||
|
|
||
| 1. Publishers send events via HTTP POST to the AWS AppSync Events endpoint. | ||
| 2. The AWS Lambda function processes and enriches events before delivery. | ||
| 3. AWS AppSync Events delivers messages to all WebSocket subscribers on that channel. | ||
| 4. Channel namespaces (`notifications`, `alerts`) organize topics. | ||
|
|
||
| ## Deployment | ||
|
|
||
| 1. Clone the repository and navigate to the pattern directory: | ||
| ```bash | ||
| git clone https://github.com/aws-samples/serverless-patterns | ||
| cd serverless-patterns/appsync-events-lambda-cdk | ||
| ``` | ||
|
|
||
| 2. Install dependencies: | ||
| ```bash | ||
| npm install | ||
| ``` | ||
|
|
||
| 3. Bootstrap CDK (one-time per account/region): | ||
| ```bash | ||
| cdk bootstrap | ||
| ``` | ||
|
|
||
| 4. Deploy the stack: | ||
| ```bash | ||
| cdk deploy | ||
| ``` | ||
|
|
||
| ## Testing | ||
|
|
||
| ```bash | ||
| # Publish an event (replace values from cdk deploy output) | ||
| curl -X POST "https://<HttpEndpoint>/event" \ | ||
| -H "x-api-key: <ApiKeyValue>" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"channel":"notifications/general","events":["{\"message\":\"Hello from CDK\"}"]}' | ||
| ``` | ||
|
|
||
| ## Cleanup | ||
|
|
||
| ```bash | ||
| cdk destroy | ||
| ``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| #!/usr/bin/env node | ||
| import 'source-map-support/register'; | ||
| import * as cdk from 'aws-cdk-lib'; | ||
| import { AppsyncEventsLambdaStack } from '../lib/appsync-events-lambda-stack'; | ||
| const app = new cdk.App(); | ||
| new AppsyncEventsLambdaStack(app, 'AppsyncEventsLambdaStack'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {"app":"npx ts-node --prefer-ts-exts bin/app.ts"} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| { | ||
| "title": "AWS AppSync Events with AWS Lambda handler", | ||
| "description": "Deploy an AWS AppSync Events API with an AWS Lambda handler for real-time pub/sub message processing.", | ||
| "language": "TypeScript", | ||
| "level": "300", | ||
| "framework": "CDK", | ||
| "introBox": { | ||
| "headline": "How it works", | ||
| "text": [ | ||
| "AWS AppSync Events provides WebSocket-based real-time pub/sub. An AWS Lambda handler processes published messages before delivery to subscribers." | ||
| ] | ||
| }, | ||
| "gitHub": { | ||
| "template": { | ||
| "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/appsync-events-lambda-cdk", | ||
| "templateURL": "serverless-patterns/appsync-events-lambda-cdk", | ||
| "projectFolder": "appsync-events-lambda-cdk" | ||
| } | ||
| }, | ||
| "resources": { | ||
| "bullets": [ | ||
| { | ||
| "text": "AWS AppSync Events", | ||
| "link": "https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html" | ||
| } | ||
| ] | ||
| }, | ||
| "deploy": { | ||
| "text": [ | ||
| "cdk deploy" | ||
| ], | ||
| "commands": [ | ||
| "npm install", | ||
| "cdk bootstrap", | ||
| "cdk deploy" | ||
| ] | ||
| }, | ||
| "testing": { | ||
| "text": [ | ||
| "Publish events via HTTP endpoint" | ||
| ] | ||
| }, | ||
| "cleanup": { | ||
| "text": [ | ||
| "cdk destroy" | ||
| ], | ||
| "commands": [ | ||
| "cdk destroy" | ||
| ] | ||
| }, | ||
| "authors": [ | ||
| { | ||
| "name": "Nithin Chandran R", | ||
| "bio": "Technical Account Manager at AWS", | ||
| "linkedin": "nithin-chandran-r" | ||
| } | ||
| ], | ||
| "services": { | ||
| "from": [ | ||
| { | ||
| "service": "appsync" | ||
| } | ||
| ], | ||
| "to": [ | ||
| { | ||
| "service": "lambda" | ||
| } | ||
| ] | ||
| } | ||
| } |
79 changes: 79 additions & 0 deletions
79
appsync-events-lambda-cdk/lib/appsync-events-lambda-stack.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import * as cdk from 'aws-cdk-lib'; | ||
| import * as appsync from 'aws-cdk-lib/aws-appsync'; | ||
| import * as lambda from 'aws-cdk-lib/aws-lambda'; | ||
| import * as logs from 'aws-cdk-lib/aws-logs'; | ||
| import * as iam from 'aws-cdk-lib/aws-iam'; | ||
| import { Construct } from 'constructs'; | ||
|
|
||
| export class AppsyncEventsLambdaStack extends cdk.Stack { | ||
| constructor(scope: Construct, id: string, props?: cdk.StackProps) { | ||
| super(scope, id, props); | ||
|
|
||
| // Lambda handler for event processing | ||
| const eventFn = new lambda.Function(this, 'EventHandlerFn', { | ||
| runtime: lambda.Runtime.NODEJS_22_X, | ||
| handler: 'index.handler', | ||
| code: lambda.Code.fromAsset('src'), | ||
| timeout: cdk.Duration.seconds(10), | ||
| logRetention: logs.RetentionDays.ONE_WEEK, | ||
| }); | ||
|
|
||
| // IAM role for AppSync to invoke Lambda | ||
| const appsyncRole = new iam.Role(this, 'AppSyncLambdaRole', { | ||
| assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'), | ||
| }); | ||
| eventFn.grantInvoke(appsyncRole); | ||
|
|
||
| // IAM role for AppSync to push logs to CloudWatch | ||
| const logsRole = new iam.Role(this, 'ApiLogsRole', { | ||
| assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'), | ||
| managedPolicies: [ | ||
| iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSAppSyncPushToCloudWatchLogs'), | ||
| ], | ||
| }); | ||
|
|
||
| // AppSync Events API with unique name derived from stack name | ||
| const api = new appsync.CfnApi(this, 'EventsApi', { | ||
| name: `${cdk.Aws.STACK_NAME}-EventsApi`, | ||
| eventConfig: { | ||
| authProviders: [{ authType: 'API_KEY' }], | ||
| connectionAuthModes: [{ authType: 'API_KEY' }], | ||
| defaultPublishAuthModes: [{ authType: 'API_KEY' }], | ||
| defaultSubscribeAuthModes: [{ authType: 'API_KEY' }], | ||
| logConfig: { | ||
| logLevel: 'INFO', | ||
| cloudWatchLogsRoleArn: logsRole.roleArn, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| // API key with 365-day expiry | ||
| const apiKey = new appsync.CfnApiKey(this, 'EventsApiKey', { | ||
| apiId: api.attrApiId, | ||
| expires: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60, | ||
| }); | ||
|
|
||
| // Channel namespace wired to Lambda event handler | ||
| new appsync.CfnChannelNamespace(this, 'NotificationsChannel', { | ||
| apiId: api.attrApiId, | ||
| name: 'notifications', | ||
| publishAuthModes: [{ authType: 'API_KEY' }], | ||
| subscribeAuthModes: [{ authType: 'API_KEY' }], | ||
| codeHandlers: "import { util } from '@aws-appsync/utils';\nexport function onPublish(ctx) {\n return { events: ctx.events };\n}", | ||
| }); | ||
|
|
||
| // Second channel namespace | ||
| new appsync.CfnChannelNamespace(this, 'AlertsChannel', { | ||
| apiId: api.attrApiId, | ||
| name: 'alerts', | ||
| publishAuthModes: [{ authType: 'API_KEY' }], | ||
| subscribeAuthModes: [{ authType: 'API_KEY' }], | ||
| }); | ||
|
|
||
| new cdk.CfnOutput(this, 'HttpEndpoint', { value: cdk.Fn.getAtt(api.logicalId, 'Dns.Http').toString() }); | ||
| new cdk.CfnOutput(this, 'RealtimeEndpoint', { value: cdk.Fn.getAtt(api.logicalId, 'Dns.Realtime').toString() }); | ||
| new cdk.CfnOutput(this, 'ApiId', { value: api.attrApiId }); | ||
| new cdk.CfnOutput(this, 'ApiKeyValue', { value: apiKey.attrApiKey }); | ||
| new cdk.CfnOutput(this, 'FunctionName', { value: eventFn.functionName }); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| { | ||
| "name": "appsync-events-lambda-cdk", | ||
| "version": "1.0.0", | ||
| "bin": { "app": "bin/app.js" }, | ||
| "scripts": { "build": "tsc", "cdk": "cdk" }, | ||
| "dependencies": { | ||
| "aws-cdk-lib": "^2.180.0", | ||
| "constructs": "^10.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "typescript": "~5.4.0", | ||
| "@types/node": "^20.0.0" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| exports.handler = async (event) => { | ||
| const { events } = event; | ||
|
|
||
| if (!events || !Array.isArray(events)) { | ||
| return { events: [{ payload: { error: 'No events received' } }] }; | ||
| } | ||
|
|
||
| const processed = events.map(e => { | ||
| let payload; | ||
| try { | ||
| payload = JSON.parse(e.payload); | ||
| } catch (err) { | ||
| return { payload: JSON.stringify({ error: 'Invalid JSON payload' }) }; | ||
| } | ||
| return { | ||
| payload: JSON.stringify({ | ||
| ...payload, | ||
| processedAt: new Date().toISOString(), | ||
| enriched: true, | ||
| messageId: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, | ||
| }), | ||
| }; | ||
| }); | ||
|
|
||
| return { events: processed }; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "compilerOptions": { | ||
| "target": "ES2020", | ||
| "module": "commonjs", | ||
| "lib": ["es2020"], | ||
| "strict": true, | ||
| "noImplicitAny": true, | ||
| "esModuleInterop": true, | ||
| "outDir": "build", | ||
| "declaration": true | ||
| }, | ||
| "exclude": ["node_modules", "cdk.out"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # 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. | ||
|
|
||
| > **Note:** CloudTrail data events are billed separately from management events. See [AWS CloudTrail Pricing](https://aws.amazon.com/cloudtrail/pricing/) for current data event rates. | ||
|
|
||
| ## 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 20+](https://nodejs.org/en/download/) installed | ||
|
|
||
| ## Deployment Instructions | ||
|
|
||
| 1. Clone and navigate to the pattern: | ||
| ``` | ||
| cd serverless-patterns/eventbridge-cloudtrail-dataplane-cdk | ||
| npm install | ||
| ``` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a |
||
| 2. Bootstrap CDK (first-time CDK users only): | ||
| ``` | ||
| cdk bootstrap | ||
| ``` | ||
| 3. Deploy: | ||
| ``` | ||
| cdk deploy | ||
| ``` | ||
|
|
||
| ## How it works | ||
|
|
||
| - A CloudTrail trail is created with data event logging enabled for EventBridge event buses (`AWS::Events::EventBus`) | ||
| - 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "app": "npx ts-node --prefer-ts-exts bin/app.ts" | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recommendation: Mention that CloudTrail data events are billed separately and link pricing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a callout box in the README noting CloudTrail data events are billed separately, with a link to the pricing page.