DEV Community

Cover image for Send Scheduled Emails Using Amazon SES & EventBridge - (Let's Build πŸ—οΈ Series)
awedis for AWS Heroes

Posted on

Send Scheduled Emails Using Amazon SES & EventBridge - (Let's Build πŸ—οΈ Series)

One of the key aspects to automate is user notifications, especially for recurring events or updates.

In modern applications, decoupling solutions is crucial. A decoupled architecture not only allows for faster upgrades but also enhances scalability, resilience, and ease of maintenance.

In this article, we’ll build a simple application that sends an email to the user every morning at 8:00 AM. For this example, we'll keep the content straightforward: a simple 'Good Morning!' message. Without further ado, let’s dive into the cool part.

First, we'll set up an Amazon EventBridge to handle the scheduling. If you'd like to learn more about EventBridge, check out this article: Schedule Lambda Functions Using EventBridge | Serverless

Here’s a Terraform code snippet that schedules an AWS EventBridge rule to trigger a Lambda function every day at 8:00 AM:

terraform {
  required_version = "1.5.1"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.22.0"
    }
  }
}

provider "aws" {
  region = var.region
}

resource "aws_lambda_function" "email_lambda" {
  function_name = "email_lambda"
  handler       = "main.lambda_handler"
  runtime       = "python3.8"

  role          = aws_iam_role.lambda_exec_role.arn
  filename      = "main.zip"
  source_code_hash = filebase64sha256("main.zip")
}

resource "aws_iam_role" "lambda_exec_role" {
  name = "lambda_exec_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_exec_policy" {
  role       = aws_iam_role.lambda_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_cloudwatch_event_rule" "every_day_8am" {
  name                = "every_day_8am"
  schedule_expression = "cron(0 8 * * ? *)"
}

resource "aws_cloudwatch_event_target" "trigger_lambda" {
  rule      = aws_cloudwatch_event_rule.every_day_8am.name
  target_id = "lambda_target"
  arn       = aws_lambda_function.email_lambda.arn
}

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

resource "aws_iam_policy" "lambda_policy" {
  name        = "lambda_policy"
  description = "Policy to allow Lambda to send emails using SES"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action   = "ses:SendEmail"
      Effect   = "Allow"
      Resource = "*"
    }]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_policy_attachment" {
  role       = aws_iam_role.lambda_exec_role.name
  policy_arn = aws_iam_policy.lambda_policy.arn
}
Enter fullscreen mode Exit fullscreen mode

πŸ“‹ Note: Ensure that the handler configuration in your Lambda function matches the function signature. If your file is named lambda_function.py, the handler should be configured as lambda_function.lambda_handler.

Here's how you can write a Python script that sends an email using AWS SES inside an AWS Lambda function:

import boto3
from botocore.exceptions import ClientError

def lambda_handler(event, context):
  ses_client = boto3.client('ses', region_name='eu-west-1')

  sender_email = "your sender email"
  recipient_email = "your receiver email"
  subject = "Good Morning!"
  body_text = "Good Morning!"

  email = {
    'Source': sender_email,
    'Destination': {
      'ToAddresses': [
          recipient_email,
      ],
    },
    'Message': {
      'Subject': {
        'Data': subject,
      },
      'Body': {
        'Text': {
          'Data': body_text,
        },
      },
    },
  }

  try:
    response = ses_client.send_email(**email)
    print(f"Email sent! Message ID: {response['MessageId']}")
  except ClientError as e:
    print(f"Error sending email: {e.response['Error']['Message']}")

  return {
    'statusCode': 200,
    'body': 'Email sent successfully'
  }
Enter fullscreen mode Exit fullscreen mode

πŸ“‹ Note: Make sure to verify your sender email as well before.

Here is a picture of success once the email was sent:

Image description

Here is another picture of EventBridge triggering every morning the Lambda function:

Image description

Conclusion

Scheduling jobs is crucial, especially as more tasks require automation. I hope this article serves as a helpful starting point and inspires you to build something truly amazing from it.

If you did like my content, and want to see more, feel free to connect with me on ➑️ LinkedIn, happy to guide or help with anything that needs clarification πŸ˜ŠπŸ’

Top comments (0)