【AWS CDK v2】Step Functions Workflow StudioとCDKを組み合わせて簡単にワークフローを作成する


## はじめに

この記事は、[ミロゴス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/milogos) 5日目の記事です。

AWS CDKを使ってStep Functionsを作成する際、ステートマシンの作成に悩んだことはないでしょうか。
AWS CDKでStep Functionsを作れても、ステートマシンの定義ファイル自体を作成することは記事作成時点ではできません。
そのためこの記事ではStep Functions Workflow Studioを使って下記の図のようなステートマシンの定義ファイルをGUIで作成しCDKの実装に取り込む方法を紹介します。

![スクリーンショット 2023-12-04 12.18.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412171/4e094284-ae28-2a98-eebb-e6b3295e1434.png)

## 環境

- AWS CDKは2.95.1を使用

```bash
cdk --version
2.95.1 (build ae455d8)
```

## 実装

### ステートマシンの構成の確認

今回作成するステートマシンは下記のものになります。
Lambdaが二つあり、入力値に応じて呼び出すLambdaを変えるシンプルな作りになっています。

![スクリーンショット 2023-12-04 12.18.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412171/76273906-4bb9-6a51-9194-68580d9381b3.png)

上記のステートマシンの定義ファイルが下記になります。
シンプルな構成のためあまり長くはないですが、より複雑なワークフローになると定義ファイルも長く複雑になり、テキストベースでの作成が困難になります。

```sample-state-machine.asl.json
{
  "Comment": "ステートマシンの説明",
  "StartAt": "Choice",
  "States": {
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.input_param",
          "StringMatches": "lambda1",
          "Next": "Lambda1 Invoke"
        },
        {
          "Variable": "$.input_param",
          "StringMatches": "lambda2",
          "Next": "Lambda2 Invoke"
        }
      ],
      "Default": "Fail"
    },
    "Lambda1 Invoke": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "OutputPath": "$.Payload",
      "Parameters": {
        "Payload.$": "$",
        "FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXX:function:sample-function1:$LATEST"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Next": "Success"
    },
    "Success": {
      "Type": "Succeed"
    },
    "Fail": {
      "Type": "Fail"
    },
    "Lambda2 Invoke": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "OutputPath": "$.Payload",
      "Parameters": {
        "Payload.$": "$",
        "FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXX:function:sample-function2:$LATEST"
      },
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException",
            "Lambda.TooManyRequestsException"
          ],
          "IntervalSeconds": 1,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Next": "Success"
    }
  }
}
```

### Step Functions Workflow Studioで定義ファイルの作成

それでは実際にWorkflow Studioで定義ファイルを作成していきます。
AWSコンソールからStep Functionsのステートマシン一覧画面を開き、右上の「ステートマシンの作成」ボタンを押すと、下記のようなテンプレート選択画面が開きます。

![スクリーンショット 2023-12-04 14.15.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412171/7ff053a8-cea9-b7fe-d7bc-36ae846aa407.png)

今回はサンプルなので、Blankを選び右下の「選択」ボタンで次に進みます。
するとWorkflow Studioの画面が開き、GUIでの編集が可能になります。

![スクリーンショット 2023-12-04 14.17.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412171/509acf22-d0a0-ea6f-0f12-b47b3771388c.png)

GUIでステートマシンを編集します。
この際、先にCDKを実装しLambdaなどのStep Functionsで使用するリソースをデプロイしておくとこの時点でGUIからリソースが選択できるので楽になります。

参考：[Workflow Studioを使用する](https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/workflow-studio-use.html)

ステートマシンの編集が終わったら、右上のアクションから「定義をエクスポート->JSONファイルとして」を選択し、定義ファイルを出力します。
次のステップで出力したファイルをCDKの実装に取り込みます。

![スクリーンショット 2023-12-04 14.25.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412171/f5884218-8cee-4c35-e3fe-8667b8bb5c68.png)

### CDKの実装

下記のサンプルコードではステートマシンの作成とステートマシンの実行に必要なRoleを作成しています。
`definition_body=DefinitionBody.from_file`メソッドに対して先ほど作成したjsonファイルを指定し、ステートマシンを構築します。
今回はLambdaの呼び出しのみですが、ステートマシンの構成に応じてRoleの権限は変更が必要です。

参考：[AWS CDK StateMachine Reference](https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_stepfunctions/StateMachine.html)

```step_functions_sample_stack.py
from aws_cdk import aws_iam as iam
from aws_cdk.aws_stepfunctions import DefinitionBody, StateMachine
from constructs import Construct


class StepFunctionsSampleStack:
    def __init__(
        self,
        scope: Construct,
    ) -> None:
        self.scope = scope
        self._create_state_machine()

    def _create_state_machine(
        self,
    ) -> StateMachine:
        role = self._create_state_machine_role()

        return StateMachine(
            self.scope,
            "sample-state-machine",
            definition_body=DefinitionBody.from_file(
                "sample-state-machine.asl.json",
            ),
            state_machine_name="sample-state-machine",
            role=role,
        )

    def _create_state_machine_role(
        self,
    ) -> iam.Role:
        return iam.Role(
            self.scope,
            "sample-step-functions-role",
            assumed_by=iam.ServicePrincipal("states.amazonaws.com"),
            description="step functions role",
            inline_policies={
                "sample-function-execute-policy": iam.PolicyDocument(
                    statements=[
                        iam.PolicyStatement(
                            actions=["lambda:InvokeFunction"],
                            resources=[
                                "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXX:function:sample-function1*",
                                "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXX:function:sample-function2*",
                            ],
                            effect=iam.Effect.ALLOW,
                        ),
                    ],
                ),
            },
            role_name="sample-step-functions-role",
        )
```

## まとめ

ステートマシンの定義ファイルを自分で書くのではなく、Workflow Studioを使って構築しCDKに取り込む方法を紹介しました。
定義ファイルを直接編集するのとは異なり、Workflow StudioはGUIで直感的にフローを構築することができます。
AWS CDKを用いてStep Functionsを実装している方の参考になれば幸いです。