Step FunctionsからLambda関数を実行する書き方2つの違い

2021.06.30

Step FunctionsからLambda関数を実行したい場合、ASL(Amazon State Languageの略。ステートマシンの実装をこの形式で書く)の書き方が2つあります。この違いを調べました。

方法1: Resourceに関数のARNを直接書く

{  
   "StartAt":"CallLambda",
   "States":{  
      "CallLambda":{  
         "Type":"Task",
         "Resource":"arn:aws:lambda:ap-northeast-1:123456789012:function:MyFunction",
         "End":true
      }
   }
}

方法2: Parameters.FunctionNameに関数名を書く。Resourceは arn:aws:states:::lambda:invoke 固定

{  
   "StartAt":"CallLambda",
   "States":{  
      "CallLambda":{  
         "Type":"Task",
         "Resource":"arn:aws:states:::lambda:invoke",
         "Parameters":{  
            "FunctionName":"MyFunction"
         },
         "End":true
      }
   }
}

FunctionNameの値は、関数のARNでも可能です。

{  
   "StartAt":"CallLambda",
   "States":{  
      "CallLambda":{  
         "Type":"Task",
         "Resource":"arn:aws:states:::lambda:invoke",
         "Parameters":{  
            "FunctionName":"arn:aws:lambda:ap-northeast-1:123456789012:function:MyFunction"
         },
         "End":true
      }
   }
}

以下、この2つの書き方によって生じる違いを4つご紹介します。

違い1: コールバックタスクが書ける/書けない

コールバックタスクとは、ステートマシン実行を一時中断できる機能です。例えば外部システムをワークフローの途中で呼び出す必要があったとして、外部システムでの処理完了までステートマシン実行を一時中断し、完了したら再開する、というようなことが実現できます。他にも、人による承認ステップをステートマシン内に組み込みたい場合などにも役立ちます。

Lambda関数実行でコールバックタスクを作成する場合、Resourceフィールド末尾に.waitForTaskTokenの指定が必要で、この書き方が許されているのは方法2のようにarn:aws:states:::lambda:invokeと書いている場合のみです。(つまりarn:aws:states:::lambda:invoke.waitForTaskTokenとなります)

違い2: handlerメソッドのevent引数値の渡し方が異なる

「方法1: Resourceに関数のARNを直接書く」の場合、Parametersフィールドに書いた値がそのままLambda関数handlerメソッドのeventオブジェクトに格納されます。

{  
   "StartAt":"CallLambda",
   "States":{  
      "CallLambda":{  
         "Type":"Task",
         "Resource":"arn:aws:lambda:ap-northeast-1:123456789012:function:MyFunction",
         "Parameters": {
            "hoge": "hoge val"
         },
         "End":true
      }
   }
}

MyFunction

exports.handler = (event, _context, callback) => {
  console.log(event); // { hoge: 'hoge val' }
  callback(null, { "ret": "ret val" });
};

一方、方法2の場合は階層が一つ深くなります。Parameters.Payload以下に書きます。

{  
   "StartAt":"CallLambda",
   "States":{  
      "CallLambda":{  
         "Type":"Task",
         "Resource":"arn:aws:states:::lambda:invoke",
         "Parameters":{  
            "FunctionName":"MyFunction",
            "Payload": {
               "hoge": "hoge val"
            },
         },
         "End":true
      }
   }
}

違い3: Stateの出力が変わる

「方法1: Resourceに関数のARNを直接書く」の場合、Stateの出力は関数の返り値と同じになります。

一方、方法2の場合は、関数の返り値はState出力のJSONのPayloadキー以下に格納されます。他のキーにはメタデータが格納されます。

方法2のState出力例

{
  "ExecutedVersion": "$LATEST",
  "Payload": {
    "ret": "ret val"
  },
  "SdkHttpMetadata": {
    "AllHttpHeaders": {
      "X-Amz-Executed-Version": [
        "$LATEST"
      ],
      "x-amzn-Remapped-Content-Length": [
        "0"
      ],
      "Connection": [
        "keep-alive"
      ],
      "x-amzn-RequestId": [
        "91a1f8c6-47c5-4f79-8a62-d2ae2c61cfe2"
      ],
      "Content-Length": [
        "17"
      ],
      "Date": [
        "Wed, 30 Jun 2021 09:03:53 GMT"
      ],
      "X-Amzn-Trace-Id": [
        "root=1-60dc3378-1f4e87c55ff9020c76c3bd63;sampled=0"
      ],
      "Content-Type": [
        "application/json"
      ]
    },
    "HttpHeaders": {
      "Connection": "keep-alive",
      "Content-Length": "17",
      "Content-Type": "application/json",
      "Date": "Wed, 30 Jun 2021 09:03:53 GMT",
      "X-Amz-Executed-Version": "$LATEST",
      "x-amzn-Remapped-Content-Length": "0",
      "x-amzn-RequestId": "91a1f8c6-47c5-4f79-8a62-d2ae2c61cfe2",
      "X-Amzn-Trace-Id": "root=1-60dc3378-1f4e87c55ff9020c76c3bd63;sampled=0"
    },
    "HttpStatusCode": 200
  },
  "SdkResponseMetadata": {
    "RequestId": "91a1f8c6-47c5-4f79-8a62-d2ae2c61cfe2"
  },
  "StatusCode": 200
}

違い4: 関数実行方法が限定される/されない

方法2では、Parametersフィールド以下に以下の3パラメーターを指定することによって、より色々な関数実行方法が実現できます。(だからEventオブジェクトに値を渡す時はPayloadに一つ階層下げないといけないんですね)

方法1では使えません。

ClientContext

すみませんよくわかっていませんが、モバイルアプリでクライアントアプリケーションによってLambdaに送信されたクライアントコンテキストの情報を格納するパラメーターだそうです。

Lambdaのhandlerメソッドの第2引数contextclientContextプロパティで値が確認できます。

InvocationType

Lambda関数の呼び出し方の指定。デフォルトはRequestResponseで、同期呼び出し、つまり関数が結果を返すまでStepFunctionsは待ちます。Eventにすると非同期呼び出しになり結果を待ちません。(DryRun - Validate parameter values and verify that the user or role has permission to invoke the function. というのもあります)

Qualifier

関数のバージョンやエイリアスを指定するためのパラメーター。ですがこのパラメーターが使えなくても、関数のARNや名前を書く際の末尾に:(バージョン番号 or エイリアス名) と入れれば任意のバージョンやエイリアスを指定できるので特に困らないかなと思います。

蛇足: CDKでの書き分け方

Lambda関数のStepFunctionsからの実行をCDKで書く場合、@aws-cdk/aws-stepfunctions-tasksのLambdaInvoke constructを利用できます。こちらを使う場合、Construct PropsのpayloadResponseOnly値をtrueにすると「方法1: Resourceに関数のARNを直接書く」の書き方になり、false(デフォルト)にすると方法2になります。

まとめ

Step FunctionsからLambda関数を実行する書き方2つと、その違いをご紹介しました。

基本的に「方法1: Resourceに関数のARNを直接書く」を使うほうがシンプルで良いかと思います。上に挙げた諸々の「方法1だと実現できない部分」が必要な場合のみ、方法2を使いましょう。

参考情報