🌟

AWS SAMでStep Functionsワークフローを外部定義する

2023/12/30に公開

はじめに

AWS::StepFunctions::StateMachineリソースを使用して、Step FunctionsのワークフローをSAMテンプレート内に直接インラインで記述していましたが、AWS::Serverless::StateMachineリソースを用いて、SAMテンプレートの外でワークフローを定義するように変更しました。この変更に関する手順を本記事にまとめました。なお、テンプレートはAWSの公式ドキュメントを参考にしつつ、一部を変更しています。
https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-lambda-state-machine-cloudformation.html

対応前のテンプレート

tempalte.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "An example template with an IAM role for a Lambda state machine."
Resources:
  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"

  MyLambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Handler: "index.handler"
      Role: !GetAtt [ LambdaExecutionRole, Arn ]
      Code:
        ZipFile: |
          exports.handler = (event, context, callback) => {
              callback(null, "Hello World!");
          };
      Runtime: "nodejs18.x"
      Timeout: "25"

  StatesExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - !Sub states.${AWS::Region}.amazonaws.com
            Action: "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: StatesExecutionPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "lambda:InvokeFunction"
                Resource: "*"

  MyStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      DefinitionString:
        !Sub
          - |-
            {
              "Comment": "A Hello World example using an AWS Lambda function",
              "StartAt": "HelloWorld",
              "States": {
                "HelloWorld": {
                  "Type": "Task",
                  "Resource": "${lambdaArn}",
                  "End": true
                }
              }
            }
          - {lambdaArn: !GetAtt [ MyLambdaFunction, Arn ]}
      RoleArn: !GetAtt [ StatesExecutionRole, Arn ]

対応後のテンプレート

tempalte.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "An example template with an IAM role for a Lambda state machine."
Resources:
  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"

  MyLambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Handler: "index.handler"
      Role: !GetAtt [ LambdaExecutionRole, Arn ]
      Code:
        ZipFile: |
          exports.handler = (event, context, callback) => {
              callback(null, "Hello World!");
          };
      Runtime: "nodejs18.x"
      Timeout: "25"

  StatesExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - !Sub states.${AWS::Region}.amazonaws.com
            Action: "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: StatesExecutionPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "lambda:InvokeFunction"
                Resource: "*"

  MyStateMachine:
    Type: "AWS::Serverless::StateMachine"
    Properties:
      DefinitionUri: statemachine/helloWorld.asl.json
      DefinitionSubstitutions:
        lambdaArn: !GetAtt [MyLambdaFunction, Arn]
      Role: !GetAtt [ StatesExecutionRole, Arn ]

具体的な対応内容

  1. リソースタイプをAWS::StepFunctions::StateMachineからAWS::Serverless::StateMachineに変更しました。
(対応前)
MyStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
(対応後)
MyStateMachine:
    Type: "AWS::Serverless::StateMachine"
  1. プロパティDefinitionStringを使用してワークフローをテンプレート内に直接インラインで記述していましたが、これをDefinitionUriに変更し、ASLで記述されたファイルのS3のURIまたはローカルファイルパスを指定するように変更しました。なお、今回の変更ではローカルファイルパスを使用しました。
(対応前)
  MyStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      DefinitionString:
        !Sub
          - |-
            {
              "Comment": "A Hello World example using an AWS Lambda function",
              "StartAt": "HelloWorld",
              "States": {
                "HelloWorld": {
                  "Type": "Task",
                  "Resource": "${lambdaArn}",
                  "End": true
                }
              }
            }
(対応後)
  MyStateMachine:
    Type: "AWS::Serverless::StateMachine"
    Properties:
      DefinitionUri: statemachine/helloWorld.asl.json

ワークフローの定義内容

statemachine/helloWorld.asl.json
{
  "Comment": "A Hello World example using an AWS Lambda function",
  "StartAt": "HelloWorld",
  "States": {
    "HelloWorld": {
      "Type": "Task",
      "Resource": "${lambdaArn}",
      "End": true
    }
  }
}
  1. 外部で定義されたワークフロー内のプレースホルダー変数に値を挿入するために、プロパティDefinitionSubstitutionsを使用して、文字列対文字列のマップ形式でマッピングを指定しています。
(対応前)
  MyStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      DefinitionString:
        !Sub
          - |-
            {
             <--省略-->
            }
          - {lambdaArn: !GetAtt [ MyLambdaFunction, Arn ]}
(対応後)
  1. プロパティRoleArnRoleに変更する。
(対応前)
  MyStateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      <--省略-->
      RoleArn: !GetAtt [ StatesExecutionRole, Arn ]
(対応後)
  MyStateMachine:
    Type: "AWS::Serverless::StateMachine"
    Properties:
      <--省略-->
      Role: !GetAtt [ StatesExecutionRole, Arn ]

おわりに

リソースタイプの変更に伴い、プロパティの名称が変更される場合や互換性のないものがあるため、変更内容を確認するためには公式ドキュメントを参照する必要があります。また、sam deployを実行する際には、互換性のないプロパティが定義されている場合にはエラーが発生し、その詳細はエラーメッセージで確認できます。
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-statemachine.html#sam-resource-statemachine--examples

Discussion