[AWS] Step Functionsで遊んでみる（SAM + Lambda） Part.1（基礎編）

# Step Functionsとは
AWS Step Functionsとは、マイクロサービスコンポーネントをワークフロー化するためのサービスです。

## 状態
Step Functionsでは、状態によって様々な動作を制御できます。
状態には、以下のようなものがあります。

| 状態 | 制御 |
|---|---|
| Task | ステートマシンで何らかの作業をする |
| Choice | 実行の選択肢間で選択する |
| Fail or Succeed | 失敗または成功で実行を停止する |
| Pass | 入力を単純に出力に渡す、または一部の固定データを出力する |
| Wait | 一定時間または指定された時刻/日付まで遅延を提供する |
| Parallel | 実行の並列ブランチを開始する |
| Map | ステップを動的に反復する |

## Taskで利用可能なサービス
Step FunctionsのTaskには、以下のサービスを指定できます。

- Lambda
- Batch
- DynamoDB
- ECS
- Fargate
- SNS
- SQS
- Glue
- SageMaker
- EMR
- CodeBuild
- Step FUnctions

# 今回のターゲット
Step Functionsは、範囲が非常に広く、Step Functions + Lambdaだけでみてもかなり広範囲に及びます。
投稿内容も膨大になりかねないので、この投稿に関しては、以下の内容までをターゲットにします。
パラメータを渡してみたり、条件によりタスクを換えたり、別のサービスを実行したり、といった箇所は次回にとりあげたいと思います。

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

# 前提

- SAM
- Lambda
    - Python3.8

# SAM
SAMについては、以下を参照ください。

- [[AWS] Serverless Application Model (SAM) の基本まとめ](https://qiita.com/herohit-tool/items/420f4a7b294cfcf56ed7)
- [[AWS] Serverless Application Model (SAM) でAPI Gateway + Lambda + DynamoDBなサンプルを作成してみる](https://qiita.com/herohit-tool/items/5b0fe520f6f28fb5b4bc)

# Lambda関数を用意する
まずは、RuntimeをPython（3.8）で、Hello Worldのプロジェクトを作成します。

```bash
$ sam init --runtime=python3.8
Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1

Project name [sam-app]:

Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git

AWS quick start application templates:
	1 - Hello World Example
	2 - EventBridge Hello World
	3 - EventBridge App from scratch (100+ Event Schemas)
	4 - Step Functions Sample App (Stock Trader)
	5 - Elastic File System Sample App
Template selection: 1

-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: python3.8
Dependency Manager: pip
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./sam-app/README.md
```

今回は、API Gatewayはいらないので、`template.yml`を以下のように編集します。
`AWS::Serverless::Function`に`Events`の定義が不要です。
あと、`Outputs`そのものも不要です。

```yaml:template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app

  Sample SAM Template for sam-app

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8
```

保存したら、`sam build`し、`sam deploy --guided`を実行してデプロイしましょう。

# Step Functionsでステートマシンを構成する
## 準備
まず、ステートマシンを構成するうえで、必要な準備をしていきましょう。
それは、Lambda関数の設定として、Step Functionから呼び出せるようロールの定義を追加します。
Hello Worldで作成したテンプレートは、デフォルトのロールをLambda関数に割り当てているので、これを明示的に作成し、割り当てる必要があります。
では、`template.yml`をいじってみましょう。

```yaml:template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app

  Sample SAM Template for sam-app

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8
      Role: !GetAtt HelloWorldFunctionRole.Arn
  HelloWorldFunctionRole:
    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
```

## ステートマシン定義の追加
続いて、ステートマシンの定義をJson形式で作成します。
まず、ファイルの置き場所となるディレクトリを作成し、そこにファイルを新規に作成します。

```bash
$ mkdir step_functions
$ touch step_functions/state_machine.json
```

続いて、ステートマシンの定義を行います。
下記定義は、hello_worldのLambda関数を呼び出して、終了するだけのもので、条件やパラメータなどは一切ない、非常にシンプルな定義となっています。

```json:step_functions/state_machine.json
{
  "StartAt": "hello world",
  "States": {
    "hello world": {
      "Type": "Task",
      "Resource": "${HelloWorldFunction}",
      "End": true
    }
  }
}
```

## SAMのテンプレートにステートマシン定義を追加する
最後に、`template.yml`に、ステートマシンの定義を追加しましょう。

```yaml:template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app

  Sample SAM Template for sam-app

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.8
      Role: !GetAtt HelloWorldFunctionRole.Arn
  HelloWorldFunctionRole:
    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:
      DefinitionUri: step_functions/state_machine.json
      DefinitionSubstitutions:
        HelloWorldFunction: !GetAtt HelloWorldFunction.Arn
      Role: !GetAtt StateMachineRole.Arn
  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
```

そして、`sam build`、`sam deploy --guided`まで実行してみましょう。

# 確認してみる
まず、現在の状態を確認してみましょう。
マネジメントコンソールで、`Step Functions`を選択してみましょう。
ステートマシンがデプロイされていることが確認できます。

![sfn1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/d0eab26f-a5bb-ef26-8792-aa801628d14d.png)

ここにあるステートマシンの中身をみてみましょう。
「定義」の中身をみると、下記のような感じになっているかと思います。

![sfn2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/3b681f7e-efae-d3df-ddc3-fef8dd1c0a48.png)

`hello_world`が呼び出されることが確認できますね。
「定義」のJsonの中身も、`Resource`の部分が、Lambda関数のARNに置き換えられていることがわかります。

# 実行してみる
では、ステートマシンを実行してみましょう。
「実行」タブの「実行の開始」ボタンを押すと、ステートマシンが実行されます。

![sfn3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/42a86b80-119d-16fb-2518-40d3cd9e728a.png)

今回は、パラメータなどをLambda側で受け取るようにしていないため、入力もそのままで実行して大丈夫です。

![sfn5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/95705591-363e-38fb-0672-98d4709eaa2f.png)

すぐに実行が完了し、イベント履歴が確認できます。

![sfn6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427998/237e882e-bed6-d08b-1002-886dcf1d452e.png)

リンクになっているCloud Watchを確認すると、確かにLambda関数が呼び出されていることが確認できるかと思います。

# まとめ　
今回は、基本中の基本の動作ということで、まずはここまでをしっかりと抑えておくのが重要かと思います。
パラメータの設定や、Lambda以外をTaskから呼び出す方法、条件分岐については、次回とりあげてみたいと思います。

- [[AWS] Step Functionsで遊んでみる（SAM + Lambda） Part.2（パラメータ編）](https://qiita.com/herohit-tool/items/fbd0b91dacce218819ae)
- [[AWS] Step Functionsで遊んでみる（SAM + Lambda） Part.3（分岐編）](https://qiita.com/herohit-tool/items/440a57769f043110648b)
- [[AWS] Step Functionsで遊んでみる（SAM + Lambda） Part.4（結果をSQSに投げる）](https://qiita.com/herohit-tool/items/5977cc178deda6c321dd)

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