osullic
osullic

Reputation: 612

Based on AWS SES receipt rule, execute Lambda function which sends an email using SES

I am having trouble getting an AWS Lambda function to do what I want. I only started with Lambda functions yesterday.

What I'm trying to achieve relates to a personal website. I have my own domain, attached to a website that is hosted on an AWS EC2 instance. If someone sends an email to any address at my domain, I want that email to end up in my personal Gmail inbox.

I have set a DNS MX record for my domain that directs email to AWS SES. My domain (and my personal Gmail address) is verified in SES, and I have a receipt rule that writes the email into a S3 bucket. That all works as expected - I can see the emails in the S3 bucket.

Now, I have a second receipt rule that invokes a Lambda function that should grab the email out of the S3 bucket, and again call SES to send the email contents to my personal Gmail inbox. This part isn't working.

screenshot of AWS SES Receipt Rules

Since I'm new to Lambda functions, and they run "in the cloud", I'm finding this hard to debug. I've made the Lambda function as simple as possible, just to try to get something working...

var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var ses = new AWS.SES({region: 'eu-west-1'});

var bucketName = 'my-s3-bucket-name';

var emailBody;
 
exports.handler = function(event, context, callback) {
    var sesNotification = event.Records[0].ses;
    
    s3.getObject({
            Bucket: bucketName,
            Key: sesNotification.mail.messageId
        }, function(err, data) {
            if (err) {
                console.log(err, err.stack);
                callback(err);
            } else {
                emailBody = data.Body;
                
                callback(null, null);
            }
        });
};

exports.handler = (event, context, callback) => {
    let params = {
        Destination: {
            ToAddresses: ['my.personal.email@gmail.com']
        },
        Message: {
            Body: {
                Text: {
                    Data: emailBody
                }
            },
            Subject: {
                Data: 'Subject of Email goes here'
            }
        },
        Source: 'ses-redirect@mydomain.com'
    };

    ses.sendEmail(params, function (err, data) {
        callback(null, {
            err: err,
            data: data
        });

        if (err) {
            console.log(err);
            context.fail(err);
        } else {
            console.log(data);
            context.succeed(event);
        }
    });
};

This is code that I basically put together from a couple of AWS examples...
Example 3: Retrieves Email from Amazon S3
How do I send email using Lambda and Amazon SES?

The Lambda editor has built-in test functionality, and following the steps in that last link, I was able to get Lambda to successfully send a test email to me. I just can't get it all working together - my Lambda function being executed based on my SES receipt rules, accessing the S3 bucket, and emailing me something useful.

I suspected also that it might have something to do with permissions, but it looks to me like the "execution role" of my Lambda function has all the required permissions. I've granted the execution role permission for the ses:SendEmail and s3:* actions on any resource:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}

I'm not seeing anything useful in the AWS CloudWatch logs... screenshot of AWS CloudWatch logs

I found another couple of similar questions here, but they don't provide any solution...
Lambda SES forwarding function AWS
AWS SES on Lambda - fails (silently) to send email

Upvotes: 0

Views: 1257

Answers (1)

Meir Gabay
Meir Gabay

Reputation: 3316

Don't set up a second receipt rule, you only need the first receipt rule, here's the flow-

  1. A user sends an email to any email address in your domain (as you described)
  2. Email is fetched by SES and saved to S3 (your first receipt rule), let's say it saves eml files in the path /eml
  3. Whoops! we have a new file in S3! That's another event!

The Lambda Function should be triggered by an S3 event (s3:ObjectCreated:Put) see here, which listens only to /eml path in your bucket. So now each time an eml file is saved to the bucket, you can do whatever you want with it ...

Disclaimer

If someone sends an email to any address at my domain, I want that email to end up in my personal Gmail inbox... Lambda function that should grab the email out of the S3 bucket, and again call SES to send the email contents to my personal Gmail inbox. This part isn't working.

Regarding ☝️ - I have a feeling that going this road is not the best idea since sending emails with SES is not so cheap. You don't want people to abuse your SMTP server and waste your money. Also, sending emails with SES (or any other SMTP server) usually have limits, for example GMAIL's limit is 2000 emails per day.

Upvotes: 1

Related Questions