Step Functionsを使ってみた

# Step Functions使ってみた
仕事でStep Functionsを利用することになり、既に別サービスが稼働しているため私用パソコン・個人AWSアカウントを用いて動作の確認をしてみました。

## ソースコード一式
私のGitHubの下記リポジトリに保存しております。
設定や事前準備は`src/README.md`を確認してください。
(AWS認証情報などは消しております)
[aws-step_functions](https://github.com/k-genki0913/aws-step_functions)

作成したStep Functionsのワークフローは以下になります。
<img width=30% src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763467/09b00558-6e79-7c79-1fc0-1f9fbeb1933f.png>


### Step Functionsに関して調べたこと
※全体のソースコードはリポジトリを見て下さい。

#### ▪️sam template.yamlとStep Functionsの定義ファイル(Amazon State Language)を分ける

template.yamlにStep Functionsのステートの設定(ワークフローの流れ)を
記載することができます。
ただ、template.yamlのコード数が増えリソースの定義以外も含めると
template.yamlの役割が複数になるため下記のファイル構成にしました。
・ビルドで利用するAWSリソースの定義ファイル(template.yaml)
・Step Functionsの定義ファイル(sfn.asl.json)

Step Functionsの定義ファイルとtemplate.yamlは
template.yamlの`DefinitionUri`の部分で連携してます。
※参考サイト
https://blog.serverworks.co.jp/build-step-functions-state-machine-with-aws-sam-template
```yaml: template.yaml
TestStepFunction:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: sfn.asl.json
```

#### ▪️sam template.yamlとStep Functionsの定義ファイル(Amazon State Language)を連携する

Step FunctionsでLambdaを利用することは多いと思いますが、
template.yamlと連携しない場合はStep Functionsの定義ファイルに
LambdaのARN(リソースネーム)を直接設定する必要があります。

ただそれだと利便性が悪いためLambdaなどを定義するtemplate.yamlと
Step Functions定義ファイルを連携しました。
※参考サイト
https://blog.serverworks.co.jp/build-step-functions-state-machine-with-aws-sam-template

template.yamlのStep Functionsを記載している部分の`DefinitionSubtitutions`の
キー名が`sfn.asl.json`の`Resource`で変数として利用できます。

```yaml: template.yaml
TestStepFunction:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: sfn.asl.json
      DefinitionSubstitutions:
        StateLambda1: !GetAtt StateLambda1.Arn
        StateLambda2: !GetAtt StateLambda2.Arn
```
```json: sfn.asl.json
"States": {
    "StateLambda1": {
      "Type": "Task",
      "Resource": "${StateLambda1}",
      ---- 省略 ----
    "StateLambda2": {
      "Type": "Task",
      "Resource": "${StateLambda2}",
      ---- 省略 ----
```

#### ▪️Lambdaの処理結果を次のLambdaに連携する
Step Functionsではワークフローを流れていくデータがある(とイメージしております。。)
そのため、そのデータにLambdaの処理結果をどのように追加するかを
`sfn.asl.json`の`ResultPath`で設定しております。
※参考サイト
https://dev.classmethod.jp/articles/stepfunctions-parameters-inter-states/

```json: sfn.asl.json
"StateLambda1": {
      "Type": "Task",
      "Resource": "${StateLambda1}",
      --- 省略 ---
      "ResultPath": "$.result",
```

`$`がワークフローを流れていくデータを指すことになります。
そのため`$.result`ではLambdaの処理結果が以下のように追加されます。

```javascript
{
  result: Lambda処理結果オブジェクト
}
```
処理結果は`return`したデータが`result`に格納されます。
この際にプリミティブ型を`return`するとエラーになるため必ずオブジェクト形式にしてください。

#### ▪️2つのLambdaで同一のResultPathを指定した場合
2つのLambdaの`ResultPath`が同じパスを指定している場合、
2つ目のLambdaが`return`したときにデータは上書きされます。

リポジトリのLambdaでは1つ目のLambdaで`$.result`に以下のデータを格納しております。
```javascript
{
    count: 2,
    users: [
      {
        user_id: 1,
        user_name: 'test1',
      },
      {
        user_id: 2,
        user_name: 'test2',
      },
    ],
  };
  ```

2つ目のLambdaの入力は上記になります。
2つ目のLambdaでは`$.result`に以下のデータを格納しております。
```javascript
{ count: event.result.count };
```
この場合、もし次のLambdaなどがあった場合、`$.result`には以下だけになります。
```javascript
{ count: event.result.count };
```
データの追加ではなくデータの上書きになります。

#### ▪️Lambdaの処理ではなく`Pass`ステートでデータを上書きする
Lambdaを使用しなくてもワークフローを流れていくデータを上書き or 追加できます。
今回は`Pass`ステートで`$.result`を上書きしてます。

`Result`で設定したオブジェクト形式(JSON形式？)で
`ResultPath`を上書きしてます。
```json: sfn.asl.json
"Call": {
      "Type": "Pass",
      "Result": {
        "count": 0
      },
      "ResultPath": "$.result",
      "Next": "Success"
    }
```

#### ▪️DefinitionUriの場所について
`deploy`時にtemplate.yamlのStep Functionsの`DefinitionUri`の記載位置が原因でエラーとなりました。
`Properties`のすぐ下に`DefinitionUri`か`Definition`がないとエラーとなりました。

エラーコード
```yaml: template.yaml
TestStepFunction:
    Type: AWS::Serverless::StateMachine
    Properties:
      Name: TestStepFunction
      DefinitionUri: sfn.asl.json
```

エラーメッセージ
```
Either 'Definition' or 'DefinitionUri' property must be specified Step Functions
```

`deploy`できたコード
```yaml: template.yaml
TestStepFunction:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: sfn.asl.json
```

色々なサイトを見ても`Properties`に`Name`があり`DefinitionUri`も設定しているので
なんでエラーメッセージで`Definition` or `DefinitionUri`どっちか指定しろって
怒られてるんやろってなってました。。。
順番が大事なのか。。。

### 使ってみて
色々なサイトでStep Functionsの設定など記載がありましたが
基礎の基礎が逆に少なく、なぜその設定をしているのかが少なかったため
調べることに時間がかかりました。
この記事が未来の自分自身や
AWSほぼ触ったことないけど仕事で使うことになったから知りたいけどお金かかる不安やデプロイなどわからない人の
役に立つと幸いです。