はじめに
前回はEC2、ECS、RDSをまとめて停止起動するStep Functionsを作成する前の事前作業として、Change Calendar、EventBridgeの作成と、Step Functionsの雛形を作成しました。
今回から数回にわたり、本題となるJSONataを使用したStep Functionsの解説を行っていき、初回となる今回はJSONataの基本操作を解説していこうと思います。
- 【前】EC2、ECS、RDSをまとめて停止起動するStep Functionsを作成する。(その1:事前準備)
- 【次】EC2、ECS、RDSをまとめて停止起動するStep Functionsを作成する。(その3:カレンダー判定と停止処理)
Step Functionsとは
JSONataを使ったコードを解説する前に、Step Functionsについてざっくり説明しておきます。
Step FunctionsとはAWSのサービスの一つで、以下のようなグラフィカルな画面上に各種AWSのリソースを操作するためのアクションや条件分岐など行うためのフローを配置して繋げることで、一連のフローを実行できるサービスとなります。
配置できるアクションは、基本的にAWS CLIでも実行できる制御コマンドのため、いちいちAWS CLIとシェルで一連の処理を書かなくてもグラフィカルな画面上で操作できるものと考えると、イメージしやすいかと思います。
また、Visual Studio Codeにも拡張機能があるため、ローカルでもコードを書くことは可能です。(フロー全体を実行するにはAWSへのデプロイが必要)
Visual Studio Codeの拡張機能について知りたい方は以下も参照してください。
JSONataについて
JSONata(ジェイソナタ)はJSON形式の書式を変換したり変数を使った制御を行ったりできるオープンソースのクエリおよび変換言語となります。
JSONata自体はStep Functions専用の言語ではなく、オープンソースで公開されている言語となるため、詳しい使い方や関数を調べるなら以下のJSONataドキュメントが参考になります。
※ただし、Step Functionsで導入されていない関数等もあるので、ご注意ください。
Step Functionsでは以前はJSONPathという言語を使用しておりましたが、Step Functionsで使用する場合、InputPath
、ResultSelector
、ResultPath
、OutputPath
などを使ってステップ間で値の共有や引き渡しなどを行う必要があるのですが、たくさん項目がある分、いつもこんがらがってしまい、正直なところ、私はいつも難儀していました。。。
JSONataの場合、変数を使った値の引き渡しや条件式など、個人的には一般的なスクリプトや言語の書き方に似ていることからイメージしやすくコードが書きやすいです。
また、JSONPathよりも文字列変換等、行えることが増えているため、JSONPathでの変換では実現できずにLambda等の処理を挟んで実行していたフローが、Step Functionsのみで実現できる場合が増えたことが大きなメリットとなります。
JSONataを使用したStep Functionsの基本
以下よりJSONataを使用したStep Functionsの基本について説明します。
各アクションで設定できるパラメータについて
JSONata形式でのアクションで設定できるパラメータについてはシンプルで、大きく分けて以下のような項目があります。
項目 | 内容 |
---|---|
設定 | 各ステップの名前やクロスアカウント等の設定 |
引数 | 各アクションで指定するオプションの指定 |
出力 | 各アクションの出力結果の整形 |
変数 | 後のステップで使用する変数の設定 |
「設定」についてはステップ名の指定程度となるので基本的にはあまり触らない項目となります。
「引数」はAWS CLIで言うと各オプション指定の項目となるため、リファレンスを見ながら必要な値を指定します。
Step Functionsで複雑な処理を行う上でメインとなるのは「出力」と「変数」となり、どちらも後のステップで使用する値を整形したり、変数に格納して後のステップで使用したりする項目となります。
JSONataの使用方法
Step FunctionsでJSONataを使用するためにはStep Functionsのクエリ言語指定でJSONataを指定し、以下の書式でJSONataの構文を指定することで使用できます。
"{% <JSONata構文> %}"
Step Functionsの予約変数
Step FunctionsではJSONataの予約変数として$states
という変数が予約されております。
$states
は以下のような構造体となっており、以下を使用することで、コマンド入力・出力結果の取得やエラー出力の取得などを行うことができます。
変数 | 説明 |
---|---|
$states.input |
入力結果の取得 |
$states.result |
出力結果の取得 |
$states.errorOutput |
エラー出力結果の取得 |
$states.context |
ワークフロー実行情報の取得 |
現状、上記4つが予約変数として定義されておりますが、使う機会が多いのは$states.input
と$states.result
となるかと思うので、この2つの使い方は覚えておくようにしましょう。
JSONataによる整形
以下のS3バケットのリストを取得する簡単なStep Functionsのワークフローを元にJSONataによる整形の例を説明しようと思います。
上記のワークフローを単純に実行した場合、以下のようにS3バケットのリストを取得することができます。
{
"Buckets": [
{
"CreationDate": "2024-12-07T05:12:39Z",
"Name": "test-bucket1"
},
{
"CreationDate": "2025-01-04T06:18:54Z",
"Name": "test-bucket2"
}
],
"Owner": {
"DisplayName": "testuser",
"Id": "e299f5e040ca4632ff21241ab7419fd4862af86f00171d09413909da82f58a5d"
}
}
上記の結果をそのまま次のステップに引き渡す場合は特に何もする必要はありませんが、例えば次のステップでS3バケットの名前だけ使いたい場合、CreationDate
やOwner
などの情報は不要となります。
そのため、必要な情報のみ取得するためにJSONataを使用して整形することで、必要な情報のみに絞って後ろのステップに渡すことが可能です。
いくつか方法はありますが、今回の場合、「ListBuckets」のアクションの「出力」に、以下のように記載します。
{
"Output": "{% $states.result.Buckets[].Name %}"
}
今回の場合、「ListBuckets」の結果を整形して次のステップに渡したいため、「出力」に整形するための構文を記載します。
また、キーを指定しないとJSON構文エラーとなってしまうため、適当にOutput
という名前のキーを指定します。
値にはStep Functionsの予約変数のうち、$states.result
を使用して「ListBuckets」の結果を取得します。
取得には、JSONataの知識というよりはJSONの知識が必要となりますが、JSON的には$states.result
に「ListBuckets」の結果がネストされるような形となるため、実際には以下のような構造となります。
{
"states": {
"result": {
"Buckets": [
{
"CreationDate": "2024-12-07T05:12:39Z",
"Name": "test-bucket1"
},
{
"CreationDate": "2025-01-04T06:18:54Z",
"Name": "test-bucket2"
}
],
"Owner": {
"DisplayName": "testuser",
"Id": "e299f5e040ca4632ff21241ab7419fd4862af86f00171d09413909da82f58a5d"
}
}
}
}
JSON構文では、{}
で囲まれたものはオブジェクトと言い、[]
で囲まれたものは配列と言います。
JSON構文で特定の要素を取得する場合、ネストされているオブジェクトは.
で表し、配列は[]
で表すことで、配下のオブジェクトを指定することができ、上記のJSONのS3バケット名を表す場合、states.result.Buckets[].Name
がS3バケット名の要素を表す表記となります。
Step FunctionsのJSONataで取得する場合は{% $変数名 %}
の形式となることから、Output
という変数に、「ListBuckets」の実行結果を格納する場合、上述の内容となります。
上述の整形方法で行った出力結果が以下となります。
{
"Output": [
"test-bucket1",
"test-bucket2"
]
}
変数の指定
前のステップでの結果を使って処理を行う場合、1つ前のステップであれば$states.input
を使用すれば1つ前のステップの結果を取得することができますが、2つ以上前のステップの値は取得できません。
そのため、2つ以上前のステップの結果を後ろのステップで使いたい場合は変数を使って結果を格納しておく必要があります。
変数を使用する場合は上述の通り{% $変数名 %}
とすることで呼び出すことができるため、例えばOutput
というキーの値にvariable1
という変数を指定した場合は以下のようになります。
{
"Output": "{% $variable1 %}"
}
おわりに
JSONの予備知識が無い場合、最初は悩むかと思いますが、それでもJSONPath形式のやり方を覚えるよりは大分とっつきやすいと思います。
JSONについてある程度理解すればJSONata自体はシェルスクリプトなどの概念と似ているため、思ったStep Functionsのワークフローが書きやすいと思います。
次回はJSONataを使って今回作成したワークフローの中身を解説していこうと思います。