I want to send the email received to my site and saved in S3 to external email service in my case Gmail, so here is the steps I did:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSESPuts",
"Effect": "Allow",
"Principal": {
"Service": "ses.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<bucketName>/*",
"Condition": {
"StringEquals": {
"aws:Referer": "<awsAccountId>"
}
}
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"ses:SendRawEmail"
],
"Resource": [
"arn:aws:s3:::<bucketName>/*",
"arn:aws:ses:<region>:<awsAccountId>:identity/*"
]
}
]
}
import json
# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# This file is licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License. A copy of the
# License is located at
#
# http://aws.amazon.com/apache2.0/
#
# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
# OF ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import os
import boto3
import email
import re
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
MailS3Bucket = 'name-of-the-bucket'
MailS3Prefix = 'prefix of the send rule (folder where I am saving emails in s3)'
MailSender = 'Verified Email in SES emails'
MailRecipient = 'Verified Email in SES emails too (This is the email I want to send emails to)'
Region = 'my region'
region = os.environ['Region']
def get_message_from_s3(message_id):
incoming_email_bucket = os.environ['MailS3Bucket']
incoming_email_prefix = os.environ['MailS3Prefix']
if incoming_email_prefix:
object_path = (incoming_email_prefix + "/" + message_id)
else:
object_path = message_id
object_http_path = (f"http://s3.console.aws.amazon.com/s3/object/{incoming_email_bucket}/{object_path}?region={region}")
# Create a new S3 client.
client_s3 = boto3.client("s3")
# Get the email object from the S3 bucket.
object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)
# Read the content of the message.
file = object_s3['Body'].read()
file_dict = {
"file": file,
"path": object_http_path
}
return file_dict
def create_message(file_dict):
sender = os.environ['MailSender']
recipient = os.environ['MailRecipient']
separator = ";"
# Parse the email body.
mailobject = email.message_from_string(file_dict['file'].decode('utf-8'))
# Create a new subject line.
subject_original = mailobject['Subject']
subject = "FW: " + subject_original
# The body text of the email.
body_text = ("The attached message was received from "
+ separator.join(mailobject.get_all('From'))
+ ". This message is archived at " + file_dict['path'])
# The file name to use for the attached message. Uses regex to remove all
# non-alphanumeric characters, and appends a file extension.
filename = re.sub('[^0-9a-zA-Z]+', '_', subject_original) + ".eml"
# Create a MIME container.
msg = MIMEMultipart()
# Create a MIME text part.
text_part = MIMEText(body_text, _subtype="html")
# Attach the text part to the MIME message.
msg.attach(text_part)
# Add subject, from and to lines.
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = recipient
# Create a new MIME object.
att = MIMEApplication(file_dict["file"], filename)
att.add_header("Content-Disposition", 'attachment', filename=filename)
# Attach the file object to the message.
msg.attach(att)
message = {
"Source": sender,
"Destinations": recipient,
"Data": msg.as_string()
}
return message
def send_email(message):
aws_region = os.environ['Region']
# Create a new SES client.
client_ses = boto3.client('ses', region)
# Send the email.
try:
#Provide the contents of the email.
response = client_ses.send_raw_email(
Source=message['Source'],
Destinations=[
message['Destinations']
],
RawMessage={
'Data':message['Data']
}
)
# Display an error if something goes wrong.
except ClientError as e:
output = e.response['Error']['Message']
else:
output = "Email sent! Message ID: " + response['MessageId']
return output
def lambda_handler(event, context):
# Get the unique ID of the message. This corresponds to the name of the file
# in S3.
message_id = event['Records'][0]['ses']['mail']['messageId']
print(f"Received message ID {message_id}")
# Retrieve the file from the S3 bucket.
file_dict = get_message_from_s3(message_id)
# Create the message.
message = create_message(file_dict)
# Send the email and print the result.
result = send_email(message)
print(result)
PS:
info@my-domain.com
is the prefix I set in the lambda function and the folder I am saving the email in s3.email-forwarding-service
is the actual name of the lambda functionResult: I am able to save the sent emails in s3 in their right folder (info@...) but the lambda function is not working so I am not receiving the emails on my Gmail account!!
UPDATE: I changed Lambda:
os.environ['MailS3Bucket'] = 'name-of-the-bucket'
os.environ['MailS3Prefix'] = 'prefix of the send rule (folder where I am saving emails in s3)'
os.environ['MailSender'] = 'Verified Email in SES emails'
os.environ['MailRecipient'] = Verified Email in SES emails too (This is the email I want to send emails to)'
os.environ['Region'] = 'my region'
Instead of
MailS3Bucket = 'name-of-the-bucket'
MailS3Prefix = 'prefix of the send rule (folder where I am saving emails in s3)'
MailSender = 'Verified Email in SES emails'
MailRecipient = 'Verified Email in SES emails too (This is the email I want to send emails to)'
Region = 'my region'
So now I am having this error in cloud watch:
[ERROR] ClientError: An error occurred (AccessDenied) when calling the GetObject operation: Access DeniedTraceback (most recent call last): File "/var/task/lambda_function.py", line 143, in lambda_handler file_dict = get_message_from_s3(message_id) File "/var/task/lambda_function.py", line 48, in get_message_from_s3 object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path) File "/var/runtime/botocore/client.py", line 357, in _api_call return self._make_api_call(operation_name, kwargs) File "/var/runtime/botocore/client.py", line 676, in _make_api_call raise error_class(parsed_response, operation_name)
Line 48 is: object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)
So I thinks the lambda function is not using the created user in IAM to access the s3, And I don't know python to force the use of the IAM user; HOWEVER Note That is is getting the correct email id saved in s3 according to cloudwatch logs
Any Help, Thank you and much appreciated!
I was able to fix this problem by going to the settings of the lambda function and adding allowing it to access s3 and ses.