亚马逊AWS官方博客
借助 AWS Step Functions 将回调 URL 用于批准电子邮件
本博文是 AWS 无服务器精英人才、iRobot 云机器人技术研究科学家 Ben Kehoe 所写的客座文章
AWS Step Functions 是一种无服务器工作流程编排服务,可让您使用声明性 Amazon States 语言协调流程。如果 Step Functions 任务耗时超过 15 分钟,则无法使用 AWS Lambda 函数 – 在这种情况下,Step Functions 能为我们提供回调模式。批准电子邮件是此类别中的常见的使用案例。
在这篇博文中,我将向您展示如何创建 Step Functions 状态机,以使用 sfn-callback-urls 应用程序完成电子邮件批准步骤。可以在 AWS Serverless Application Repository 中找到该应用程序。该状态机会发送一封包含批准/拒绝链接的电子邮件,随后发送确认电子邮件。您可以轻松针对自己的使用案例扩展此状态机。
解决方案概览
批准电子邮件必须包含 URL,用于在用户单击这些 URL 时将相应结果发送回 Step Functions。该 URL 应该具有较长的有效期,比预先签核的 URL 的有效期更长,因为要考虑用户本周休假的情况。 理想情况下,这不涉及令牌的存储和所需的维护工作。幸运的是,AWS Serverless Application Repository 应用程序可以处理这种情况!
sfn-callback-urls 应用程序允许您通过调用 Amazon API Gateway 或 Lambda 函数生成一次性回调 URL。每个 URL 都有一个关联的名称,表明其成功还是失败,以及应将哪些输出发回给 Step Functions。向 URL 发送 HTTP GET 或 POST 时,会将其输出发送到 Step Functions。sfn-callback-urls
是无状态的,而且它还支持带有 JSON 主体的 POST 回调,以便与 Webhook 配合使用。
部署应用程序
首先,部署 sfn-callback-urls
无服务器应用程序,并记下它公开的 Lambda 函数的 ARN。在 AWS Serverless Application Repository 控制台中,选择显示创建自定义 IAM 角色或资源策略的应用程序,然后搜索 sfn-callback-urls。 您还可以访问该应用程序。
在应用程序设置下,选中确认创建 IAM 资源的复选框。默认情况下,此应用程序会创建 KMS 密钥。您可以通过将 DisableEncryption 参数设置为 true 来禁用此功能,但首先请阅读左侧自述文件中的“安全性”部分。向下滚动并选择“部署”。
在部署确认页面上,选择 CreateUrls,以打开该函数的 Lambda 控制台。记下函数的 ARN,以便在后面的操作中使用。
执行以下操作,创建应用程序:
- 创建一个 SNS 主题并使用您的电子邮件地址订阅该主题。
- 创建处理 URL 创建和电子邮件发送的 Lambda 函数,并添加适当的权限。
- 为状态机创建 IAM 角色以调用 Lambda 函数。
- 创建状态机。
- 开始执行并给自己发送一些电子邮件!
创建 SNS 主题
在 SNS 控制台中,依次选择主题和创建主题。将该主题命名为 ApprovalEmailsTopic,然后选择创建主题。
记下主题 ARN,例如 arn:aws:sns:us-east-2:012345678912:ApprovalEmailsTopic。
现在,设置订阅以接收电子邮件。选择创建订阅。对于协议,请选择电子邮件,输入一个电子邮件地址,然后选择创建订阅。
等待包含确认链接的电子邮件发送到您的收件箱。它用于确认订阅,允许通过电子邮件将发布到该主题的消息发送给您。
创建 Lambda 函数
现在创建 Lambda 函数,以便处理回调 URL 的创建和电子邮件发送。在这篇简短的博文中,我们只要创建一个 Lambda 函数来完成两个单独的步骤:
- 创建回调 URL
- 发送批准电子邮件,然后发送确认电子邮件
代码中有一个 if
语句将两者分开,状态机需要告知 Lambda 函数哪个状态正在调用该函数。这里的最佳实践是分别使用两个单独的 Lambda 函数。
要在 Lambda 控制台中创建 Lambda 函数,请选择创建函数,将其命名为 ApprovalEmailsFunction,然后选择最新的 Python 3 运行时。在权限下,选择创建具有基本权限的新角色,然后选择创建。
向下滚动到配置部分来添加权限。选择链接以在 IAM 控制台中查看角色。
添加 IAM 权限
在 IAM 控制台中,选择新角色,然后选择添加内联策略。将 sns:Publish
权限添加到您创建的主题,将 lambda:InvokeFunction
权限添加到 sfn-callback-urls
CreateUrls
函数 ARN。
回到 Lambda 控制台,在函数中使用以下代码:
import json, os, boto3
def lambda_handler(event, context):
print('Event:', json.dumps(event))
# Switch between the two blocks of code to run
# This is normally in separate functions
if event['step'] == 'SendApprovalRequest':
print('Calling sfn-callback-urls app')
input = {
# Step Functions gives us this callback token
# sfn-callback-urls needs it to be able to complete the task
"token": event['token'],
"actions": [
# The approval action that transfers the name to the output
{
"name": "approve",
"type": "success",
"output": {
# watch for re-use of this field below
"name_in_output": event['name_in_input']
}
},
# The rejection action that names the rejecter
{
"name": "reject",
"type": "failure",
"error": "rejected",
"cause": event['name_in_input'] + " rejected it"
}
]
}
response = boto3.client('lambda').invoke(
FunctionName=os.environ['CREATE_URLS_FUNCTION'],
Payload=json.dumps(input)
)
urls = json.loads(response['Payload'].read())['urls']
print('Got urls:', urls)
# Compose email
email_subject = 'Step Functions example approval request'
email_body = """Hello {name},
Click below (these could be better in HTML emails):
Approve:
{approve}
Reject:
{reject}
""".format(
name=event['name_in_input'],
approve=urls['approve'],
reject=urls['reject']
)
elif event['step'] == 'SendConfirmation':
# Compose email
email_subject = 'Step Functions example complete'
if 'Error' in event['output']:
email_body = """Hello,
Your task was rejected: {cause}
""".format(
cause=event['output']['Cause']
)
else:
email_body = """Hello {name},
Your task is complete.
""".format(
name=event['output']['name_in_output']
)
else:
raise ValueError
print('Sending email:', email_body)
boto3.client('sns').publish(
TopicArn=os.environ['TOPIC_ARN'],
Subject=email_subject,
Message=email_body
)
print('done')
return {}
现在,将以下环境变量 TOPIC_ARN 和 CREATE_URLS FUNCTION 设置为主题的 ARN 和前面提到的 sfn-callback-urls
函数。
更新代码和环境变量后,选择保存。
创建状态机
首先您需要一个状态机角色,并假设该角色可以调用新的 Lambda 函数。
在 IAM 控制台中,创建一个角色,并将 Step Functions 用作其可信实体。这需要 AWSLambdaRole
策略,以便为其提供调用您的函数的权限。将该角色命名为 ApprovalEmailsStateMachineRole
。
现在,您可以创建状态机了。在 Step Functions 控制台中,选择创建状态机,将其命名为 ApprovalEmails
,并使用以下代码:
{
"Version": "1.0",
"StartAt": "SendApprovalRequest",
"States": {
"SendApprovalRequest": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
"Parameters": {
"FunctionName": "ApprovalEmailsFunction",
"Payload": {
"step.$": "$$.State.Name",
"name_in_input.$": "$.name",
"token.$": "$$.Task.Token"
}
},
"ResultPath": "$.output",
"Next": "SendConfirmation",
"Catch": [
{
"ErrorEquals": [ "rejected" ],
"ResultPath": "$.output",
"Next": "SendConfirmation"
}
]
},
"SendConfirmation": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "ApprovalEmailsFunction",
"Payload": {
"step.$": "$$.State.Name",
"output.$": "$.output"
}
},
"End": true
}
}
}
该状态机有两种状态。它获取一个带字段“name”的 JSON 对象作为输入。每个状态都是一个 Lambda 任务。为使这篇博文简明扼要,我在单个 Lambda 函数中结合了两种状态的功能。您将状态名称作为 step 字段传递给函数,以允许该函数选择要运行的代码块。根据使用不同函数负责不同任务的最佳实践,此字段不是必需的。
第一种状态 SendApprovalRequest 需要带有 name 字段的输入 JSON 对象。它将该名称与步骤和任务令牌(完成回调任务所必须)打包在一起,并使用它调用 Lambda 函数。无论通过回调接收怎样的输出,状态机都将其存储在 output 字段下的输出 JSON 对象中。随后,该输出会成为第二个状态的输入。
第二个状态 SendConfirmation 将该输出字段与该步骤一起使用并再次调用该函数。第二个调用不使用回调模式,也不涉及任务令牌。
开始执行
要运行该示例,请选择开始执行,并将输入设置为 JSON 对象,如下所示:
{
"name": "Ben"
}
您会看到突出显示 SendApprovalRequest 状态的执行图表。这意味着它已经启动,并且正在等待返回任务令牌。检查收件箱中是否有包含批准和拒绝链接的电子邮件。选择链接后,浏览器会打开一个确认页面,表明已接受您的回复。在状态机控制台中,您会看到执行已完成,并且您还会收到一封用于批准或拒绝的确认电子邮件。
小结
在这篇博文中,我演示了如何使用 AWS Serverless Application Repository 中的 sfn-callback-urls
应用程序为批准电子邮件创建 URL。我还向您展示了如何构建系统,来创建和发送这些电子邮件并处理结果。此模式可用作更大规模的状态机的一部分,用于管理您自己的工作流程。
此示例还在 sfn-callback-urls GitHub 代码库中作为 AWS CloudFormation 模板提供。