Amazon Web Services ブログ
AWS Step Functions で承認メールにコールバック URL を使用する
iRobot のクラウドロボティクスリサーチサイエンティストで、AWS サーバーレスヒーローでもある Ben Kehoe 氏によるゲスト投稿
AWS Step Functions は、宣言型 Amazon ステートメント言語を使用してプロセスを調整できるサーバーレスワークフローオーケストレーションサービスです。Step Functions のタスクが 15 分以上かかる場合には、AWS Lambda 関数を使用できません。このような場合、Step Functions がコールバックパターンを提供します。このカテゴリでは、承認メールが一般的なユースケースです。
この投稿では、E メール承認ステップで sfn-callback-urls アプリケーションを使用する Step Functions ステートマシンを作成する方法をご紹介します。アプリは AWS Serverless Application Repository で利用できます。ステートマシンは承認/拒否リンクを含む E メールを送信し、後で確認メールを送信します。このステートマシンはユースケースに合わせて簡単に拡張できます。
ソリューションの概要
承認メールには、ユーザーがクリックした際に適切な結果を Step Functions に送り返す URL を含める必要があります。URL は署名済み URL よりも長い期間有効である必要があります。でも、もし今週ユーザーが休暇を取っているとしたら、どうしますか? URL には、トークンの保管と必要なメンテナンスを伴わないのが理想です。幸い、AWS Serverless Application Repository というアプリがあります。
sfn-callback-urls アプリを使用すると、Amazon API Gateway か Lambda 関数のいずれかを呼び出して、1 回限りのコールバック URL を生成できます。各 URL には関連付けられた名前があり、成功したか失敗したかか、あるいはどの出力を Step Functions に送り返すかを決めます。HTTP GET または POST を URL に送信すると、その出力が Step Functions に送信されます。sfn-callback-urls
はステートレスで、Webhook で使用するための JSON ボディを持つ POST コールバックもサポートしています。
アプリのデプロイ
まず、sfn-callback-urls
サーバーレスアプリをデプロイし、発行する Lambda 関数の ARN を書き留めます。AWS Serverless Application Repository コンソールで、[Show apps that create custom IAM roles or resource policies] をクリックし、sfn-callback-urls を検索します。 アプリケーションにアクセスすることもできます。
[application settings] で、IAM リソースの作成を確認するボックスをクリックします。デフォルトでは、このアプリは KMS キーを作成します。DisableEncryption パラメータを true に設定することで、これを無効にできます。詳しくは、左側にある Readme の Security セクションをお読みください。下方へスクロールし、[Deploy] をクリックします。
デプロイの確認ページで [CreateUrls] を選択します。これで、この機能の Lambda コンソールが開きます。後で必要となりますので、関数 ARN を書き留めておきましょう。
以下を実行して、アプリケーションを作成します。
- SNS トピックを作成し、そのトピックに E メールをサブスクライブする。
- URL の作成とメール送信を処理する Lambda 関数を作成し、適切なアクセス許可を追加する。
- ステートマシンの IAM ロールを作成して、Lambda 関数を呼び出す。
- ステートマシンの作成
- 実行を開始して、ご自分に E メールを送ってみましょう。
SNS トピックの作成
SNS コンソールで、[Topics]、[Create Topic] の順にクリックします。トピックに ApprovalEmailsTopic という名前を付け、[Create Topic] を選択します。
トピック ARN (arn:aws:sns:us-east-2:012345678912:ApprovalEmailsTopic など) をメモします。
次に、メールを受信するためのサブスクリプションを設定します。[Create subscription] をクリックします。[Protocol] で [Email] を選択し、メールアドレスを入力して、[Create subscription] をクリックします。
確認リンクが記載されたメールが、受信トレイに届くのを待ちます。サブスクリプションを確認し、トピックに発行したメッセージをメールで送信できるようにします。
Lambda 関数の作成
次に、コールバック URL の作成とメールの送信を処理する Lambda 関数を作成します。この投稿ではスペースの制限があるため、1 つのLambda関数を作成して、2 つの別々のステップを完了します。
- コールバック URL を作成する
- 承認メールを送信し、後で確認メールを送信する
コードには 2 つを分離する IF
ステートメントがあります。このため、ステートマシンが Lambda 関数にどのステートを呼び出しているかを通知する必要があります。ここでは、2 つの異なる Lambda 関数を使用することを推奨します。
Lambda コンソールで Lambda 関数を作成するには、[Create function] をクリックし、ApprovalEmailsFunction という名前を付けて、最新の Python 3 ランタイムを選択します。[Permissions] で、[Create a new Role with basic permissions]、[Create] の順にクリックします。
[Configuration] のところまでスクロールダウンし、アクセス許可を追加します。リンクを選択して、IAM コンソールでロールを確認します。
IAM アクセス許可の追加
IAM コンソールで新しいロールを選択し、[Add inline policy] をクリックします。作成したトピックに sns:Publish
のアクセス許可を追加し、sfn-callback-urls
CreateUrls
関数 ARN に lambda:InvokeFunction
を追加します。
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
関数に設定します。
コードと環境変数を更新後、[Save] をクリックします。
ステートマシンの作成
最初に、新しい Lambda 関数を呼び出すことができるステートマシンのロールが必要です。
IAM コンソール で、信頼できるエンティティとして Step Functions を使用し、ロールを作成します。これには AWSLambdaRole
ポリシーが必要です。このポリシーは、関数を呼び出すためのアクセスを許可するものです。ロールに ApprovalEmailsStateMachineRole
という名前を付けます。
これで、ステートマシンを作成する準備ができました。Step Functions コンソールで、[Create state machine] をクリックし、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
}
}
}
このステートマシンには 2 つの状態があります。入力として「name」という 1 つのフィールドを持つ JSON オブジェクトを取得します。各状態は Lambda のタスクです。この投稿を簡潔にするため、両方の状態の機能を 1 つの Lambda 関数にまとめました。状態名を step フィールドとして渡し、実行するコードブロックを関数が選択できるようにします。さまざまなる機能に異な関数を使ったベストプラクティスの場合、このフィールドは必要ありません。
最初の状態である SendApprovalRequest では、name フィールドを持つ入力 JSON オブジェクトが必要です。その名前とステップおよびタスクトークン (コールバックタスクを完了するために必要) をパッケージし、それを使って Lambda 関数を呼び出します。コールバックの一部として受信される出力は、ステートマシンが output フィールド下の出力 JSON オブジェクトに保存します。この出力は、2 番目の状態への入力になります。
2 番目の状態である SendConfirmation はその出力フィールドをステップとともに取得し、関数を再度呼び出します。2 番目の呼び出しはコールバックパターンを使用せず、タスクトークンを含みません。
実行を開始する
この例を実行するには [Start execution] をクリックし、入力を次のような JSON オブジェクトに設定します。
{
"name": "Ben"
}
SendApprovalRequest 状態を強調表示した実行グラフが表示されます。これは、実行を開始し、タスクトークンが返されるのを待っていることを意味します。受信トレイで、approve と reject へのリンクがあるメールを確認してください。リンクをクリックして、ブラウザで応答を承諾したことを示す確認ページを開きます。ステートマシンコンソールで、実行が終了したことが確認できます。また、承認または拒否の確認メールが届きます。
まとめ
この投稿では、AWS Serverless Application Repository の sfn-callback-urls
アプリを使用して、承認メールの URL を作成する方法をご紹介しました。これらの E メールを作成および送信し、結果を処理できるシステムを構築する方法も解説しました。さらに大規模なステートマシンの一環としてこのパターンを使用し、独自のワークフローを管理できます。
今回の例は、sfn-callback-urls GitHub リポジトリ の AWS CloudFormation テンプレートとしても利用できます。