Skip to content

Commit

Permalink
Performing initial commit for terraform pattern of delayed-eventbridg…
Browse files Browse the repository at this point in the history
…e-events
  • Loading branch information
farrjam committed Jul 13, 2024
1 parent 99dc671 commit 11815f3
Show file tree
Hide file tree
Showing 5 changed files with 416 additions and 0 deletions.
65 changes: 65 additions & 0 deletions delayed-eventbridge-events-terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Delayed EventBridge events with EventBridge Scheduler

This pattern listens for EventBridge events, processes them and creates schedules for every user. 24 hours after user has been created a schedule is run that publishes events directly into EventBridge. The pattern is deployed using Terraform.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/delayed-eventbridge-events-terraform.

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. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```
1. Change directory to the pattern directory:
```
cd delayed-eventbridge-events-terraform
```
1. From the command line, initialize Terraform:
```
terraform init
```
1. From the commend line, apply the configuration in the main.tf file and follow the prompts:
```
terraform apply
```

## How it works

We assume we have a business requirement to email a customer 24 hours after they have signed up.

1. `UserCreated` event is triggered. In this example we assume a `UserCreated` event is triggered into our event bus.
2. EventBridge Rule setup to listen to the `UserCreated` event.
3. Lambda function listens to `UserCreated` event and creates an Amazon EventBridge Schedule for 24 hours in the future (2 mins for development mode).
4. 24 hours pass, and schedule is triggered and raises `UserCreated24HoursAgo` event directly into EventBridge..
5. Consumers listen for event and process it. An example would be email a welcome message to customers or an offer etc.

## Testing

Use the [AWS CLI](https://aws.amazon.com/cli/) to send a test event to EventBridge:

1. Send an event to EventBridge:
```bash
aws events put-events --entries "Source='myapp.users',DetailType='UserCreated',Detail='{\"id\": \"test-customer-id\", \"firstName\": \"FirstName\", \"lastName\": \"LastName\"}',EventBusName='<custom-event-bus-ARN>'"
```

2. Check the CloudWatch metrics of `UserCreatedRule` and `UserCreated24HoursAgoRule` EventBridge rule and `SchedulesForUsers24HoursAfterCreation` EventBridge Scheduler Group. Also, check the CloudWatch Logs of Lambda Functions which are targets of corresponding `UserCreatedRule` and `UserCreated24HoursAgoRule` EventBridge rules.

## Cleanup

1. Delete all created resources and follow prompts:
```
terraform destroy
```
----
Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
52 changes: 52 additions & 0 deletions delayed-eventbridge-events-terraform/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"title": "Delayed EventBridge events with EventBridge Scheduler",
"description": "Setup future EventBridge events with EventBridge Scheduler",
"language": "",
"level": "200",
"framework": "Terraform",
"introBox": {
"headline": "How it works",
"text": [
"This pattern listens for EventBridge events, processes them and creates schedules for every user. 24 hours after user has been created a schedule is run that publishes events directly into EventBridge."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/delayed-eventbridge-events-terraform",
"templateURL": "serverless-patterns/delayed-eventbridge-events-terraform",
"projectFolder": "delayed-eventbridge-events-terraform",
"templateFile": "delayed-eventbridge-events-terraform/main.tf"
}
},
"resources": {
"bullets": [
{
"text": "What is Amazon EventBridge Scheduler?",
"link": "https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html"
}
]
},
"deploy": {
"text": [
"terraform apply"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: <code>terraform destroy</code>."
]
},
"authors": [
{
"name": "Farrukh Jamal",
"image": "https://media.licdn.com/dms/image/C5103AQHH_uSeGxOlZw/profile-displayphoto-shrink_400_400/0/1570698220038?e=1726099200&v=beta&t=7HUDKvPyM5GbBA1eOhVGB9POwFhXC3v87-vrqCIk1d0",
"bio": "Cloud Engineer at AWS based in the Australia.",
"linkedin": "https://www.linkedin.com/in/farrukhjamal/"
}
]
}
247 changes: 247 additions & 0 deletions delayed-eventbridge-events-terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.57"
}
}

required_version = ">= 0.14.9"
}

provider "aws" {
profile = "default"
region = "us-east-1"
}

# Create a new eventbus for our demo
resource "aws_cloudwatch_event_bus" "custom_event_bus" {
name = "MyCustomBus"
}

# need to create role and policy for scheduler to put events onto our bus
resource "aws_iam_role" "scheduler_role" {
name = "delayed-eb-events-SchedulerRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = {
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
Service = "scheduler.amazonaws.com"
}
}
})

inline_policy {
name = "ScheduleToPutEvents"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"events:PutEvents",
],
Resource = [aws_cloudwatch_event_bus.custom_event_bus.arn]
},
]
})
}
}

# Creating scheduler group
resource "aws_scheduler_schedule_group" "scheduler_schedule_group" {
name = "SchedulesForUsers24HoursAfterCreation"
}

data "aws_iam_policy" "lambda_basic_execution_role_policy" {
name = "AWSLambdaBasicExecutionRole"
}


# Listen to UserCreated and create the schedule
resource "aws_iam_role" "user_created_function_role" {
name = "delayed-eb-events-UserCreatedFunctionRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = {
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
Service = "lambda.amazonaws.com"
}
}
})

managed_policy_arns = [
data.aws_iam_policy.lambda_basic_execution_role_policy.arn
]

inline_policy {
name = "CreateSchedule"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"scheduler:CreateSchedule",
"iam:PassRole"
],
Resource = ["*"]
},
]
})
}
}

data "archive_file" "process_user_created_function_zip_file" {
type = "zip"
source_file = "${path.module}/src/process-user-created/index.mjs"
output_path = "${path.module}/process_user_created_function_payload.zip"
}

resource "aws_lambda_function" "process_user_created_lambda" {
filename = data.archive_file.process_user_created_function_zip_file.output_path
function_name = "ProcessUserCreatedFunction"
role = aws_iam_role.user_created_function_role.arn
handler = "index.handler"

source_code_hash = data.archive_file.process_user_created_function_zip_file.output_base64sha256

runtime = "nodejs20.x"

environment {
variables = {
SCHEDULE_ROLE_ARN = aws_iam_role.scheduler_role.arn,
EVENTBUS_ARN = aws_cloudwatch_event_bus.custom_event_bus.arn
}
}
}

# EventBridge rule to listen to UserCreated so we can process it and create schedule
resource "aws_cloudwatch_event_rule" "user_created_rule" {
name = "UserCreatedRule"
description = "Listen to UseCreated events"

event_pattern = jsonencode({
source = ["myapp.users"],
detail-type = [
"UserCreated"
]
})
event_bus_name = aws_cloudwatch_event_bus.custom_event_bus.arn
}

resource "aws_cloudwatch_event_target" "user_created_rule_target" {
rule = aws_cloudwatch_event_rule.user_created_rule.name
arn = aws_lambda_function.process_user_created_lambda.arn
event_bus_name = aws_cloudwatch_event_bus.custom_event_bus.name
}

resource "aws_lambda_permission" "allow_eventbridge" {
statement_id = "AllowExecutionFromEventBridge"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.process_user_created_lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.user_created_rule.arn
}

# Function that listens to schedule directly onto EventBridge and "email a customer" as an example

resource "aws_iam_role" "email_customer_function_role" {
name = "delayed-eb-events-EmailCustomerFunctionRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = {
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = {
Service = "lambda.amazonaws.com"
}
}
})

managed_policy_arns = [
data.aws_iam_policy.lambda_basic_execution_role_policy.arn
]
}

data "archive_file" "email_customer_function_zip_file" {
type = "zip"
source_file = "${path.module}/src/email-customer/index.mjs"
output_path = "${path.module}/email_customer_function_payload.zip"
}

resource "aws_lambda_function" "email_customer_lambda" {
filename = data.archive_file.email_customer_function_zip_file.output_path
function_name = "EmailCustomerFunction"
role = aws_iam_role.email_customer_function_role.arn
handler = "index.handler"

source_code_hash = data.archive_file.email_customer_function_zip_file.output_base64sha256

runtime = "nodejs20.x"
}

# Rule to match schedules for users and attach our email customer lambda
resource "aws_cloudwatch_event_rule" "user_created_24hours_ago_rule" {
name = "UserCreated24HoursAgoRule"
description = "Listen to UseCreated events"

event_pattern = jsonencode({
source = ["scheduler.notifications"],
detail-type = [
"UserCreated24HoursAgo"
]
})
event_bus_name = aws_cloudwatch_event_bus.custom_event_bus.arn
}

resource "aws_cloudwatch_event_target" "user_created_24hours_ago_rule_target" {
rule = aws_cloudwatch_event_rule.user_created_24hours_ago_rule.name
arn = aws_lambda_function.email_customer_lambda.arn
event_bus_name = aws_cloudwatch_event_bus.custom_event_bus.name
}

resource "aws_lambda_permission" "allow_eventbridge_email_customer_lambda" {
statement_id = "AllowExecutionFromEventBridge"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.email_customer_lambda.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.user_created_24hours_ago_rule.arn
}

# Outputs
output "custom_event_bus_arn" {
description = "Custom Event Bus ARN"
value = aws_cloudwatch_event_bus.custom_event_bus.arn
}

output "user_created_rule_arn" {
description = "UserCreatedRule rule ARN"
value = aws_cloudwatch_event_rule.user_created_rule.arn
}

output "user_created_24hours_ago_rule_arn" {
description = "UserCreated24HoursAgoRule rule ARN"
value = aws_cloudwatch_event_rule.user_created_24hours_ago_rule.arn
}

output "scheduler_group_name" {
description = "SchedulesForUsers24HoursAfterCreation scheduler group name"
value = aws_scheduler_schedule_group.scheduler_schedule_group.name
}

output "process_user_created_lambda_function_log_group" {
description = "ProcessUserCreatedFunction CloudWatch Log Group"
value = "/aws/lambda/${aws_lambda_function.process_user_created_lambda.function_name}"
}

output "email_customer_lambda_function_log_group" {
description = "EmailCustomerFunction CloudWatch Log Group"
value = "/aws/lambda/${aws_lambda_function.email_customer_lambda.function_name}"
}


12 changes: 12 additions & 0 deletions delayed-eventbridge-events-terraform/src/email-customer/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const handler = async (event) => {

// Received EventBridge Scheduler schedule event
console.log('Received event:', JSON.stringify(event, null, 2));


/**
* Example of a consumer for the UserCreated24HoursAgo event, if you wanted to email them you could put your email code here
*/

console.log('Email the customer 24 hours ago after they were created.')
};
Loading

0 comments on commit 11815f3

Please sign in to comment.