Skip to content
Merged
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
78 changes: 78 additions & 0 deletions python/eventbridge-mesh/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# CDK Python Sample: Event Bridge mesh with CDK

![Language-Support: Stable](https://img.shields.io/badge/language--support-stable-success.svg?style=for-the-badge)

## Description:
A CDK way to set up a Event Bridge Mesh(Cross-Account), where you relay the messages from one Event Bridge in a producer account to another Event Bridge in a consumer account

## Backgroud:
This is a CDK application that implements cross-account event routing using Amazon EventBridge. It's designed for enterprise scenarios where:

- Teams work in separate AWS accounts (producer and consumer)
- Consumer teams need autonomy to manage their event processing
- Event routing changes shouldn't require coordination with producer teams

## Solution:

### Single consumer
![architecture](./images/single-consumer.png)

### Multiple consumers
![architecture](./images/multi-consumers.png)


## Instructions

### CDK bootstrapping
To deploy the stacks in different accounts, you need to bootstrap the CDKToolkit with trust flag. Assuming you will run `cdk deploy` command from account ID: `123456789012`, and deploys producer stack to account ID: `111111111111`, and consumer stack to account ID: `222222222222`

1. In account: `111111111111`, run the below command:
```
cdk bootstrap aws://111111111111/us-east-1 \
--trust 123456789012 \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
```

2. In account: `222222222222`, run the below command:
```
cdk bootstrap aws://222222222222/us-east-1 \
--trust 123456789012 \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
```

3. (Optional: Only do this step, when you deploys multiple consumers solution) In account: `333333333333`, run the below command:
```
cdk bootstrap aws://333333333333/us-east-1 \
--trust 123456789012 \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
```

### Deploy Single consumer solution:
1. Run: `cd single-consumer`
2. Change the values of `producerAccountId` and `consumerAccountId` in `cdk.json`
3. Install and configure the CDK: https://docs.aws.amazon.com/CDK/latest/userguide/install_config.html
4. Make sure you use a role or user has proper permission in account ID: `123456789012`, and run below commands:
```shell
npm run build

cdk ls

cdk synth

cdk deploy --all
```

### Deploy Multiple consumers solution:
1. Run: `cd multiple-consumer`
2. Change the values of `producerAccountId`, `consumer1AccountId`, and `consumer2AccountId` in `cdk.json`
3. Install and configure the CDK: https://docs.aws.amazon.com/CDK/latest/userguide/install_config.html
4. Make sure you use a role or user has proper permission in account ID: `123456789012`, and run below commands:
```shell
npm run build

cdk ls

cdk synth

cdk deploy --all
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions python/eventbridge-mesh/multiple-consumers/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
from aws_cdk import (
aws_events as events,
aws_events_targets as targets,
aws_logs as logs,
aws_iam as iam,
App, Stack, Environment
)
from constructs import Construct

class ProducerStack(Stack):
def __init__(self, scope: Construct, id: str, *,
app_name: str,
consumer_accounts: list[str],
**kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Create the EventBus
producer_event_bus = events.EventBus(
self, f"{app_name}-producer-event-bus"
)

# Create rules for each consumer account
for index, consumer_account_id in enumerate(consumer_accounts):
# Create rule to forward events to consumer account
rule = events.Rule(
self,
f"{app_name}-forward-to-consumer-{index + 1}-rule",
event_bus=producer_event_bus,
event_pattern=events.EventPattern(
source=['com.myapp.events']
)
)

# Add target to forward to consumer account's event bus
consumer_bus = events.EventBus.from_event_bus_arn(
self,
f"{app_name}-consumer-{index + 1}-event-bus",
f"arn:aws:events:{Stack.of(self).region}:{consumer_account_id}:event-bus/default"
)
rule.add_target(targets.EventBus(consumer_bus))


class ConsumerStack(Stack):
def __init__(self, scope: Construct, id: str, *,
app_name: str,
consumer_name: str,
producer_account_id: str,
**kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Create or reference the consumer event bus
consumer_event_bus = events.EventBus(
self, f"{app_name}-{consumer_name}-event-bus"
)

# Add policy to allow producer account to put events
consumer_event_bus.add_to_resource_policy(iam.PolicyStatement(
sid="allowProducerAccount",
effect=iam.Effect.ALLOW,
principals=[iam.AccountPrincipal(producer_account_id)],
actions=["events:PutEvents"],
resources=[consumer_event_bus.event_bus_arn]
))

# Create consumer rules
consumer_rule = events.Rule(
self,
f"{app_name}-{consumer_name}-rule",
event_bus=consumer_event_bus,
event_pattern=events.EventPattern(
source=['com.myapp.events'],
detail_type=['specific-event-type']
)
)

# Add target (e.g., CloudWatch)
log_group = logs.LogGroup(
self, f"{app_name}-{consumer_name}-logs"
)
consumer_rule.add_target(targets.CloudWatchLogGroup(log_group))


app = App()

# Get context values
app_name = app.node.try_get_context("appName")
region = app.node.try_get_context("region")
producer_account_id = app.node.try_get_context("producerAccountId")
consumer1_account_id = app.node.try_get_context("consumer1AccountId")
consumer2_account_id = app.node.try_get_context("consumer2AccountId")

# Create producer stack
producer_stack = ProducerStack(
app, f"{app_name}-producer-stack",
app_name=app_name,
consumer_accounts=[consumer1_account_id, consumer2_account_id],
env=Environment(
account=producer_account_id,
region=region
)
)

# Create consumer 1 stack
consumer1_stack = ConsumerStack(
app, f"{app_name}-consumer1-stack",
app_name=app_name,
consumer_name="consumer1",
producer_account_id=producer_account_id,
env=Environment(
account=consumer1_account_id,
region=region
)
)

# Create consumer 2 stack
consumer2_stack = ConsumerStack(
app, f"{app_name}-consumer2-stack",
app_name=app_name,
consumer_name="consumer2",
producer_account_id=producer_account_id,
env=Environment(
account=consumer2_account_id,
region=region
)
)

app.synth()
10 changes: 10 additions & 0 deletions python/eventbridge-mesh/multiple-consumers/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"app": "python3 app.py",
"context": {
"appName": "eventbridge-mesh",
"region": "us-east-1",
"producerAccountId": "111111111111",
"consumer1AccountId": "222222222222",
"consumer2AccountId": "333333333333"
}
}
2 changes: 2 additions & 0 deletions python/eventbridge-mesh/multiple-consumers/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws-cdk-lib==2.186.0
constructs>=10.0.0,<11.0.0
111 changes: 111 additions & 0 deletions python/eventbridge-mesh/single-consumer/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
from aws_cdk import (
aws_events as events,
aws_events_targets as targets,
aws_logs as logs,
aws_iam as iam,
App, Stack, Environment
)
from constructs import Construct

class ProducerStack(Stack):
def __init__(self, scope: Construct, id: str, *,
app_name: str,
consumer_account_id: str,
**kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Create the EventBus
producer_event_bus = events.EventBus(
self, f"{app_name}-producer-event-bus"
)

# Create rule to forward events to consumer account
rule = events.Rule(
self, f"{app_name}-forward-to-consumer-rule",
event_bus=producer_event_bus,
event_pattern=events.EventPattern(
source=['com.myapp.events']
)
)

# Add target to forward to consumer account's event bus
consumer_bus = events.EventBus.from_event_bus_arn(
self,
'ConsumerEventBus',
f"arn:aws:events:{Stack.of(self).region}:{consumer_account_id}:event-bus/default"
)
rule.add_target(targets.EventBus(consumer_bus))

# Optional: Add CloudWatch target for monitoring
log_group = logs.LogGroup(self, f"{app_name}-producer-logs")
rule.add_target(targets.CloudWatchLogGroup(log_group))


class ConsumerStack(Stack):
def __init__(self, scope: Construct, id: str, *,
app_name: str,
producer_account_id: str,
**kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Create or reference the consumer event bus
consumer_event_bus = events.EventBus(
self, f"{app_name}-consumer-event-bus"
)

# Add policy to allow producer account to put events
consumer_event_bus.add_to_resource_policy(iam.PolicyStatement(
sid="allowProducerAccount",
effect=iam.Effect.ALLOW,
principals=[iam.AccountPrincipal(producer_account_id)],
actions=["events:PutEvents"],
resources=[consumer_event_bus.event_bus_arn]
))

# Create consumer rules
consumer_rule = events.Rule(
self, f"{app_name}-consumer-rule",
event_bus=consumer_event_bus,
event_pattern=events.EventPattern(
source=['com.myapp.events'],
detail_type=['specific-event-type']
)
)

# Add target (e.g., CloudWatch)
log_group = logs.LogGroup(self, f"{app_name}-consumer-logs")
consumer_rule.add_target(targets.CloudWatchLogGroup(log_group))


app = App()

# Get context values
app_name = app.node.try_get_context("appName")
region = app.node.try_get_context("region")
producer_account_id = app.node.try_get_context("producerAccountId")
consumer_account_id = app.node.try_get_context("consumer1AccountId")

# Create producer stack
producer_stack = ProducerStack(
app, f"{app_name}-producer-stack",
app_name=app_name,
consumer_account_id=consumer_account_id,
env=Environment(
account=producer_account_id,
region=region
)
)

# Create consumer stack
consumer_stack = ConsumerStack(
app, f"{app_name}-consumer-stack",
app_name=app_name,
producer_account_id=producer_account_id,
env=Environment(
account=consumer_account_id,
region=region
)
)

app.synth()
10 changes: 10 additions & 0 deletions python/eventbridge-mesh/single-consumer/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"app": "python3 app.py",
"context": {
"appName": "eventbridge-mesh",
"region": "us-east-1",
"producerAccountId": "111111111111",
"consumer1AccountId": "222222222222"
}

}
2 changes: 2 additions & 0 deletions python/eventbridge-mesh/single-consumer/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws-cdk-lib==2.186.0
constructs>=10.0.0,<11.0.0
11 changes: 11 additions & 0 deletions typescript/eventbridge-mesh/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out

package-lock.json

6 changes: 6 additions & 0 deletions typescript/eventbridge-mesh/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
Loading
Loading