SAM, Step Functions ハンズオン

# 背景

会社の上司から Step Functions の学習をお勧めされたので、学習してみる。

# 参考記事

下記記事を見ながら取り組んでみる。

https://dev.classmethod.jp/articles/aws-sam-step-functions/

AWS SAM とは:

> AWS Serverless Application Model（以下、AWS SAM）は、サーバーレスアプリケーションのデプロイに特化した、CloudFormation（以下、CFn）の拡張機能です。

# 準備

AWS SAM CLI をインストールする。

https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html

(ついでに、これまで作っていなかった、管理者権限を持つユーザーを作成した。)
(また、シークレットアクセスキーも新調する。)

しばらく Homebrew を使っていなかったので、関連プログラムの更新が走り、少し時間がかかった。


# やってみる

今回はLambda Functionを呼び出す、シンプルなステートマシンを構築する。

下記のようなディレクトリ・ファイルを構成する。

```
.
├── functions
│   └── hello_world
│       ├── app.py
│       └── requirements.txt
├── statemachine
│   └── sfn.asl.json
└── template.yaml
```

ステートマシンから呼び出しされる、 Lambda Function のコードは下記。

```python:functions/hello_world/app.py
def lambda_handler(event, context):
  print("Hello world")
```

ステートマシンのワークフローは、ASL (Amazon States Language) で定義する。
Lambda Function の指定を変数定義にすることで、 AWS SAM テンプレート内で動的に指定できるようにする。

```json:statemachine/sfn.asl.json
{
  "StartAt": "hello world",
  "States": {
    "hello world": {
      "Type": "Task",
      "Resource": "${LambdaFunction}",
      "End": true
    }
  }
}
```

AWS SAM テンプレートは下記。

```template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
  StateMachineName:
    Description: Please type the Step Functions StateMachine Name.
    Type: String
    Default: 'sfn-sam-app-statemachine'
  LambdaFunctionName:
    Description: Please type the Lambda Function Name.
    Type: String
    Default: 'sfn-sam-app-function'
Resources:
  LambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${LambdaFunctionName}
      CodeUri: functions/hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8
      Timeout: 60
      Role: !GetAtt LambdaFunctionRole.Arn
  LambdaFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AWSStepFunctionsReadOnlyAccess
  StateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      Name: !Sub ${StateMachineName}
      DefinitionUri: statemachine/sfn.asl.json
      DefinitionSubstitutions:
        LambdaFunction: !GetAtt LambdaFunction.Arn
      Role: !GetAtt StateMachineRole.Arn
      Logging:
        Level: ALL
        IncludeExecutionData: True
        Destinations:
          - CloudWatchLogsLogGroup:
              LogGroupArn: !GetAtt StateMachineLogGroup.Arn
  StateMachineLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName : !Join [ "", [ '/aws/states/', !Sub '${StateMachineName}', '-Logs' ] ]
  StateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - states.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
        - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
```

`sam deploy` でデプロイする。
オプション `--guided` を指定することで、 CFn スタック名など、必要なパラメータを対話形式で指定することが可能。

```bash:bash
$ sam deploy --guided

Configuring SAM deploy
======================

        Looking for config file [samconfig.toml] :  Not found

        Setting default arguments for 'sam deploy'
        =========================================
        Stack Name [sam-app]:
        AWS Region [ap-northeast-1]:
        Parameter StateMachineName [sfn-sam-app-statemachine]:
        Parameter LambdaFunctionName [sfn-sam-app-function]:
        Parameter Resources []:
        #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
        Confirm changes before deploy [y/N]: y
        #SAM needs permission to be able to create roles to connect to the resources in your template
        Allow SAM CLI IAM role creation [Y/n]:
        #Preserves the state of previously provisioned resources when an operation fails
        Disable rollback [y/N]:
        Save arguments to configuration file [Y/n]:
        SAM configuration file [samconfig.toml]:
        SAM configuration environment [default]:
```

プロンプトに従い必要な情報を入力していくと、変更されるリソースなどが表示される。

```bash:bash
Waiting for changeset to be created..

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------
Operation                        LogicalResourceId                ResourceType                     Replacement
---------------------------------------------------------------------------------------------------------------------------------
+ Add                            LambdaFunctionRole               AWS::IAM::Role                   N/A
+ Add                            LambdaFunction                   AWS::Lambda::Function            N/A
+ Add                            StateMachineLogGroup             AWS::Logs::LogGroup              N/A
+ Add                            StateMachineRole                 AWS::IAM::Role                   N/A
+ Add                            StateMachine                     AWS::StepFunctions::StateMachi   N/A
                                                                  ne
---------------------------------------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:814937260541:changeSet/samcli-deploy1641627008/63193769-38f1-432f-a09c-358fbeab285f


Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
```

作成されたリソースなどが表示され、しばらくするとデプロイが完了する。

```bash:bash
2022-01-08 16:30:28 - Waiting for stack create/update to complete

CloudFormation events from stack operations
---------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                   ResourceType                     LogicalResourceId                ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS               AWS::IAM::Role                   StateMachineRole                 Resource creation Initiated
CREATE_IN_PROGRESS               AWS::Logs::LogGroup              StateMachineLogGroup             -
CREATE_IN_PROGRESS               AWS::IAM::Role                   StateMachineRole                 -
CREATE_IN_PROGRESS               AWS::IAM::Role                   LambdaFunctionRole               -
CREATE_IN_PROGRESS               AWS::IAM::Role                   LambdaFunctionRole               Resource creation Initiated
CREATE_IN_PROGRESS               AWS::Logs::LogGroup              StateMachineLogGroup             Resource creation Initiated
CREATE_COMPLETE                  AWS::Logs::LogGroup              StateMachineLogGroup             -
CREATE_COMPLETE                  AWS::IAM::Role                   StateMachineRole                 -
CREATE_COMPLETE                  AWS::IAM::Role                   LambdaFunctionRole               -
CREATE_IN_PROGRESS               AWS::Lambda::Function            LambdaFunction                   -
CREATE_IN_PROGRESS               AWS::Lambda::Function            LambdaFunction                   Resource creation Initiated
CREATE_COMPLETE                  AWS::Lambda::Function            LambdaFunction                   -
CREATE_IN_PROGRESS               AWS::StepFunctions::StateMachi   StateMachine                     -
                                 ne
CREATE_IN_PROGRESS               AWS::StepFunctions::StateMachi   StateMachine                     Resource creation Initiated
                                 ne
CREATE_COMPLETE                  AWS::StepFunctions::StateMachi   StateMachine                     -
                                 ne
CREATE_COMPLETE                  AWS::CloudFormation::Stack       sam-app                          -
---------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-app in ap-northeast-1
```

デプロイが完了しているので、マネジメントコンソールからもステートマシンの作成を確認することができる。

![スクリーンショット 2022-01-08 16.45.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599665/d5fc4611-df29-4dac-a6f3-547f3cd8d7e9.png)

今回の構成でワークフローを変更する際は、 `sfn.asl.json` を更新する。

```json:statemachine/sfn.asl.json
{
  "StartAt": "hello world",
  "States": {
    "hello world": {
      "Type": "Task",
      "Resource": "${LambdaFunction}",
      "Next": "succeed",
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "Next": "fail"
        }
      ]
    },
    "succeed": {
      "Type": "Succeed"
    },
    "fail": {
      "Type": "Fail",
      "Error": "ErrorCode 100",
      "Cause": "There is Error."
    }
  }
}
```

再度、デプロイを行えば環境に反映される。

```bash:bash
$ sam deploy

...

CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------
Operation                        LogicalResourceId                ResourceType                     Replacement
---------------------------------------------------------------------------------------------------------------------------------
* Modify                         StateMachine                     AWS::StepFunctions::StateMachi   False
                                                                  ne
---------------------------------------------------------------------------------------------------------------------------------

...

CloudFormation events from stack operations
---------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                   ResourceType                     LogicalResourceId                ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------
UPDATE_IN_PROGRESS               AWS::StepFunctions::StateMachi   StateMachine                     -
                                 ne
UPDATE_COMPLETE                  AWS::StepFunctions::StateMachi   StateMachine                     -
                                 ne
UPDATE_COMPLETE_CLEANUP_IN_PRO   AWS::CloudFormation::Stack       sam-app                          -
GRESS
UPDATE_COMPLETE                  AWS::CloudFormation::Stack       sam-app                          -
---------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-app in ap-northeast-1
```

変更が反映されたことが確認できる。

![スクリーンショット 2022-01-08 16.52.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599665/7eeaf22a-1f03-322a-d9a7-36661dbfb4e3.png)

# やってみて

SAM を使うと、コード上で書けば簡単に Step Functions を定義・デプロイできて、かなり便利だなと感じた。
まだ文法や書き方がよく分かっていないので、SAM と Step Functions をもう少し勉強しようと思う。