[AWS] Step Functionsで遊んでみる（SAM + Lambda） Part.2（パラメータ編）

# Part.1のおさらい
[Part1](https://qiita.com/herohit-tool/items/a43044383832ed04ffba)では、主に以下のことを行いました。

- Lambda関数を作成する（SAM）
- Lambda関数をデプロイする（SAM）
- ステートマシンを構成する
- Lambda関数を呼び出す（だけ）
- ステートマシンをデプロイする（SAM）
- ステートマシンを実行する（マネジメントコンソール）

今回は、その続きからはじめてみます。
ここから始める方は、
https://github.com/hito-psv/sam-demo-004
のコードを`git clone`してもらっても大丈夫ですし、[Part1](https://qiita.com/herohit-tool/items/a43044383832ed04ffba)からやってみてもらっても構いません。

# 今回のターゲット
- Lambdaに固定引数を渡してみる
- Lambdaに外部からの引数を渡してみる
- Lambdaにコンテキストオブジェクトを渡してみる
- Lambdaの戻り値を、別のTaskの引数に渡してみる

をターゲットにしてみたいと思います。
Lambda部分については、[Part1](https://qiita.com/herohit-tool/items/a43044383832ed04ffba)で作成した「HelloWorld」の関数を少しだけいじって、色々試してみたいと思います。

# 準備
## Lambda関数を修正する
基本的にHelloWorldのままですが、引数の内容を確認するために、CloudWatchに引数の内容を出力する記述だけ追記してみましょう。
`hello_world/app.py`はテンプレートから作成されたデフォルトのままになっているので、リファクタリングも含めて、以下のようにしてみます。

```python:hello_world/app.py
import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info(event)
    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "hello world",
        })
    }
```

## ビルド・デプロイする
この状態で、`sam build`、`sam deploy --guided`を実行し、Lambda関数をデプロイしましょう。

# Lambdaに固定引数を渡してみる
## ステートマシン定義の修正
まずは、固定のパラメータですが、ステートマシンの定義を変更する必要があります。
変更点としては、`Parameters`を追加することです。

```json:step_functions/state_machine.json
{
    "StartAt": "hello world",
    "States": {
      "hello world": {
        "Type": "Task",
        "Resource": "${HelloWorldFunction}",
        "Parameters": {
          "p1": 100,
          "p2": "0123",
          "p3": {
            "p3-1": 20,
            "p3-2": "xyz"
          }
        },
        "End": true
      }
    }
  }
```

## ビルド・デプロイする
この状態で、`sam build`、`sam deploy --guided`を実行し、デプロイしましょう。

## ステートマシンを実行してみる
マネジメントコンソールから、ステートマシンを実行してみましょう。
今回は、ステートマシンの定義に渡すパラメータを固定でセットしているので、入力は適当で構いません。

![sf1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/8c5b55fc-efd4-125d-9668-579eea84071b.png)

## CloudWatch Logsを確認してみる
ステートマシンから、実行したLambda関数のCloudWatch Logsに遷移できるので、そこからログの中身をチェックしてみましょう。

![cw1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/ffdfc1f7-3a1c-a76d-2c0d-b0514cf23a7c.png)

Lambda関数の第一引数`events`に、ステートマシン定義でセットしたパラメータが渡されてきていることが確認できると思います。

# Lambdaに外部からの引数を渡してみる
## ステートマシン定義の修正
続いて、ステートマシンへの入力値をパラメータに使用するために、ステートマシン定義を修正します。
ポイントは、

- Key部が「.$」で終わっているもののみが、入力値を使用できる
- Value部は「$.」の後ろに、入力パラメータのKey名を指定する
- 入力値を渡すKeyのValue部に「$」とすると、全入力値をパラメータとして渡すことができる

です。では、これらを考慮して修正してみます。

```json:step_functions/state_machine.json
{
    "StartAt": "hello world",
    "States": {
      "hello world": {
        "Type": "Task",
        "Resource": "${HelloWorldFunction}",
        "Parameters": {
          "p1.$": $.p1,
          "p2.$": "$.p2",
          "p3": {
            "p3-1": 20,
            "p3-2": "xyz"
          },
          "all.$": "$"
        },
        "End": true
      }
    }
  }
```

## ビルド・デプロイする
この状態で、`sam build`、`sam deploy --guided`を実行し、デプロイしましょう。

## ステートマシンを実行してみる
マネジメントコンソールから、ステートマシンを実行してみましょう。
今回は、p1、p2を使用するため、入力蘭を以下のように指定してみます。

![sf2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/d01885ac-22a6-b097-dd5b-15848359af47.png)

- p1に、9999数値を指定
- p2に「p2 strings.」という文字列を指定

## CloudWatch Logsを確認してみる
では、Lambda関数のCloudWatch Logsを確認してみましょう。

- p1に9999という数値
- p2に「p2 strings.」という文字列
- allに、入力値全て

がセットされていることがわかります。
順序が入れ替わっていますが、Jsonデータとして参照する分には特に問題はありません。

![cw2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/1d547d82-c403-9781-dabb-fa877ea4e47f.png)

## 入力値に、パラメータを指定しないとどうなる？
ステートマシン実行時に、

```json
{
    "Comment": "Insert your JSON here"
}
```

のように、ステートマシンの定義で必要としている「p1」や「p2」がないとどうなるかですが、ステートマシンの実行に失敗します。
この場合、Lambda関数を呼び出す前に、ステートマシンでエラー終了となります。

```json
{
  "error": "States.Runtime",
  "cause": "An error occurred while executing the state 'hello world' (entered at the event id #2). The JSONPath '$.p1' specified for the field 'p1.$' could not be found in the input '{\n    \"Comment\": \"Insert your JSON here\"\n}'"
}
```

# Lambdaにコンテキストオブジェクトを渡してみる
## ステートマシン定義の修正
同様にステートマシンを修正しますが、その前に、コンテキストオブジェクトとは？という疑問があります。
コンテキストオブジェクトとは、ステートマシンと実行に関する情報をさします。
具体的には、Roleの情報や、実行時間、タスク名などです。
コンテキストオブジェクトをLamnda関数に渡すには、

- Key部が「.$」で終わっているもののみが、入力値を使用できる
- 入力値を渡すKeyのValue部に「$$」を指定する

です。では、これらを考慮して修正してみます。

```json:step_functions/state_machine.json
{
    "StartAt": "hello world",
    "States": {
      "hello world": {
        "Type": "Task",
        "Resource": "${HelloWorldFunction}",
        "Parameters": {
          "p1.$": $.p1,
          "p2.$": "$.p2",
          "p3": {
            "p3-1": 20,
            "p3-2": "xyz"
          },
          "all.$": "$",
          "context.$": "$$"
        },
        "End": true
      }
    }
  }
```

## ステートマシンを実行してみる
マネジメントコンソールから、ステートマシンを実行してみましょう。
「p1」「p2」を指定して実行してください。

## CloudWatch Logsを確認してみる
では、Lambda関数のCloudWatch Logsを確認してみましょう。

- contextに、コンテキストオブジェクト（と思わしきもの）

がセットされていることがわかります。

![cw3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/a3176305-8594-1664-3a5e-a3da3573718a.png)

# Lambdaの戻り値を、別のTaskの引数に渡してみる
最後に、これです。
今回は、HelloWorldのLambda関数を使いまわしてみたいと思います。

## Lambda関数の修正
まず、現在のHelloWorldのLambda関数は、以下のような結果を出力します。

```json
{
  "statusCode": 200,
  "body": "{\"message\": \"hello world\"}"
}
```

実はこれだとちょっと面倒なことがあって、`body`部の結果が文字列データになってしまっているのです。
そこで、Json構造のまま返せるように、Lambda関数をちょっといじりましょう。
単純に、結果を返すときに、余計な文字列化処理を削除するだけでOKです。

```python:hello_world/app.py
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info(event)
    return {
        "statusCode": 200,
        "body": {
            "message": "hello world",
        }
    }
```

## ステートマシン定義の修正
今回は、HelloWorldのLambda関数を2回呼び出すようにします。
今回の変更点は、大きく、以下の点です。

- ステート名を「hello world 1|2」の形式に変更
- それに伴い、`StartAt`で最初のステート名を`hello world 1`に変更
- `hello world 1`に、`ResultPath`、`OutputPath`を追加
- `hello world 1`の`Next`に`hello world 2`を指定
- `hello world 2`を追加

`hello_world_1`の`ResultPath`で、Lambda関数の結果を`hello_world_result`というKey名で保持します。
その内容を`OutputPath`で、入力内容全体に反映させます。
`hello_world_2`では、`InputPath`に「$」を指定することで、`hello_world_result`を含む`hello world 1`への入力値全体を、Lambda関数に渡しています。

```json:step_functions/state_machine.json
{
  "StartAt": "hello world 1",
  "States": {
    "hello world 1": {
      "Type": "Task",
      "Resource": "${HelloWorldFunction}",
      "Parameters": {
        "p1.$": $.p1,
        "p2.$": "$.p2",
        "p3": {
          "p3-1": 20,
          "p3-2": "xyz"
        },
        "all.$": "$"
      },
      "ResultPath": "$.hello_world_result",
      "OutputPath": "$",
      "Next": "hello world 2"
    },
    "hello world 2": {
      "Type": "Task",
      "Resource": "${HelloWorldFunction}",
      "InputPath": "$",
      "End": true
    }
  }
}
```

## ステートマシンを実行してみる
マネジメントコンソールから、ステートマシンを実行してみましょう。
「p1」「p2」を指定して実行してください。

まずは、`hello world 1`、`hello world 2`と2回呼ばれているかを確認しましょう。

![sf5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/7d2d6cf3-e788-f8fe-10fa-0654a841550b.png)

## CloudWatch Logsを確認してみる
では、Lambda関数のCloudWatch Logsを確認してみましょう。

- `hello world 1`（赤枠）では、従来通りのパラメータ
- `hello_world_2`（青枠）では、`hello world 1`のパラメータに加え、`hello world 1`のレスポンス（`hello_world_result`）が付加されたパラメータ

がセットされていることがわかります。

![cw5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/4a7bfc47-2866-7d65-daed-504328334842.png)

# まとめ
今回は、パラメータの受け渡し方の方法や、結果を受け渡していく方法について検証してみました。
「$」の使い方を覚えれば、それほど難しくないかと思います。

次回は、結果による分岐などについて検証してみたいと思います。

- [[AWS] Step Functionsで遊んでみる（SAM + Lambda） Part.3（分岐編）](https://qiita.com/herohit-tool/items/440a57769f043110648b)

# サンプルコードリポジトリ
https://github.com/hito-psv/sam-demo-005