AWS Step Functions の Map 処理で出力される配列を組み込み関数で検証して、ステートマシン全体の成功・失敗の分岐をする
はじめに
こんにちは、筧( @TakaakiKakei )です。
AWS Step Functions の Map 処理の出力には配列が返されます。
今まで、こちらの配列の内容に応じて条件分岐を行う場合は、ステートマシンで "Type": "Task"
を使い、Lambda を呼び出すことが多かったと思います。
ところで、先日ステートマシン上で配列を操作できる組み込み関数が追加されました。
こちらの組み込み関数を利用すれば、Lambda を利用せずステートマシン上で条件分岐を完結できそうだなと思ったので調査してみました。 今回は調査結果を汎化してご紹介します。
結論
結論を最初に記述します。
States.ArrayContains
組み込み関数で、配列に特定の値が存在するか判断可能。- Map 処理の出力は一次元配列にして、
States.ArrayContains
組み込み関数から参照しやすいようにするのがベター。 States.ArrayContains
組み込み関数は、Type: Pass
で使い、その結果をType: Choice
で利用するのが良さそう。
やってみた
以下のようなステートマシンを作成しました。
以下がステートマシンのコードです。
name: Test-Blog-dev definition: StartAt: Map States: Map: Type: Map ItemsPath: $ MaxConcurrency: 3 Iterator: StartAt: Work States: Work: Type: Task Parameters: input.$: $ execution.$: $$ Resource: Fn::GetAtt: [work, Arn] Catch: - ErrorEquals: - States.ALL ResultPath: $.error_info Next: Error Next: Done Done: Type: Task Parameters: input.$: $ status: 'Success' execution.$: $$ ResultPath: $ # Lambda で 'Success' という文字列が return されるようにしてください Resource: Fn::GetAtt: [slack, Arn] End: true Error: Type: Task Parameters: input.$: $ status: 'Fail' execution.$: $$ ResultPath: $ # Lambda で 'Fail' という文字列が return されるようにしてください Resource: Fn::GetAtt: [slack, Arn] End: true ResultPath: $ Next: StatusPass StatusPass: Type: Pass Parameters: # Map の出力結果に Fail が含まれる場合、is_fail に true が返される。含まれない場合、is_fail に false が返される。 # refs: https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-arrays is_fail.$: States.ArrayContains($, 'Fail') Next: StatusChoice StatusChoice: Type: Choice Default: FailState Choices: - Variable: $.is_fail BooleanEquals: false # Map の出力結果がすべて Success の場合 Next: SuccessState SuccessState: Type: Succeed FailState: Type: Fail
解説
コードを3分割して解説します。 3つ目が本ブログのメインの内容です。
1~24行目: Map 処理の冒頭
name: Test-Blog-dev definition: StartAt: Map States: Map: Type: Map ItemsPath: $ MaxConcurrency: 3 Iterator: StartAt: Work States: Work: Type: Task Parameters: input.$: $ execution.$: $$ Resource: Fn::GetAtt: [work, Arn] Catch: - ErrorEquals: - States.ALL ResultPath: $.error_info Next: Error Next: Done
- Map 処理を始めから定義しています。最初は Work という Task ステートを宣言しており、work という lambda 関数を呼び出しています。work 関数の内容は任意ですので割愛します。
- work 関数が正常終了すれば、Done という Task ステートに進みます。
- work 関数で例外が発生すれば、Error という Task ステートに進みます。
25~46行目: Map 処理内の成功・失敗の分岐
Done: Type: Task Parameters: input.$: $ status: 'Success' execution.$: $$ ResultPath: $ # Lambda で 'Success' という文字列が return されるようにしてください Resource: Fn::GetAtt: [slack, Arn] End: true Error: Type: Task Parameters: input.$: $ status: 'Fail' execution.$: $$ ResultPath: $ # Lambda で 'Fail' という文字列が return されるようにしてください Resource: Fn::GetAtt: [slack, Arn] End: true ResultPath: $ Next: StatusPass
- Done という Task ステートでは、slack という lambda 関数を呼び出しています。内容は省略しますが、ここでは関数内で正常終了したことを Slack 通知する想定です。関数内容は任意ですが、後述の処理のために return で、'Success' という文字列のみを返すようにしてください。
End: true
の記述があるので、Map 処理はこちらのステートで終わっています。 - Error という Task ステートでは、slack という lambda 関数を呼び出しています。同じく内容は省略しますが、ここでは関数内で異常終了したことを Slack 通知する想定です。関数内容は任意ですが、後述の処理のために return で、'Fail' という文字列のみを返すようにしてください。
End: true
の記述があるので、Map 処理はこちらのステートで終わっています。 ResultPath: $
としているので、Map の処理結果は下記のような配列で出力され、StatusPass という Pass ステートに渡されます。
['Success','Fail','Success']
47~64行目: ステートマシン全体の成功・失敗の分岐
StatusPass: Type: Pass Parameters: # Map の出力結果に Fail が含まれる場合、is_fail に true が返される。含まれない場合、is_fail に false が返される。 # refs: https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-arrays is_fail.$: States.ArrayContains($, 'Fail') Next: StatusChoice StatusChoice: Type: Choice Default: FailState Choices: - Variable: $.is_fail BooleanEquals: false # Map の出力結果がすべて Success の場合 Next: SuccessState SuccessState: Type: Succeed FailState: Type: Fail
- StatusPass という Pass ステート では、States.ArrayContains()を利用して、Map 処理の出力の配列内に、特定の文字列が存在するかを判断します。今回の場合、'Fail'という文字列が存在する場合は ture が、'Fail'という文字列が一つも存在しない場合は false が、is_fail に渡されます。
- StatusChoice という Choice ステートでは、is_fail の真偽値を BooleanEquals で評価します。false の場合は Map の出力結果がすべて 'Success' と判断して、SuccessState に分岐して成功で終わります。ture の場合は Map の出力結果に 'Fail' が含まれると判断して、FailState に分岐して失敗で終わります。
補足
Map 処理で一部処理が失敗しても中断しない方法
Map 処理で一部処理が失敗したら他処理が中断するのでは?と思われる方もいるかもしれません。 この場合、Catch フィールドで次のステートに渡すことで中断しないようにできます。 例えば、今回のコードでは、Work という Task ステートで処理が失敗しても上記によって中断されません。 一方で、Done と Error の Task ステートで処理が失敗すると、ステートマシン全体が中断されますが、エラーが起こりづらい簡単な処理にする想定なので、ここではリスク受容しました。
States.ArrayContains組み込み関数に渡す配列
Map 処理の出力は配列です。 そして、当該関数の第一引数にはインプットとなる配列を、第二引数には検索する値をて有効な JSON オブジェクトで指定します。 もし Map 処理の出力が多次元配列だと、第一引数に指定したい配列が深い階層に出力されることになるので、当該組み込み関数を利用した検証が難しくなると感じました。 なので、以下のような一次元配列が出力されるように調整するのがベターかなと思いました。
['Success','Fail','Success']
難しかったところ
States.ArrayContains組み込み関数を使うステート
最初は、Choice ステートの StatusCheck で当該組み込み関数を利用しようとしました。 ところが、以下のような SCHEMA_VALIDATION_FAILED が発生してしまい、うまく利用できませんでした。
StatusChoice: Type: Choice Default: FailState Choices: - Variable: States.ArrayContains($, 'Fail') BooleanEquals: false Next: SuccessState
Resource handler returned message: "Invalid State Machine Definition: 'SCHEMA_VALIDATION_FAILED: Value is not a Reference Path: Reference path didn't start with '$' at ..snip..
そこで、Pass ステートの StatusPass を前段に追加して、当該組み込み関数を利用しました。
StatusPass: Type: Pass Parameters: # Map の出力結果に Fail が含まれる場合、is_fail に true が返される。含まれない場合、is_fail に false が返される。 # refs: https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-arrays is_fail.$: States.ArrayContains($, 'Fail') Next: StatusChoice StatusChoice: Type: Choice Default: FailState Choices: - Variable: $.is_fail BooleanEquals: false # Map の出力結果がすべて Success の場合 Next: SuccessState
今後、States.ArrayContains
組み込み関数を、Choice ステートで使う方法が確認できたら別途ご紹介したいと思います。
おわりに
最後まで読んでいただきありがとうございます。
組み込み関数で配列を操作できるようになったことで、Map 処理の出力結果を扱いやすくなりました。 他の組み込み関数も今後利用していきたいと思います。
それではまた!