PAWS SES 电子邮件接收与 S3 和 Lambda 函数转发到外部电子邮件 (Gmail)
PAWS SES Email Receive With S3 And Lambda Function Forward To External Email (Gmail)
我想将收到的电子邮件发送到我的站点并保存在 S3 中到外部电子邮件服务,在我的例子中是 Gmail,所以这是我执行的步骤:
- 使用此策略创建 S3 存储桶:
{
"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>"
}
}
}
]
}
- 使用此策略创建的 IAM 用户:
{
"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/*"
]
}
]
}
- 在 Python 3.7 中创建了此 Lambda 函数并将超时值设置为 30 秒:
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
是我在 lambda 函数中设置的前缀和我在 s3 中保存电子邮件的文件夹。
email-forwarding-service
是 lambda 函数的实际名称
- 当然,我将 S3 和 IAM 策略中的变量更改为实际数据
- 我将规则设置为活动状态,所以它一定能正常工作
- 这取自AWS Documentation
- 我什么都不知道 python 除了我在 python 文件开头添加的变量
之外,所有内容都已复制并粘贴
结果:我可以将已发送的电子邮件保存在 s3 中的正确文件夹中 (info@...),但 lambda 函数无法正常工作,因此我的 Gmail 收不到电子邮件帐户!!
更新: 我更改了 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'
而不是
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'
所以现在我在云手表中遇到了这个错误:
[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)
第 48 行是:object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)
所以我认为lambda函数没有使用IAM中创建的用户访问s3,而且我不知道python强制使用IAM用户;但是请注意,这是根据 cloudwatch 日志
获取保存在 s3 中的正确电子邮件 ID
任何帮助,
谢谢你,非常感谢!
我能够通过转到 lambda 函数的设置并添加允许它访问 s3 和 ses 来解决这个问题。
我想将收到的电子邮件发送到我的站点并保存在 S3 中到外部电子邮件服务,在我的例子中是 Gmail,所以这是我执行的步骤:
- 使用此策略创建 S3 存储桶:
{
"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>"
}
}
}
]
}
- 使用此策略创建的 IAM 用户:
{
"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/*"
]
}
]
}
- 在 Python 3.7 中创建了此 Lambda 函数并将超时值设置为 30 秒:
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
是我在 lambda 函数中设置的前缀和我在 s3 中保存电子邮件的文件夹。email-forwarding-service
是 lambda 函数的实际名称- 当然,我将 S3 和 IAM 策略中的变量更改为实际数据
- 我将规则设置为活动状态,所以它一定能正常工作
- 这取自AWS Documentation
- 我什么都不知道 python 除了我在 python 文件开头添加的变量 之外,所有内容都已复制并粘贴
结果:我可以将已发送的电子邮件保存在 s3 中的正确文件夹中 (info@...),但 lambda 函数无法正常工作,因此我的 Gmail 收不到电子邮件帐户!!
更新: 我更改了 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'
而不是
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'
所以现在我在云手表中遇到了这个错误:
[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)
第 48 行是:object_s3 = client_s3.get_object(Bucket=incoming_email_bucket, Key=object_path)
所以我认为lambda函数没有使用IAM中创建的用户访问s3,而且我不知道python强制使用IAM用户;但是请注意,这是根据 cloudwatch 日志
获取保存在 s3 中的正确电子邮件 ID任何帮助, 谢谢你,非常感谢!
我能够通过转到 lambda 函数的设置并添加允许它访问 s3 和 ses 来解决这个问题。