小西秀和です。
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)