AWS SAMがStep Functionsをサポートしたのでやってみる

## はじめに
AWS::Serverless::StateMachine リソースを使用して、SAM テンプレート内で AWS Step Functions の
ステートマシンを定義できるようになりました。

:rocket: **AWS SAM adds support for AWS Step Functions**
https://aws.amazon.com/jp/about-aws/whats-new/2020/05/aws-sam-adds-support-for-aws-step-functions/



AWS SAM CLI も v0.52.0 で Step Functions Resource をサポートしています。

:rocket: **Support for a new Step Functions Resource in SAM**
https://github.com/awslabs/aws-sam-cli/releases/v0.52.0
　
## やってみる
前述のとおり、AWS SAM CLI は v0.52.0 以上である必要があります。

```shell-session
$ sam --version
SAM CLI, version 0.52.0
```

ステートマシンの定義ファイルとSAM テンプレートを以下のように作成していきます。

```shell-session
sam-app
├── statemachine
│   └── putitem.asl.json
└── template.yaml
```

### ステートマシンの定義
ここでは簡単な例を示すために AWS Step Functions のサービス統合を使用して
Input で受け取った内容をDynamoDB に保存するだけのステートマシンを作成します。
以下のようなシンプルな定義です。Lambda 関数すらありません。
Resource と TableName をプレースホルダ変数としているところに着目してください。

```statemachie/putitem.asl.json
{
    "StartAt": "PutDynamoDB",
    "States": {
      "PutDynamoDB": {
        "Type": "Task",
        "Resource": "${DDBPutItem}",
        "Parameters": {
          "TableName": "${DDBTable}",
          "Item": {
            "id": {"S.$": "$.id"},
            "message": {"S.$": "$.message"}
          }
        },
        "End": true 
      }
    }
}
```

余談ですが、AWS Toolkit for Visual Studio を使用するとローカルでステートマシンの
グラフを描写できるので便利です。(今回は描写するまでもないんですが。。。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/3cba54ba-db28-2f85-215b-86f9e66be20a.png)
**参考: AWS Toolkit for Visual Studio Code が AWS Step Functions のサポートを開始**
https://aws.amazon.com/jp/about-aws/whats-new/2020/03/aws-toolkit-for-visual-studio-code-supports-aws-step-functions/

### SAM テンプレート
AWS::Serverless::StateMachine リソースを使用しています。
ステートマシンは Definition プロパティでテンプレート内に直接インラインで定義するか
以下のように DefinitionUri プロパティで定義ファイルを指定することができます。
更に DefinitionSubstitutions プロパティを使用することで、デプロイ時に取得した値を
先ほどのステートマシン定義に注入することができます。
また受け取ったデータを保存するための DynamoDB テーブルも定義しています。

```template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app
  Sample SAM Template for Step Functions

Resources:
  PutDynamoDBStateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: statemachine/putitem.asl.json
      DefinitionSubstitutions:
        DDBPutItem: !Sub arn:${AWS::Partition}:states:::dynamodb:putItem
        DDBTable: !Ref Table
      Policies:
        - DynamoDBWritePolicy:
            TableName: !Ref Table

  Table:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
```

その他のプロパティについては以下のドキュメントを参照ください。

**AWS::Serverless::StateMachine**
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-statemachine.html

### デプロイ & 結果確認
準備は以上で、あとはいつもどおりに sam deploy するだけです！

```shell-session
$ cd sap-app
$ sam deploy --guided
```

変更セットを確認して実行します。
Dynamo DB テーブルおよび ステートマシン、ステートマシン用のロールが作成されます。

```shell-session
CloudFormation stack changeset
-----------------------------------------------------------------------------------------
Operation           LogicalResourceId                   ResourceType
-----------------------------------------------------------------------------------------
+ Add               PutDynamoDBStateMachineRole         AWS::IAM::Role
+ Add               PutDynamoDBStateMachine             AWS::StepFunctions::StateMachine
+ Add               Table                               AWS::DynamoDB::Table
-----------------------------------------------------------------------------------------
```

デプロイされたステートマシンを確認すると、SAM によって作成された DynamoDB テーブル名が
定義に反映されていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/fddd7ef7-b7d1-fece-81d2-dd52061ef1e9.png)
以下のような入力でステートマシンを実行します。

```json
{
    "id": "1",
    "message": "Hello World"
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/564c6c09-ed3c-6ecc-7e1f-3185b4faa372.png)
成功しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/fe60dfa8-a0eb-34be-7730-918f260f09ac.png)
テーブルにデータも保存されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/26df6da0-46af-3e9c-6d97-eb0b509873f8.png)

## API Gateway を追加する
Amazon API Gateway から 先ほどのステートマシンを実行できるように変更してみます。
### SAM テンプレートの編集
AWS::Serverless::StateMachine のプロパティに以下を追加するだけです。SAM の本領発揮ですね。

```yaml
      Events:
        HttpRequest:
          Type: Api
          Properties:
            Method: POST
            Path: /request
```

以下のようになります。

```template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app
  Sample SAM Template for Step Functions

Resources:
  PutDynamoDBStateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      DefinitionUri: statemachine/putitem.asl.json
      DefinitionSubstitutions:
        DDBPutItem: !Sub arn:${AWS::Partition}:states:::dynamodb:putItem
        DDBTable: !Ref Table
      Events:
        HttpRequest:
          Type: Api
          Properties:
            Method: POST
            Path: /request
      Policies:
        - DynamoDBWritePolicy:
            TableName: !Ref Table

  Table:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: id
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
```

再度デプロイします。

```shell-session
$ sam deploy
```

変更セットを確認して実行します。API Gateway が追加されます。

```
CloudFormation stack changeset
-----------------------------------------------------------------------------------------
Operation      LogicalResourceId                          ResourceType
-----------------------------------------------------------------------------------------
+ Add          PutDynamoDBStateMachineHttpRequestRole     AWS::IAM::Role
+ Add          ServerlessRestApiDeploymenta4c0d5c32b      AWS::ApiGateway::Deployment
+ Add          ServerlessRestApiProdStage                 AWS::ApiGateway::Stage
+ Add          ServerlessRestApi                          AWS::ApiGateway::RestApi
-----------------------------------------------------------------------------------------
```

デプロイされた API の設定を確認すると、AWS Service Proxy で直接ステートマシンを実行するよう
構成されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/d44fb75b-e2be-8a1f-92f4-46504910c7d6.png)

API に対して以下のような Post リクエストを送ります。

```shell-session
$ curl -X POST -d '{"input": "{\"id\": \"2\",\"message\": \"Work From Home\"}","name": "FromAPI","stateMachineArn": "arn:aws:states:ap-northeast-1:123456789012:stateMachine:PutDynamoDBStateMachine-xxxxxxxxxxxx"}' https://yyyyyyyyyy.execute-api.ap-northeast-1.amazonaws.com/Prod/request
{"executionArn":"arn:aws:states:ap-northeast-1:123456789012:execution:PutDynamoDBStateMachine-xxxxxxxxxxxx:FromAPI","startDate":1.590690379952E9}
```

API 経由の実行が成功しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/3b693b6b-73d5-7815-a4d6-7961124cfa34.png)
データも保存されていることが確認できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/5ed576d7-6efa-1024-a1d9-e2b09b0d28fd.png)
以上です。
参考になれば幸いです。