Receive emails in Rails app using AWS Lambda & SES

John Harlan, 9 months ago

Here is a basic Ruby on Rails application demonstrating exactly how to receive emails from an Amazon Web Services (AWS) Lambda process. This method works for any Rails app where you want users to be able to send emails into your Rails app.

Setup AWS S3, Lambda, and SES

We will begin with getting all the pieces in place in our AWS account for receiving emails, storing them in an S3 bucket, and setting up a Lambda function to post to our new Rails app.

S3 Bucket for SES to Store Emails

First, you will need to create a standard S3 bucket with a custom configuration for SES to store emails. You can do this by:

  1. Creating an S3 Bucket
  2. Updating the permissions rules with the following:

AWS Lambda function to process incoming mail

    1. Click on "Lambda" from your AWS Console
    2. Click on "Create New Function" from the Lambda console
    3. On the "Select Blueprint" screen, click "Skip" at the bottom right
    4. Enter a name and description for your Lambda function and paste the following code. Remember, you must replace the domain 'http://example.com' with the domain where you are deploying.
    5.  

Image of Lambda Setup

    1. For Role, select basic execution role and a new tab will open inside your IAM console asking to create this new role.

Image of New Role

  1. (Optional) Choose a VPC for where to deploy the function.
  2. Finally, click "Create Function."

You also have the option to save and test the function, which will be helpful in making sure everything is working in production once you get your Rails app deployed.

Setting up AWS SES to receive email

Setup the domain for which you want to receive email. CrateBind typically uses a subdomain so that it doesn't interfere with our normal company email @cratebind.com.

Image of SES New Domain

Next, you should also update your DNS record to point the subdomain MX records to SES. The Value field depends on the AWS region in which your SES domain was set up. For our account, we are using us-west-2 so our MX records would be as follows: Image of Route 53 Setup

 

Lastly, you will create a new default rule in SES for how to process incoming messages.

    1. Go to SES in your AWS Dashboard.
    2. Click on "Rule Sets."
    3. Click "Create Rule Set."
    4.  

Create Rule Set

    1. Once you have created the Rule Set, now you need to create a Rule.
    2. In the Create Rule screen, add the recipient. You can read the instructions on this screen for how to setup a wildcard rule. For our case, we will just create a recipient for recipient@emails.cratebind.com.
    3.  

Create Rule Set

    1. Configure the actions for the rule. Here are the steps: (1) save email to S3, (2) trigger our AWS Lambda function and (3) stop processing.
    2.  

SES Configuration

  1. You will need to update your specified S3 bucket with the following permission set in order for SES to be able to put the messages there (instructions above).

Also, here are some additional instructions on setting up SES to receive emails if you get stuck: AWS SES Email Receiving Tutorial.

Setup Your Rails App

Generate model for receiving emails

Create a basic model and controller that will receive the post response from AWS Lambda and save the email in the database.

Model Method

We will use Webmock and stub data to test that we have the correct controller and model actions in place to process the data we receive from AWS Lambda. Our basic setup will be for our AWS Lambda function to store the incoming message in our S3 bucket and then post the message id to our Rails app. So, we will first build a class method in our Email model that takes the message_id and pulls the email from the S3 bucket, saving it to our database. We have created a example message that represents the text file saved by AWS SES to S3 and we will write a test that stubs this message from the get request to our S3 bucket in spec/models/email_spec.rb. This is the Webmock setting to stub the request to S3:

And this is the model method that we have built to handle the incoming messages:

Controller Action

Next, we will build a test to spec the controller action that receives the POST from AWS Lambda, validating the token and passing the message_id to the model method we just built and tested. The controller action looks like this. You obviously will want to create a different token than just 'test_token' and you will want to set it as an ENV variable.

 

Like What you see? We should talk!