How to deploy a Python Lambda Function with Terraform to send Emails via AWS SES

- 6 min read

Deploying a Python Lambda Function with Terraform to send Emails via AWS SES

In this article you will learn how to set up a Python Lambda function for sending emails via AWS SES with Terraform

Email communication remains a critical part of customer interaction in the digital age. AWS Simple Email Service (SES) offers a robust platform for handling outbound communications, but integrating this service efficiently within your infrastructure is key. In this guide, we'll explore how to deploy a Python AWS Lambda function for sending emails via AWS SES, simplifying the process and enabling better manageability.

Introduction to AWS Lambda and SES with Terraform

AWS Lambda provides a serverless compute service that abstracts server management for you, scaling automatically and only charging for compute time consumed. Combined with AWS SES for email delivery, it can automate email sending without the need for managing servers. Terraform, our tool of choice, allows us to define our AWS resources in code, creating a repeatable, and predictable deployment process.

Prerequisites

  • An AWS account and credentials with necessary permissions
  • Basic understanding of AWS services (Lambda, SES, and IAM).
  • Familiarity with Terraform.
  • A forked copy of the GitHub repository containing the Terraform and Python code.

Setting Up AWS SES with Terraform

Terraform's infrastructure as code allows us to define resources such as SES domain identities and email sending policies. Below is a breakdown of the Terraform configuration you'll use:

version.tf sets the AWS provider and Terraform versions to ensure compatibility.

#version.tf provider "aws" { region = var.aws_region } terraform { required_version = ">= 1.6.2" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.23.1" } } }

variables.tf defines variables such as the AWS region and verified email address.

# variables.tf variable "aws_region" { type = string default = "eu-central-1" } variable "verified_email" { type = string }

ses.tf creates the SES email identity and an email template resource, essential for sending emails. Note: You need to create a demo html file and save it in terraform/static/demo_template.html in order to be able to execute the terraform code.

# ses.tf resource "aws_ses_email_identity" "semplates_email_identity" { email = var.verified_email } resource "aws_ses_template" "semplates_demo_template" { name = "SEMPLATES_DEMO_TEMPLATE" subject = "Semplates Test Lambda Function" html = file("${path.module}/static/demo_template.html") }

lambda.tf sets up the Lambda function with the necessary IAM policies to allow sending emails through SES. Note: make sure your python code can be found in the given source path ../lambdas/python to work with this example.

# lambda.tf data "aws_iam_policy_document" "ses_send_templated_email_policy" { statement { effect = "Allow" actions = [ "ses:SendTemplatedEmail" ] resources = [ aws_ses_email_identity.semplates_email_identity.arn, aws_ses_template.semplates_demo_template.arn ] } statement { effect = "Allow" actions = [ "ses:GetTemplate", ] resources = [aws_ses_template.semplates_demo_template.arn] } } resource "aws_iam_policy" "ses_send_templated_email" { name = "SESSendTemplatedEmailPolicy" description = "Policy to allow SES sendTemplatedEmail only." policy = data.aws_iam_policy_document.ses_send_templated_email_policy.json } module "lambda_function" { source = "terraform-aws-modules/lambda/aws" function_name = "PythonSesEmailSender" source_path = "${path.module}/../lambdas/python/" handler = "main.lambda_handler" runtime = "python3.11" attach_policy = true policy = aws_iam_policy.ses_send_templated_email.arn }

Adding the Python code for the Lambda Function The Lambda function is where the magic happens. The provided Python script, main.py, uses the AWS SDK to send templated emails. It includes the send_templated_email function that takes parameters like the receiver's email, the SES template name, and template data. Here's a portion of the Python script for context:

import json import boto3 from botocore.exceptions import ClientError # Initialize the SES client. # If you're running the function locally, make sure your AWS credentials are set up (e.g., via `~/.aws/credentials` # If you're running this on AWS Lambda, ensure the execution role has permissions to use SES. ses_client = boto3.client('ses') # Adjust the region if necessary. def send_templated_email(receiver_email: str, template_name: str, sender_email: str, template_data: dict): try: response = ses_client.send_templated_email( Source=sender_email, Destination={ 'ToAddresses': [ receiver_email, ], }, Template=template_name, TemplateData=json.dumps(template_data) ) except ClientError as e: print(e.response['Error']['Message']) return { "statusCode": 400, "body": json.dumps({'error_message': e.response['Error']['Message']}) } print("Email sent! Message ID:", response['MessageId']) return { "statusCode": 200, "body": json.dumps({'message': 'Email sent successfully!'}) } def lambda_handler(event, context): try: receiver_email = event["receiver_email"] sender_email = event["sender_email"] template_name = event["template_name"] placeholders = event["placeholders"] return send_templated_email(receiver_email, template_name, sender_email, placeholders) except Exception as e: return { "statusCode": 400, "body": json.dumps({'error_message': f'Missing key in input: {str(e)}'}) }

Make sure to also add the requirements.txt file in the python folder with the following contents

boto3==1.28.73

Your folder structure should now look like this:

├── lambdas │ └── python │ └── main.py # Python script for Lambda function │ └── requirements.txt # Python dependencies ├── terraform ├── lambda.tf # Terraform configuration for Lambda ├── ses.tf # Terraform configuration for SES ├── variables.tf # Terraform variable definitions ├── versions.tf # Terraform provider and version specifications └── static └── demo_template.html # HTML template for SES

Deploying your Lambda Function

So lets deploy the above code to our AWS account. For this, we simply need to apply the terraform code. Make sure your AWS Credentials are set correctly in your local environment. For this, you need to have the aws cli installed (on MacOS: brew install awscli) and runaws configure. Enter your Access Key Id, Access Secret Key and AWS Region. If this is set up correctly, you can change to the terraform directory ( cd terraform ) and then run terraform apply. Terraform will show you the changes that will be applied. If all seems correct, enter yes to apply the changes.

Testing Your Deployment

After deploying, you can send a test email by invoking the Lambda function with a test event. If everything is configured correctly, the Lambda function will send an email using the SES template and you will receive a successful response. Here are the steps to achieve this:

Step 1: After email verification, you will need to sign in to the AWS Management Console and check your AWS Lambda Functions. Select the one named PythonSesEmailSender. 

Step 2: Select the test tab and enter the following Event JSON:

{ "receiver_email": "<receiver-email>", "sender_email": "<sender-email>", "template_name": "SEMPLATES_DEMO_TEMPLATE", "placeholders": { "FIRST_NAME": "Jonathan", "LAST_NAME": "Doe" } }

Step 3: Hit the test button. The result should show:

{ "statusCode": 200, "body": { "message": "Email sent successfully!" } }

Clean up after testing

In case you want to revert your changes within your AWS account, you can do this with a single terraform command. terraform destroy removes all resources that are defined and reverts the changes we have done to the AWS Account, removing the Lambda function, policy, and AWS SES setup. 

Handling Email Templates: Beyond Terraform

While Terraform aids in the initial setup of AWS SES templates, the article's emphasis on using Semplates for ongoing template management is worth noting. Managing templates through Terraform implies that changes must go through a development pipeline, creating a potential bottleneck. Semplates offers a more collaborative and efficient approach to template management. It enables non-developer teams, such as design and product teams, to create, update, and manage email templates directly.

The Benefits of Semplates

  • Improved Collaboration: Allows design and product teams to contribute directly to the template creation and modification process.
  • Increased Productivity: Developers are freed from template management tasks, allowing them to focus on product development.
  • Ease of Use: A graphical user interface makes it simpler to manage templates without dealing with AWS CLI or scripting.
  • Direct Email Testing: Enables sending test emails from the interface, streamlining the testing process.

The Shift in Workflow

Adopting Semplates signifies a shift from a developer-centric workflow to a more inclusive, cross-functional process. This shift not only enhances efficiency but also reduces the time and cost associated with email template management.

Conclusion

Deploying a Python Lambda function on AWS for email sending through SES is a task made simple with the provided Terraform setup. While Terraform streamlines the initial deployment, Semplates introduces a paradigm shift in managing SES templates, fostering a collaborative environment and optimizing the workflow for sending transactional emails. If you don't want to set all this up by yourself, make sure to fork the GitHub repository, set your environment variables, and deploy and test the Lambda function directly. Make sure to check the Readme within the GitHub project before applying the terraform code.

Create Great Email Templates on Amazon SES. Use Semplates.

Our discover plan is free forever. No credit card required.
Need more functionalities? You can upgrade anytime.

🍪

Our cookie policy

We use cookie technology to analyse the website traffic and page usage, improve the navigation experience and support our marketing efforts. If you accept, you grant us permission to store cookies on your device for those purposes only.
Please read our Data Privacy Policy for more information.

Accept all

Only necessary