DEV Community

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

Posted on

12

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 ๐Ÿ˜Š๐Ÿ’

Postmark Image

"Please fix this..."

Focus on creating stellar experiences without email headaches. Postmark's reliable API and detailed analytics make your transactional emails as polished as your product.

Start free

Top comments (0)

Image of Quadratic

Free AI chart generator

Upload data, describe your vision, and get Python-powered, AI-generated charts instantly.

Try Quadratic free

๐Ÿ‘‹ Kindness is contagious

Dive into this informative piece, backed by our vibrant DEV Community

Whether youโ€™re a novice or a pro, your perspective enriches our collective insight.

A simple โ€œthank youโ€ can lift someoneโ€™s spiritsโ€”share your gratitude in the comments!

On DEV, the power of shared knowledge paves a smoother path and tightens our community ties. Found value here? A quick thanks to the author makes a big impact.

Okay