小西秀和です。
AWSで承認フローの機能を提供するサービスにはAWS Systems Manager AutomationやAWS CodePipelineの承認アクションなどがあります。
近年、AI技術の急速な進化により、従来人間が手動で行っていた承認プロセスを生成AIで置き換えたり、強力にサポートしたりすることが可能になってきました。しかし、専門知識や権限を持つ人間による最終判断も依然として重要です。
そこで私は、将来的に生成AIを承認フローに組み込むことを見据え、AWS Step Functionsを活用した承認フローシステムをAWSサービスを使用して試作しました。この試作の主な目的は以下の通りです。
- APIを介して承認フローをシステム化することで、人間と生成AIの間で意思決定プロセスを柔軟に切り替えられる
- 初期段階では人間が承認を行い、生成AIの能力が十分と判断された場合に段階的にAIへ移行できる
- 生成AIの判断に不安がある場合や、最終確認が必要な場合は、人間が承認プロセスに介入できる
- 人間と生成AIを組み合わせた多段階承認フローにより、より高い精度での意思決定が可能になる
なお、本記事ではAWS CloudFormationテンプレートを使用してこのデモを構築しています。AWS CDKやAWS SAMなど、より高度なIaCツールを使用しなかった理由には以下のことが挙げられます。
- 再現性と可搬性: CloudFormationテンプレート一つで完結させることで、環境に依存せず、誰でも同じ結果を得られるようにしました。CDKやSAMを使用すると、バージョンの違いや依存関係の問題が生じる可能性があり、再現性が低下する恐れがあります。
- 学習障壁の低さ: 多くのAWSユーザーにとって、CloudFormationは馴染みのある技術です。CDKやSAMを使用すると、追加のツールやプログラミング言語の知識が必要になる場合があり、読者の方々にとって障壁となる可能性があります。
- AWSリソースの直接的な理解: CloudFormationテンプレートでは、AWSリソースを直接定義します。これにより、AWSサービスの詳細な設定や動作を理解しやすくなり、教育的な価値が高まります。
- デバッグの容易さ: 単一のテンプレートファイルであるため、エラーの特定や修正が比較的容易です。これは、読者の方々が自身の環境で実装する際のトラブルシューティングを容易にします。
- バージョン管理とメンテナンスの簡素化: 単一ファイルでの管理は、バージョン管理を簡素化し、長期的な保守性を高めます。CloudFormationの基本的な構文は長年にわたり安定しており、将来的な変更や非推奨化のリスクが低いです。
- 迅速な展開とテスト: 追加のビルドステップが不要なため、テンプレートの変更をすぐに適用してテストできます。これにより、読者の方々が自身の環境で素早く試すことができます。
- AWSコンソールとの互換性: CloudFormationテンプレートは、AWSコンソールで直接編集・適用できます。これにより、GUIを通じた迅速な変更や確認が可能となり、より多くの方々にとって扱いやすいものとなります。
これらの理由により、CloudFormationテンプレートを使用することで、より多くの読者の方々が容易に理解し、実践できるデモ環境を提供できると考えました。
今回はAWSサービスを使用して構築できる承認フローのうち、AWS Systems Manager Automationの承認アクションを使用してAWS Step Functionsのワークフローへ承認フローを追加する方法を試してみたいと思います。
※本記事および当執筆者のその他の記事で掲載されているソースコードは自主研究活動の一貫として作成したものであり、動作を保証するものではありません。使用する場合は自己責任でお願い致します。また、予告なく修正することもありますのでご了承ください。
今回の記事の内容は次のような構成になっています。
本記事で試す構成図
今回試すAWS Step FunctionsへAWS LambdaとAWS Systems Manager Automationで承認フローを追加する構成は次のようになります。

流れとしては、
まず、AWS Step FunctionsステートマシンでwaitForTaskTokenを指定したAWS Lambda関数から親SSM Documentを実行(親SSM Automation)します。
親SSM Automationは子SSM Documentを実行(子SSM Automation)し、子SSM Automationは承認アクション(aws:approve)で承認者にAmazon SNSトピックのメール通知で承認の可否を確認します。
承認フローの結果が承認だった場合は子SSM Automationから承認とトークンのパラメータを結果返却用のAWS Lambdaに渡します。
承認フローの結果が拒否だった場合は親SSM Automationから拒否とトークンのパラメータを結果返却用のAWS Lambdaに渡します。
上記のようにSSM Documentを親子関係にしている理由は、AWS Systems Manager Automationの承認アクション(aws:approve)は拒否するとFailedして後続のAWS Lambda関数が呼ばれないためです。
そこで、子SSM Documentの承認アクションの拒否によるFailedを親SSM Documentでキャッチして拒否した結果を返却するAWS Lambda関数を実行するようにしています。
このようにAWS Systems Manager Automationの承認アクションを部品化して使用する利点は、承認者の認証と承認アクションの権限を指定できることにあります。
子SSM Documentの承認アクション(aws:approve)で送信されるAmazon SNSトピックのメール通知が届くと、承認者はリンクからAWSマネジメントコンソールへログインし、承認アクションを許可されたIAMロールまたはIAMユーザーの場合のみ承認の可否を決定できます。
一方でAWS CloudFormationテンプレートを更新する場合の考慮事項として、親SSM Document、子SSM Documentの定義を更新する場合にAWS CloudFormationの仕様上、SSM Document名を変更して再作成する必要があることが挙げられます。
AWS CloudFormationテンプレートとパラメータの例
AWS CloudFormationテンプレート(AWS Step FunctionsへのAWS LambdaとAWS Systems Manager Automationによる承認フローの追加)
入力パラメータ例
EmailForNotification: sample@h-o2k.com #承認リクエストを送信するメールアドレス SsmApprovers: arn:aws:iam::XXXXXXXXXXXX:role/ho2k.com #承認リクエストに対して承認、拒否を決定するIAMロールまたはIAMユーザー SsmMinRequiredApprovals: 1 #承認に必要な人数。ここに記載の人数が承認して初めて処理として承認される。 SsmAutomationAssumeRoleName: SsmAutomationAssumeRole #作成するAutomationAssumeRoleの名称(SSM Automationの実行にこのロールを使用する) SsmParentDocumentApprovalActionName: SsmParentDocumentApprovalAction #親SSM Documentの名称 SsmParentDocumentApprovalActionVersionName: 1 #親SSM Documentのバージョン名 SsmChildDocumentForApprovalActionName: SsmChildDocumentForApprovalAction #子SSM Documentの名称 SsmChildDocumentForApprovalActionVersionName: 1 #子SSM Documentのバージョン名
テンプレート本体
ファイル名:SfnApprovalCFnSfnWithSsmApproval.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Add AWS Systems Manager Automation Approval Action to AWS Step Functions.'
Parameters:
SsmAutomationAssumeRoleName:
Type: String
Default: "SsmAutomationAssumeRole"
SsmParentDocumentApprovalActionName:
Type: String
Default: "SsmParentDocumentApprovalAction"
SsmChildDocumentForApprovalActionName:
Type: String
Default: "SsmChildDocumentForApprovalAction"
SsmParentDocumentApprovalActionVersionName:
Type: String
Default: "1"
SsmChildDocumentForApprovalActionVersionName:
Type: String
Default: "1"
SsmApprovers:
Type: String
Default: "arn:aws:iam::XXXXXXXXXXXX:role/ho2k.com"
SsmMinRequiredApprovals:
Type: String
Default: "1"
EmailForNotification:
Type: String
Default: "sample@h-o2k.com"
Resources:
SsmAutomationAssumeRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref SsmAutomationAssumeRoleName
Path: /
MaxSessionDuration: 43200
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
- lambda.amazonaws.com
- edgelambda.amazonaws.com
- events.amazonaws.com
- scheduler.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub 'IAMPolicy-AdditionalPolicyForAutomationRole'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- iam:PassRole
Resource:
- !Sub 'arn:aws:iam::${AWS::AccountId}:role/*'
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource:
- 'arn:aws:logs:*:*:*'
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub 'arn:aws:logs:*:*:log-group:/aws/*/*:*'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole'
LambdaForSsmStartAutomationExecution:
Type: AWS::Lambda::Function
DependsOn:
- SsmAutomationAssumeRole
Properties:
FunctionName: LambdaForSsmStartAutomationExecution
Description : 'LambdaForSsmStartAutomationExecution'
Runtime: python3.9
MemorySize: 10240
Timeout: 900
Role: !GetAtt SsmAutomationAssumeRole.Arn
Handler: index.lambda_handler
Code:
ZipFile: |
import botocore
import boto3
import json
import os
import sys
region = os.environ.get('AWS_REGION')
sts_client = boto3.client("sts", region_name=region)
account_id = sts_client.get_caller_identity()["Account"]
ssm_client = boto3.client('ssm', region_name=region)
def lambda_handler(event, context):
print(("Received event: " + json.dumps(event, indent=2)))
try:
ssm_auto_resp = ssm_client.start_automation_execution(
DocumentName=event['ssm_parent_doc_name'],
Parameters={
'DocumentName': [event['ssm_child_doc_name']],
'AutomationAssumeRole': [event['ssm_automation_assume_role']],
'Description': [event['ssm_description']],
'Message': [event['ssm_message']],
'NotificationArn': [event['ssm_notification_arn']],
'Approvers': [event['ssm_approvers']],
'MinRequiredApprovals': [event['ssm_min_required_approvals']],
'LambdaNameForApproved': [event['ssm_lambda_for_approved']],
'LambdaParametersForApproved': [json.dumps({
'result': event['ssm_lambda_parameters_for_approved'],
'token': event['token']
})],
'LambdaNameForRejected': [event['ssm_lambda_for_reject']],
'LambdaParametersForRejected': [json.dumps({
'result': event['ssm_lambda_parameters_for_reject'],
'token': event['token']
})]
},
Mode='Auto'
)
except Exception as ex:
print(f'Exception:{ex}')
tb = sys.exc_info()[2]
print(f'ssm_client start_automation_execution FAIL. Exception:{str(ex.with_traceback(tb))}')
raise
result = {}
result['params'] = event.copy()
return result
LambdaForReceivingAutomationResult:
Type: AWS::Lambda::Function
DependsOn:
- AutomationResultReceivedByLambdaRole
Properties:
FunctionName: AutomationResultReceivedByLambda
Description : 'LambdaForReceivingAutomationResult'
Runtime: python3.9
MemorySize: 10240
Timeout: 900
Role: !GetAtt AutomationResultReceivedByLambdaRole.Arn
Handler: index.lambda_handler
Code:
ZipFile: |
import botocore
import boto3
import json
import os
import sys
region = os.environ.get('AWS_REGION')
sts_client = boto3.client("sts", region_name=region)
account_id = sts_client.get_caller_identity()["Account"]
sns_client = boto3.client('sns', region_name=region)
sfn_client = boto3.client('stepfunctions', region_name=region)
def lambda_handler(event, context):
print(("Received event: " + json.dumps(event, indent=2)))
if event.get('result','') == 'Approved':
is_approved = True
else:
is_approved = False
try:
#コールバックしたトークンでSFN側にタスクの成功を送信する。
sfn_res = sfn_client.send_task_success(
taskToken=event['token'],
output=json.dumps({'is_approved':is_approved})
)
except Exception as ex:
print(f'Exception:{ex}')
tb = sys.exc_info()[2]
print(f'sfn_client send_task_success FAIL. Exception:{str(ex.with_traceback(tb))}')
raise
return {'is_approved':is_approved}
AutomationResultReceivedByLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'IAMRole-LambdaForReceivingAutomationResult'
Path: /
MaxSessionDuration: 43200
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- edgelambda.amazonaws.com
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub 'IAMPolicy-LambdaForReceivingAutomationResult'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource:
- 'arn:aws:logs:*:*:*'
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub 'arn:aws:logs:*:*:log-group:/aws/lambda/AutomationResultReceivedByLambda:*'
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:Automation*'
- Effect: Allow
Action:
- states:ListActivities
- states:ListExecutions
- states:ListStateMachines
- states:DescribeActivity
- states:DescribeExecution
- states:DescribeStateMachine
- states:DescribeStateMachineForExecution
- states:GetExecutionHistory
- states:SendTaskSuccess
Resource:
- '*'
SsmParentDocumentApprovalAction:
Type: AWS::SSM::Document
Properties:
Name: !Ref SsmParentDocumentApprovalActionName
DocumentType: Automation
VersionName: !Ref SsmParentDocumentApprovalActionVersionName
DocumentFormat: YAML
Content:
description: 'SsmParentDocumentApprovalAction'
schemaVersion: '0.3'
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
AutomationAssumeRole:
type: String
description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
default: ''
DocumentName:
description: 'Document Name'
type: String
default: SsmChildDocumentForApprovalAction
Description:
description: 'Operation Description'
type: String
default: SsmChildDocumentForApprovalAction
Message:
description: Message
type: String
default: 'Please Approve after Confirmation.'
NotificationArn:
description: 'Amazon SNS Topic ARN for Approval Notification.'
type: String
default: 'arn:aws:sns:ap-northeast-1:000000000000:AutomationApprovalNotification'
LambdaNameForApproved:
description: 'Lambda Function Name to Invoke if Approved.'
type: String
default: AutomationResultReceivedByLambda
LambdaParametersForApproved:
description: 'Lambda Function Parameters to Invoke if Approved.'
type: String
default: "{\"result\":\"Approved\",\"token\":\"XXXXXXXXXXXX\"}"
LambdaNameForRejected:
description: 'Lambda Function Name to Invoke if Rejected.'
type: String
default: AutomationResultReceivedByLambda
LambdaParametersForRejected:
description: 'Lambda Function Parameters to Invoke if Rejected.'
type: String
default: "{\"result\":\"Rejected\",\"token\":\"XXXXXXXXXXXX\"}"
Approvers:
description: 'The IAM User or IAM Role of the Approver.'
type: StringList
MinRequiredApprovals:
description: MinRequiredApprovals
type: Integer
default: 1
mainSteps:
- name: ParentExecuteAutomation
action: 'aws:executeAutomation'
timeoutSeconds: 43200
onFailure: 'step:LambdaNameForRejected'
inputs:
DocumentName: '{{DocumentName}}'
RuntimeParameters:
AutomationAssumeRole: '{{AutomationAssumeRole}}'
Description: '{{Description}}'
Message: '{{Message}}'
NotificationArn: '{{NotificationArn}}'
LambdaNameForApproved: '{{LambdaNameForApproved}}'
LambdaParametersForApproved: '{{LambdaParametersForApproved}}'
LambdaNameForRejected: '{{LambdaNameForRejected}}'
LambdaParametersForRejected: '{{LambdaParametersForRejected}}'
Approvers: '{{Approvers}}'
MinRequiredApprovals: '{{MinRequiredApprovals}}'
isEnd: true
- name: LambdaNameForRejected
action: 'aws:invokeLambdaFunction'
maxAttempts: 3
timeoutSeconds: 120
onFailure: Abort
inputs:
FunctionName: '{{LambdaNameForRejected}}'
Payload: '{{LambdaParametersForRejected}}'
isEnd: true
SsmChildDocumentForApprovalAction:
Type: AWS::SSM::Document
Properties:
Name: !Ref SsmChildDocumentForApprovalActionName
DocumentType: Automation
VersionName: !Ref SsmChildDocumentForApprovalActionVersionName
DocumentFormat: YAML
Content:
description: 'SsmChildDocumentForApprovalAction'
schemaVersion: '0.3'
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
AutomationAssumeRole:
type: String
description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
default: ''
Description:
description: 'Operation Description'
type: String
default: 'SsmChildDocumentForApprovalAction'
Message:
description: Message
type: String
default: 'Please Approve after Confirmation.'
NotificationArn:
description: 'Amazon SNS Topic ARN for Approval Notification.'
type: String
default: 'arn:aws:sns:ap-northeast-1:000000000000:AutomationApprovalNotification'
LambdaNameForApproved:
description: 'Lambda Function Name to Invoke if Approved.'
type: String
default: AutomationResultReceivedByLambda
LambdaParametersForApproved:
description: 'Lambda Function Parameters to Invoke if Approved.'
type: String
default: "{\"result\":\"Approved\",\"token\":\"XXXXXXXXXXXX\"}"
LambdaNameForRejected:
description: 'Lambda Function Name to Invoke if Rejected.'
type: String
default: AutomationResultReceivedByLambda
LambdaParametersForRejected:
description: 'Lambda Function Parameters to Invoke if Rejected.'
type: String
default: "{\"result\":\"Rejected\",\"token\":\"XXXXXXXXXXXX\"}"
Approvers:
description: 'The IAM User or IAM Role of the Approver.'
type: StringList
MinRequiredApprovals:
description: MinRequiredApprovals
type: Integer
default: 1
mainSteps:
- name: ApprovalAction
action: 'aws:approve'
timeoutSeconds: 43200
onFailure: 'step:LambdaNameForRejected'
inputs:
Message: '{{Message}}'
NotificationArn: '{{NotificationArn}}'
Approvers: '{{Approvers}}'
MinRequiredApprovals: '{{MinRequiredApprovals}}'
- name: LambdaNameForApproved
action: 'aws:invokeLambdaFunction'
maxAttempts: 3
timeoutSeconds: 120
onFailure: Abort
inputs:
FunctionName: '{{LambdaNameForApproved}}'
Payload: '{{LambdaParametersForApproved}}'
isEnd: true
- name: LambdaNameForRejected
action: 'aws:invokeLambdaFunction'
maxAttempts: 3
timeoutSeconds: 120
onFailure: Abort
inputs:
FunctionName: '{{LambdaNameForRejected}}'
Payload: '{{LambdaParametersForRejected}}'
isEnd: true
SnsAutomationApprovalNotification:
Type: AWS::SNS::Topic
Properties:
TopicName: AutomationApprovalNotification
DisplayName: AutomationApprovalNotification
FifoTopic: False
Subscription:
- Endpoint: !Ref EmailForNotification
Protocol: email
StepFunctionsWithSsmAutomationApproval:
Type: AWS::StepFunctions::StateMachine
DependsOn:
- LambdaForSsmStartAutomationExecution
- LambdaForReceivingAutomationResult
- StepFunctionsWithSsmAutomationApprovalRole
- StepFunctionsWithSsmAutomationApprovalLogGroup
Properties:
StateMachineName: StepFunctionsWithSsmAutomationApproval
StateMachineType: STANDARD
RoleArn: !GetAtt StepFunctionsWithSsmAutomationApprovalRole.Arn
LoggingConfiguration:
Level: ALL
IncludeExecutionData: true
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt StepFunctionsWithSsmAutomationApprovalLogGroup.Arn
DefinitionString: !Sub |-
{
"Comment": "Sample of adding an Approval flow to AWS Step Functions.",
"TimeoutSeconds": 43200,
"StartAt": "InvokeLambdaForSsmStartAutomationExecution",
"States": {
"InvokeLambdaForSsmStartAutomationExecution": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
"Parameters": {
"FunctionName": "${LambdaForSsmStartAutomationExecution.Arn}:$LATEST",
"Payload": {
"step.$": "$$.State.Name",
"token.$": "$$.Task.Token",
"ssm_parent_doc_name.$": "$$.Execution.Input.ssm_parent_doc_name",
"ssm_automation_assume_role.$": "$$.Execution.Input.ssm_automation_assume_role",
"ssm_child_doc_name.$": "$$.Execution.Input.ssm_child_doc_name",
"ssm_description.$": "$$.Execution.Input.ssm_description",
"ssm_lambda_for_approved.$": "$$.Execution.Input.ssm_lambda_for_approved",
"ssm_lambda_parameters_for_approved.$": "$$.Execution.Input.ssm_lambda_parameters_for_approved",
"ssm_lambda_for_reject.$": "$$.Execution.Input.ssm_lambda_for_reject",
"ssm_lambda_parameters_for_reject.$": "$$.Execution.Input.ssm_lambda_parameters_for_reject",
"ssm_notification_arn.$": "$$.Execution.Input.ssm_notification_arn",
"ssm_approvers.$": "$$.Execution.Input.ssm_approvers",
"ssm_min_required_approvals.$": "$$.Execution.Input.ssm_min_required_approvals",
"ssm_message.$": "States.Format('Approval request has been received. Please review file {} at the following URL to decide whether to approve or deny. URL: {}', $$.Execution.Input.confirmation_file, $$.Execution.Input.confirmation_url)"
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"Next": "Fail"
}
],
"Next": "ApprovalResult"
},
"ApprovalResult": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.is_approved",
"BooleanEquals": true,
"Next": "Approved"
},
{
"Variable": "$.is_approved",
"BooleanEquals": false,
"Next": "Rejected"
}
],
"Default": "Rejected"
},
"Approved": {
"Type": "Succeed"
},
"Rejected": {
"Type": "Succeed"
},
"Fail": {
"Type": "Fail"
}
}
}
StepFunctionsWithSsmAutomationApprovalRole:
Type: AWS::IAM::Role
DependsOn:
- LambdaForSsmStartAutomationExecution
- LambdaForReceivingAutomationResult
Properties:
RoleName: !Sub 'IAMRole-StepFunctionsWithSsmAutomationApproval'
Path: /
MaxSessionDuration: 43200
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- states.amazonaws.com
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: !Sub 'IAMPolicy-StepFunctionsWithSsmAutomationApproval'
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Sub '${LambdaForSsmStartAutomationExecution.Arn}:*'
- !Sub '${LambdaForReceivingAutomationResult.Arn}:*'
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Sub '${LambdaForSsmStartAutomationExecution.Arn}'
- !Sub '${LambdaForReceivingAutomationResult.Arn}'
- PolicyName: CloudWatchLogsDeliveryFullAccessPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:DescribeResourcePolicies
- logs:DescribeLogGroups
- logs:GetLogDelivery
- logs:CreateLogDelivery
- logs:DeleteLogDelivery
- logs:UpdateLogDelivery
- logs:ListLogDeliveries
- logs:PutResourcePolicy
Resource:
- '*'
- PolicyName: XRayAccessPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource:
- '*'
StepFunctionsWithSsmAutomationApprovalLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/vendedlogs/states/Logs-StepFunctionsWithSsmAutomationApproval
Outputs:
Region:
Value:
!Ref AWS::Region
StepFunctionsInputExample:
Description: "AWS Step Functions Input Example"
Value: !Sub |-
{
"region": "${AWS::Region}",
"ssm_parent_doc_name": "${SsmParentDocumentApprovalAction}",
"ssm_automation_assume_role": "${SsmAutomationAssumeRole.Arn}",
"ssm_child_doc_name": "${SsmChildDocumentForApprovalAction}",
"ssm_description": "Automation Approval Action For AWS Step Functions.",
"ssm_lambda_for_approved": "${LambdaForReceivingAutomationResult}",
"ssm_lambda_parameters_for_approved": "Approved",
"ssm_lambda_for_reject": "${LambdaForReceivingAutomationResult}",
"ssm_lambda_parameters_for_reject": "Rejected",
"ssm_notification_arn": "${SnsAutomationApprovalNotification}",
"ssm_approvers": "${SsmApprovers}",
"ssm_min_required_approvals": "${SsmMinRequiredApprovals}",
"confirmation_url": "https://hidekazu-konishi.com/",
"confirmation_file": "index.html"
}
構築手順
- AWS Step FunctionsやAWS Systems Manager Automationをサポートしているリージョンで、テンプレートのパラメータに必要な値を入力してAWS CloudFormationでデプロイする。
AWS CloudFormationスタック作成後にOutputフィールドへAWS Step Functions実行時の入力パラメータ例(JSON形式)がStepFunctionsInputExampleとして出力されるのでメモしておく。 - 入力したEmailアドレスにSNSトピックのサブスクリプション承認リクエストが届くので承認しておく。
デモの実行
上記「構築手順」でメモした
StepFunctionsInputExampleのJSONパラメータのうち、confirmation_urlとconfirmation_fileを修正し、AWS Step FunctionsステートマシンStepFunctionsWithSsmAutomationApprovalの入力値にして実行する。
confirmation_urlとconfirmation_fileはAWS Systems Manager Automation承認アクションのメールに記載されます。confirmation_urlが承認するために参照するURL、confirmation_fileが承認するために参照するURL中にあるファイルを想定しています。例えば、confirmation_urlにAmazon S3コンソールへのURL、confirmation_fileにAmazon S3オブジェクトのファイル名を記載することなどが考えられます。構築時に指定したEmailアドレスにAWS Systems Manager Automation承認アクションのメールが届くので、承認(
Approve)するか拒否(Reject)するかをAWSマネジメントコンソールから選択する。- AWS Step Functionsステートマシン
StepFunctionsWithSsmAutomationApprovalのステップが選択した承認(Approve)、拒否(Reject)の通りに遷移することを確認する。
削除手順
- 「構築手順」で作成したAWS CloudFormationスタックを削除する。
参考:
What is AWS Step Functions? - AWS Step Functions
AWS Systems Manager Automation - AWS Systems Manager
What is AWS CodePipeline? - AWS CodePipeline
Tech Blog with related articles referenced
まとめ
今回はAWS Systems Manager Automationの承認アクションを使用してAWS Step Functionsのワークフローへ承認フローを追加する方法を試しました。
この方法を使用することで、AWS Step Functionsのワークフローに柔軟な承認プロセスを組み込むことが確認できました。
AWS Systems Manager Automationの承認アクションとIAMロールを利用することで、承認者の認証と承認アクションの権限も制御できできます。
また、承認が許諾された場合に加えて、承認が拒否された場合にも適切に継続的な処理を行うことができ、複雑な承認フローが必要なプロセスにも対応可能です。
次のステップとして、この承認フローを他のAWSサービスと連携させたり、複数の承認ステップを持つ多段階承認フローへ拡張したりすることも考えられます。
AWSのサーバーレスサービスを組み合わせることで、メンテナンスの手間を最小限に抑えつつ、柔軟で応用可能な承認ワークフローを実現できることが分かりました。
今後もこのようなAWSサービスを使用したアプローチによる承認ワークフロー管理を試していきたいと思います。
次回は今回試したAWS Step Function承認フローを別のAWS Step Functionsのワークフローから呼び出して多段階承認フローを作成する方法を紹介したいと思います。
- [English Edition] How to Add an Approval Flow to AWS Step Functions Workflow (AWS Systems Manager Automation Edition)
