AWSサーバレスアーキテクチャで文章解析サービスを構築する　その１

# はじめに

本記事はAWSで提供されているサーバレスサービスを使用し、文章の感情分析および翻訳を行うサービスを構築するハンズオンです。
全３記事の構成となっています。

- AWSサーバレスアーキテクチャで文章解析サービスを構築する　その１
- [AWSサーバレスアーキテクチャで文章解析サービスを構築する　その２](https://qiita.com/kenryo/private/c9f0fe7b2f6bdf765412)
- [AWSサーバレスアーキテクチャで文章解析サービスを構築する　その３](https://qiita.com/kenryo/private/7fb84b4f736b3d9e29d1)

全てのハンズオンを完了すると以下のサービスについて基本的な使い方を理解できるようになります。

- AWS Step Functions
- Amazon S3
- Amazon EventBridge
- Amazon API Gateway
- Amazon DynamoDB
- Amazon Translate
- Amazon Comprehend

構築する最終的なアーキテクチャは以下の通りです。

![system.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b653c7a2-0f22-0fe7-bd77-c0c0ef95c846.png)

記念すべき第１回では、本サービスの中核となる`AWS Step Functions`（以降、 `Step Functions`` と呼ぶ）に関する基本概念を座学と簡単なハンズオンを通して理解していただきます。

:::note warn
注意①
本資料内スクショのAWS管理コンソールのデザインは資料作成当時のものです。
デザインは今後変更される可能性があり、スクショと差異がある場合がありますのでご了承ください。
:::

:::note warn
注意②
ハンズオンで作成したリソースについてはハンズオン終了後、各自削除をお願いします。
:::

## ゴール

- Step Functionsの特徴およびユースケースを理解していただく
- Step Functionsの基本概念を理解していただく
- Step Functionsのインプットとアプトプットを理解していただく
- Workflow Studioで簡単なステートマシンを作成し、Step Functionsに慣れる

## Step Functionsとは？

Step Functionsは[AWS公式](https://aws.amazon.com/jp/step-functions/)で以下のように説明されています。

>AWS Step Functions は、デベロッパーが AWS のサービスを利用して分散型アプリケーションを構築し、プロセスを自動化し、マイクロサービスのオーケストレーション、データと機械学習のパイプラインを構築できるようにするビジュアルワークフローサービスです。

具体的にいうとStep Functionsは以下のようなサービスと言えます。

- アプリ、サービスの流れの制御（処理順序、並列実行、条件分岐、エラーハンドリング）を簡単に実装できるサービス
- GUIツール（Workflow Studio）でアプリ、サービスの流れを可視化、編集できる
- AWS Lambdaなどのサービスを使って分散型アプリ、マイクロサービスを構築できる

なお、本サービスは`2016年12月`に一般公開されました。

## Step Functionsができた背景

私見ですが、Step Functionsができた背景は2つあると考えています。

- マイクロサービス的なシステム設計の台頭
- 分散アプリケーション、マイクロサービスをより簡単に構築したいというニーズの増加

### マイクロサービス的なシステム設計の台頭

まず、マイクロサービスとは何なのかをみていきます。

[AWSの記事](https://aws.amazon.com/jp/microservices/)によるとマイクロサービスの定義は以下です。

>マイクロサービスは、小さな独立した複数のサービスでソフトウェアを構成する、ソフトウェア開発に対するアーキテクチャ的、組織的アプローチです。各サービスは、正確に定義されたAPIを通じてやり取りします。

[Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9)ではマイクロサービスは以下のように定義されています。

>マイクロサービス（英語：microservices）とは、ソフトウェア開発の技法の1つであり、1つのアプリケーションを、ビジネス機能に沿った複数の小さいサービスの疎に結合された集合体として構成するサービス指向アーキテクチャ（service-oriented architecture; SOA）の1種である。
>2014年、ThoughtWorks社のマーチン・ファウラーとジェームス・ルイスが提唱したソフトウェアアーキテクチャである。

まとめるとマイクロサービスの定義は以下です。

- `2014年`に誕生したソフトウェアアーキテクチャないしソフトウェア開発技法
- 1つのアプリケーションを機能単位の小さなサービスとして分割し、各サービスはAPIで疎通する

[日本の大手調査会社SDKI Inc.の2021年の調査](https://prtimes.jp/main/html/rd/p/000001516.000072515.html)によると、マイクロサービスアーキテクチャ市場は2022年から2031年の間で大幅に増加するという予想結果がでました。  
このことから、アプリケーションにマイクロアーキテクチャや分散アーキテクチャを採用するケースが増えていき、その役割の一つを担うStep Functionsも重用されていくサービスになると考えられます。

### 分散アプリケーション、マイクロサービスをより簡潔に構築したいというニーズの増加

Step Functionsが発表される前まで、同等の機能を持つサービスとしてAmazon Simple Workflow Service（通称、SWF。[2012年リリース](https://aws.amazon.com/jp/about-aws/whats-new/2012/02/21/aws-announces-swf/)）がありました。
このサービスは先述したような複数のAWS Lambda（[2014年リリース](https://www.publickey1.jp/blog/14/aws_lambdaaws_reinvent_2014.html)）や他サービスを組み合わせ、一つのアプリケーションを構築するものです。
しかし、AWSは現在特殊な事情がない限りSWFではなく、Step Functionsの利用を推奨しています。理由は、[SWFはJava or Rubyでの実装が必要](https://dev.classmethod.jp/articles/cm-advent-calendar-2015-getting-started-again-swf/
)であり、開発が複雑になる、学習コストがかかるからです。[（よくある質問「Q: Amazon SWF と AWS Step Functions はどのように使い分ければよいですか?」）](https://aws.amazon.com/jp/swf/faqs/)  
分散アプリケーション、マイクロサービスの採用数が増え、より低コストで簡単に構築したいというニーズが増えたため、Step Functions（[2016年にリリース](https://atmarkit.itmedia.co.jp/ait/articles/1612/13/news019.html)）が誕生したのではないかと考えます。

## Step Functionsのユースケース

Step Functionsはどのような使われ方をするのか見ていきます。
以下3ケースを例として説明します。

- マイクロサービスオーケストレーション
- AWS内の運用オペレーションの自動化
- 機械学習モデルのトレーニング、データ変換処理

### マイクロサービスオーケストレーション

[マイクロサービス的なシステム設計の台頭](#マイクロサービス的なシステム設計の台頭)でお話ししたように、マイクロサービスを実現する際にStep Functionsを使用することがあります。

https://logmi.jp/business/articles/323346#s12

### AWS内の運用オペレーションの自動化

AWS内部でのセキュリティ対応などの運用オペレーションでStep Functionsが使用されることがあります。  
リンク記事では、AWSのセキュリティサービスで不正な動きをしたEC2の通信を遮断する設定を自動追加するフローをStep Functionsで実現しています。

https://aws.amazon.com/jp/blogs/security/automatically-block-suspicious-traffic-with-aws-network-firewall-and-amazon-guardduty/

### 機械学習モデルのトレーニング、データ変換処理

ワークフローが明確になっている場合、フローを可視化をすると開発効率化につながります。
機械学習はデータの加工、学習、推論など作業の流れが明確であるため、Step Functionsが採用される場合があります。

https://iot.kyoto/iotan_blog/2022/05/25/12220/

## Step Functionsの基本概念

Step Functionsを使う上で事前に知っておくべき用語、機能、構成要素について説明します。

- Workflow Studio
- ワークフロー
- ステートマシン
- ステート
- ASL（Amazon States Language）

### Workflow Studio

下記は`Workflow Studio`のスクショです。赤枠部分がWorkflow Studioに当たります。

![yougo-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/e72b5bc5-de51-4f1a-f7c1-2bcc2720b28f.png)

- ドラッグ&ドロップで`ステートマシン(後述)`（つまり、`ASL(後述)`）を作成することができるGUIツール

### ワークフロー

- 業務処理の流れ（ビジネスプロセス）
- `ステートマシン(後述)`で表現される

### ステートマシン

下記スクショの赤枠部分がステートマシンに当たります。

![yougo-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/3db7a359-6664-ab9f-7073-dc7d35a346b7.png)

- `ワークフロー`をStep Functionsの機能を利用して表現したもの
- 複数の`ステート(後述)`を束ねた単位
- `ASL(後述)`を使用して定義される

### ステート

下記スクショの赤枠部分がステートに当たります。

![yougo-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bee6a412-8670-7007-159a-877f1fa46e27.png)

- AWSサービスの実行、条件分岐、並列処理などの1処理を実行する単位
- `ステートマシン`は複数の`ステート`から構成される

### ASL（Amazon States Language）

下記スクショの赤枠部分がASLに当たります。

![yougo-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/8d8d2bee-3356-c4e5-bd48-2242925eea70.png)

- `ステートマシン`内の`ステート`の状態遷移を表現するために使用されるJSON形式の独自言語
- Step Functionsの実体と言える

## ASLについて

先述したように、ステートマシンはJson形式の独自言語であるASL（Amazon States Language）で定義されます。
ASLの記載項目について以下2つの定義を説明します。

- ステートマシンの定義
- ステートの定義

### ステートマシンの定義

|フィールド名|必須/非必須|説明|
|---|---|---|
|Comment|オプション|ステートマシンの説明|
|StartAt|必須|どのステートから実行するか。ステート名を指定|
|TimeoutSeconds|オプション|ステートマシンのタイムアウト時間を秒数で指定|
|Version|オプション|ASLのバージョン。デフォルトは1.0|
|States|必須|実行するステート。詳細は[ステートの定義](#ステートの定義)参照|

### ステートの定義

|フィールド名|必須/非必須|説明|
|---|---|---|
|Type|必須|ステートの種類。詳細は[Type](#type)参照|
|Next|オプション|現在のステートが終了した後に実行するステート名|
|End|オプション|この値がtrueの場合、このステートが終了したらステートマシンの終了を表す|
|Comment|オプション|ステートの説明|
|InputPath|オプション|ステートの入力の一部をフィルタリングして新しい入力とするパス|
|Parameters|オプション|ステート内のLambda関数などの入力とするJsonを指定する|
|OutputPath|オプション|ステートの出力の一部をフィルタリングして新しい出力とするパス|
|ResultPath|オプション|ステート内のLambda関数などの出力をステートの入力に何という名前で埋め込むか指定する|

#### Type

ステートは8種類あり、フィールド`Type`の値によって、必要なフィールド、構造が異なる。

|Type|説明|
|---|---|
|Task|ステートマシンで実行する作業（AWSサービスなど）を定義する|
|Choice|ステートマシンに条件分岐を設定する|
|Fail|ステートマシンの実行を失敗で停止させる|
|Succeed|ステートマシンの実行を成功で停止させる|
|Pass|何の処理もせず、入力を出力に渡す|
|Wait|指定された時間待機し、指定されたステートに遷移する|
|Parallel|このステート内で定義したステートを並列実行する|
|Map|入力配列の要素毎にステートを順次実行または並列実行する|

## ハンズオン

では、実際に手を動かし、Step Functionsがどのようなものなのか体験してみましょう。  
最終的に作成するサービスのアーキテクチャ図を再掲します。

![system.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b653c7a2-0f22-0fe7-bd77-c0c0ef95c846.png)

最初は小さな動くシステムを作り、そこから徐々に機能を追加して、下のシステムを実際に構築していきます。  
このシステムは簡単にいうと文章解析サービスであり、内容を箇条書きすると以下です。

- 日本語が記載されたテキストファイルを入力するとその文章を英語翻訳する
- 日本語が記載されたテキストファイルを入力するとその文章を感情分析する
- 上記分析結果をデータベースに保管する
- REST APIで入力となるテキストファイルをアップロードする

`Step Functions`が中核となるサービスであることから、上記サービスの構築に取り掛かる前に`Step Functions`の基礎的な部分、重要な部分をハンズオンを通して体験します。

### 前準備

ハンズオンを実施するための準備します。

#### S3バケットの作成

画面上部にある検索窓に`s3`と入力し、検索結果に`S3`が表示されるので、クリックしてください。

![s3-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/c2aa58cc-4868-b053-fb68-5059af5ef9b1.png)

オレンジ色の`バケットを作成`ボタンを押下してください。

![s3-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/8a5b15ac-db1b-32ed-2fc8-efb1ec29b240.png)

S3バケットに関する設定入力画面に遷移します。  
`バケット名`に`{ユーザ名}-{実施年月日}`を入力してください。
なお、バケット名は全世界で一意にする必要があります。もし、作成時に`バケット名はすでに存在します`などのエラーが発生した場合はバケット名の末尾に乱数など付与し、再実施してください。
その他設定はデフォルト値で構いません。  
最後に画面最下部にある`バケットを作成`ボタンを押下してください。

![s3-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5b79be28-c1c2-2a8d-5842-10e666b0c5e5.png)

画面上部に成功メッセージが表示されればバケットの作成完了です。

![s3-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/7626c454-0f71-3353-f5ef-397b27debee7.png)

#### ステートマシンの作成

続いてステートマシンを作成しましょう。

画面上部にある検索窓に`step`と入力し、検索結果に`Step Functions`が表示されるので、クリックしてください。

![sf-basic-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/aaad4c62-80c6-d8e3-89b3-ac3e6188192c.png)

オレンジ色の`ステートマシンの作成`ボタンをクリックしてください。

![sf-basic-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/1bdfcc15-e269-9c36-12e5-0de3db3897c0.png)

そのまま`次へ`ボタンをクリックしてください。

![sf-basic-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5603b5d5-657c-48d8-8c9c-f04723702a63.png)

Workflow Studioが表示されます。  
左メニューで`フロー`タブを選択し、`Pass`を中央画面のStartとEndの間にドラッグ&ドロップしてください。  
続いて、右上の`次へ`ボタンをクリックしてください。

![sf-basic-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/07332d43-a771-9276-cbe9-31ed14c98107.png)

確認画面が表示されます。  
Workflow Studioで生成したASLとステートマシンが表示されます。  
確認したら右下の`次へ`ボタンをクリックしてください。

![sf-basic-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/6ee711c4-fd1d-e344-6214-d0d3e8f063e3.png)

ステートマシンに関する設定を入力します。  
`ステートマシン名`は適当な名前を入力し、それ以外はデフォルト値にしてください。  
最後に`ステートマシンの作成`ボタンを押下してください。

![sf-basic-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/d0d19e6f-efae-c0aa-aebf-459dc9c0b8a7.png)

画面上部に成功メッセージが表示されればステートマシンの作成完了です。

![sf-basic-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b8206221-4c20-5fc2-e965-4e18bf18a9fe.png)

#### ステートマシンの実行

作成したステートマシンを実行してみます。  
上記画面右上にある`実行を開始`ボタンを押下するとダイアログが表示されます。  
ダイアログではステートマシンのインプットとなるJson文字列を`入力`に指定します。  
今回は下記Jsonを入力します。  
`実行を開始`ボタンをクリックし、ステートマシンを実行します。

```json
{
  "first": 1,
  "second": 2
}
```

![sf-basic-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/9ae45818-3dec-4f8b-0baf-bd4dce02359a.png)

実行結果画面が表示されます。  
画面上部の`実行ステータス`でステートマシンが最終的に成功か失敗したか表示されます。  
また、画面中央の`グラフビュー`でステート毎の処理の成否を視覚的に確認することができます。

![sf-basic-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b9822842-9d9c-8a4e-e72c-4bedc5d2d971.png)

ステートの詳細情報を確認することもできます。  
画面中央の`グラフビュー`の`Pass`ステートをクリックし、`ステップの詳細`の`入力`タブを選択すると、当該ステートのインプットとなったJson文字列が表示されます。  
今回はステートマシン実行時に指定したJson文字列が入力となっています。

![sf-basic-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/1dc37713-1037-d803-7f46-6e2bba70f188.png)

続いて`出力`タブを選択してみます。  
`出力`タブには当該ステートがアウトプットしたJson文字列が出力されます。  
Passステートは`何の処理もせず、入力を出力に渡す`機能です。また、今回`OutputPath`など指定していないため、入力のJson文字列がそのまま出力されます。

![sf-basic-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/252e0ba6-66e3-b523-4416-93f5e16f9791.png)

### ステート間の入出力制御（InputPath/Parameters/OutputPath/ResultPath）を体験する

続いて、ステート間のパラメータ、つまりJson文字列の受け渡しについて確認します。
ここは仕組みが多少理解しづらく、重点的に説明すべきと感じたため、章を設けました。

[ステートの定義](#ステートの定義)で`InputPath`、`Parameters`、`OutputPath`、`ResultPath`について説明しました。  
これら設定はWorkflow Studioでステートを選択し、`入力`、`出力`タブなど選択すると指定することができます。  
これら設定は例えば以下の場合に使用します。

- 当該ステート内では、入力されたJsonの一部を使用したい
- 当該ステートの出力は、入力Jsonそのものではなく、独自の形式で返したい

つまり、これら設定はステートの入出力の加工、フィルタリングを指定するものです。

![sf-inout-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/07686254-77f5-1afb-0522-972960c0eacd.png)

`データフローシミュレーター`を使用すると手軽に`InputPath`、`Parameters`、`OutputPath`、`ResultPath`などの設定を試すことができます。  

左メニューの`データフローシミュレーター`をクリックしてください。  
次に`ステート入力`にステートの入力となるテスト用のJsonを入力します。  
今回は以下のJsonを入力します。  
入力したら`次へ`をクリックしてください。

```json
{
  "first": "1",
  "second": "2",
  "third": {
    "first": "3-1",
    "second": "3-2"
  }
}
```

![sf-inout-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/711131a2-2f96-bc3b-5c80-a54447ef7486.png)

#### InputPath

まずは`InputPath`の設定です。  
フィルタ条件を指定することで入力Jsonの一部のみフィルタリングしてステートに渡すことができます。  
今回は以下を入力します。  
`$`はJson全体を表しており、`.`の後にフィルタリングする各要素のJsonキー名を指定します。  
下記の場合、入力JsonのJsonキー`third`に定義したオブジェクトを取得するよう指定しています。

```json
$.third
```

フィルタリング後の結果は以下になります。

```json
{
  "first": "3-1",
  "second": "3-2"  
}
```

`データフローシミュレーター`を使用すると加工、フィルタリング前のJsonとその後のJsonが一目で確認できるため、とても便利です。

![sf-inout-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5dc8e605-56c2-15c1-3b8b-e130c6665560.png)

#### Parameters

続いて、`Parameters`です。  
こちらはステートの入力を`InputPath`の結果と異なる形式で渡したい場合に指定します。  
`InputPath`の結果がこの設定で上書きされるイメージです。  
`.$`や`$.`は元となるJson(`InputPath`の結果)の値を参照したい場合、キーと値に指定する必要があります。  
以下だと`$.first`、`$.second`が指定されています。  
`InputPath`のフィルタリング後のJsonの値、つまり、`"3-1"`、`"3-2"`がそれぞれ埋め込まれます。

```json
{
  "staticValue": "Just a string",
  "third": {
    "first.$": "$.first",
    "second.$": "$.second"
  }
}
```

結果は以下になります。`Parameters`に設定したJsonに`InputPath`の結果が埋め込まれ出力されていることがわかるかと思います。

```json
{
  "staticValue": "Just a string",
  "third": {
    "first": "3-1",
    "second": "3-2"
  }
}
```

![sf-inout-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/162b4939-f7f3-7c18-e3ff-6f72b10f1a3f.png)

#### Task ResultとResultSelector

続いて、`Task Result`、`ResultSelector`ですが、こちらはLambda等の結果とそのフィルタリング設定です。  
今回は触らないので説明は割愛させていただきます。  
値は両方とも空にしてください。その場合、`Parameters`の結果がそのまま出力されます。

![sf-inout-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/c0da9ea1-c55f-05a3-5936-a4931af2432b.png)

![sf-inout-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/af6d6210-4b72-b116-103d-ba04e5a3f722.png)

#### ResultPath

次に`ResultPath`です。  
`ResultSelector`のフィルタリング後の結果を、最初の入力Jsonのどこに挿入するか指定できます。  
下記は入力Json(`$`)直下に`result`というキーを追加するよう指定しています。
そして、`result`配下に`ResultSelector`のフィルタリング後のオブジェクトを追加するよう指定しています。

```json
$.result
```

結果は以下になります。

```json
{
  "first": "1",
  "second": "2",
  "third": {
    "first": "3-1",
    "second": "3-2"
  },
  "result": {
    "staticValue": "Just a string",
    "third": {
      "first": "3-1",
      "second": "3-2"
    }
  }
}
```

![sf-inout-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b516409b-e9cb-c733-8a84-a77552a7f7d7.png)

#### OutputPath

最後は`OutputPath`です。  
`ResultPath`の結果のフィルタリングを設定します。
下記の場合、Jsonキー`result`に定義したオブジェクトを取得するよう指定しています。

```json
$.result
```

フィルタリング後の結果は以下になります。

```json
{
  "staticValue": "Just a string",
  "third": {
    "first": "3-1",
    "second": "3-2"
  }
}
```

![sf-inout-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b0aae9d1-7fb7-d1e1-05f5-8e2bc5c180fa.png)

ステート間の入出力制御について`データフローシミュレーター`を使用し、体験してみました。  
実際にステートの設定を変更することで入出力制御の確認もできますが、フィルタリングの途中経過がわからないので`データフローシミュレーター`はとても便利だと感じます。 
是非活用してみてください。  
なお、入出力制御のより詳しい説明は以下の記事をわかりやすいので参考にしてください。

https://dev.classmethod.jp/articles/stepfunctions-parameters-inter-states/

### 様々なステートタイプを試してみる

:::note warn
注意
本章で作成するステートマシンは冗長な部分があります。
例えば、`GetObject`ステートでエラーが発生した際、`Pass`ステート、`Choice`ステート経て、`Fail`ステートに遷移しますが、本来はそのような実装は不要であり、直接`Fail`ステートに遷移させれば良いです。
あくまでハンズオン用としてご了承ください。
:::

[Type](#type)でステートが8種類あると紹介しました。  
今回は`Task`、`Pass`、`Choice`、`Fail`、`Success`についてハンズオンを通して体験してみます。

まず、[ステートマシンの作成](#ステートマシンの作成)で自分が作成したステートマシンのリンクをクリックしてください。

![sf-type-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/2f531a4a-1f03-cfa4-9b55-c88a6a26841f.png)

次に`編集`ボタンをクリックしてください。

![sf-type-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/5060e3e2-c980-d9e0-7c11-d611e55dc504.png)

右側にある`Workflow Studio`ボタンをクリックしてください。

![sf-type-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/6a7f7b60-caa8-db84-bc9f-840a0572a78d.png)

#### Task、Passタイプのステートの追加と遷移先指定

`Pass`があった部分を選択し、`削除`ボタンで削除してください。  
次に左上の検索窓に`s3 getobject`と入力し、一番上にある`GetObject`ステートを`Pass`ステートがあった場所にドラッグ&ドロップしてください。
ステートマシンが以下のようになっていることを確認してください。

![sf-type-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/b298ca0a-62f5-4217-b8a6-6070e7408fa1.png)

`GetObject`ステートは指定したS3バケットから指定したファイルを取得する`Task`タイプのステートです。  
今回はファイルの存在確認という目的で使用します。  
右側のメニューに`APIパラメータ`があります。  
`Bucket`には[S3バケットの作成](#s3バケットの作成)で作成したS3バケットの名前、`Key`には`finish.txt`と入力してください。

![sf-type-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/1be906bb-f22a-2453-bbeb-7e9bdcbdf26c.png)

次に、`Pass`ステートを`GetObject`ステートの後に追加します。  
このステートは`GetObject`ステートでエラーが発生した際の遷移先として設定します。  
右側の`状態名`は`Pass-Fail`にしてください。
ステートマシンが以下のようになっていることを確認してください。

![sf-type-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/aa812f66-1c04-9e2b-13a6-3adf692975ca.png)

続いて、`Pass-Fail`を選択し、左メニューの`出力`タブを選択します。  
`結果`に以下のJsonを入力してください。  
これにより、`Pass-Fail`ステートが最終的に出力するJsonを指定できます。
`Pass-Fail`ステートの後に実行されるステートの入力はここで指定したJsonになります。

```json
{
  "error": "hoge"
}
```

![sf-type-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/08a9501b-441c-3c4f-d381-0d64a56c7517.png)

次に、`Pass`ステートを`GetObject`ステートと`Pass-Fail`ステートの間に追加します。  
このステートは`GetObject`ステートが成功した際の遷移先として設定します。
`状態名`は`Pass-Success`としてください。  
また、`次の状態`を`最後に移動`に変更してください。
ステートマシンが以下のようになっていることを確認してください。

![sf-type-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/de9ea928-ba56-e769-3079-c910891835c4.png)

続いて、`Pass-Success`ステートを選択し、左メニューの`出力`タブを選択します。  
`結果`に以下のJsonを入力してください。  
これにより、`Pass-Success`ステートが最終的に出力するJsonを指定できます。
`Pass-Success`ステートの後に実行されるステートの入力はここで指定したJsonになります。

```json
{
  "error": ""
}
```

![sf-type-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/2fedb0a3-07ef-6fd9-1f1f-b8b6e3a599c0.png)

#### エラーハンドリング（キャッチャー）の追加

`GetObject`ステートでエラーが発生したら`Pass-Fail`ステートに遷移させるようにします。  
`GetObject`ステートを選択し、左メニュー`エラー処理`タブを選択し、`新しいキャッチャーの追加`をクリックしてください。

![sf-type-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/ad3b312a-2488-f15e-803a-7f1a2edd3f32.png)

`Errors`はキャッチするエラーの種類を定義します。`States.ALL`を入力してください。
この設定はステート内の全てのエラーをキャッチする設定です。
Javaで言う`try-catch`構文で`Exception`をキャッチしているイメージになります。  
`Fallback state`は`Errors`で指定したエラーが発生した場合の遷移先を指定します。`Pass-Fail`を選択してください。

![sf-type-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/edab8ab6-003b-ff5f-a844-37179d3e78fe.png)

#### Choice、Success、Failタイプのステートの追加と条件分岐による遷移

続いて、`Choice`ステートを追加します。  
左メニューから`Choice`を選択し、`Pass-Success`の後に追加してください。

![sf-type-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/f27172d8-4372-b202-9afe-ad8f52ca2c36.png)

`Pass-Fail`ステートを選択し、`設定`タブで`次の状態`を`Choice`に変更してください。

![sf-type-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/6eb47a26-c570-7a42-4966-922337b63356.png)

`Choice`ステートはif文のようなもので指定した条件に一致したら指定したステートに遷移させることができます。  
今回は`Pass-Success`、`Pass-Fail`が出力したJsonの内容を見て、`Success`ステートか`Fail`ステートに遷移させます。  
`Choice`ステートに伸びている矢印で`Default`が付いている方に`Success`ステートを追加してください。  
`Rule #1`が付いている方に`Fail`ステートを追加してください。

![sf-type-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/ab2c89f9-e2ee-ae88-a61e-17f4c6175ce0.png)

`Choice`ステートの条件を指定します。  
`Choice`ステートを選択し、右メニューに`Rule #1`があるのでクリックしてください。  
次に`Then next state is`で`Fail`を選択してください。
続いて`Add conditions`ボタンを押下してください。

![sf-type-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/639590d2-3b08-90ab-4846-78f0816be27d.png)

条件入力ダイアログが表示されます。  
`Variable`は`$.error`、`Operator`は`matches string`、`Value`は`*hoge*`を入力してください。
`Variable`の設定は[InputPath](#inputpath)などの書き方と同様です。
これは`Pass-Success`、`Pass-Fail`の出力Jsonのキー`error`の値に`hoge`という文字列が含まれていたら、条件に一致すると判定するRuleです。

```json
{
  "error": "hoge"
}
```

最後に`条件を保存する`ボタンをクリックしてください。

![sf-type-16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/e938f22b-7cf5-8730-99f8-d21b1853746d.png)

以上でステートマシンが一通り完成しました。  
右上の`適用して終了`ボタンをクリックしてください。

![sf-type-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/95f5f5c0-4faa-d7ba-e827-96699c824364.png)

#### IAMロールに権限（IAMポリシー）を追加

作成したステートマシンを実行してみます。  
結果は失敗になります。`GetObject`ステートをクリックし、`出力`タブでエラー内容を確認してみましょう。  
`Cause`に出力されるメッセージに`Status Code: 403`とあります。これは権限が不足していることに起因するエラーです。  
作成したステートマシンにS3バケットにアクセスする権限がないためエラーになっています。ステートマシンにS3バケットを操作する権限を与える必要があります。
ステートマシンには`IAMロール`というアイデンティティが付与されています。
`IAMロール`は`IAMポリシー`という実際の操作権限の集まりであり、`IAMロール`をステートマシンなどのAWSリソースに与えることでリソースは`IAMポリシー`に定義された操作権限を得ることができます。
今回ステートマシン作成時に`IAMロール`が自動生成されています。
その`IAMロール`にS3バケットを操作する`IAMポリシー`を付与することで今回のエラーは解決することができます。 

![sf-type-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/7ff35aff-8cdf-ca7c-b201-2b6c0437df64.png)

では、当該`IAMロール`に`IAMポリシー`を与えてみましょう。
ステートマシンの詳細画面に遷移し、`編集`ボタンを押下してください。  
画面中央に`アクセス許可`があります。`既存のロール`を表示されているIAMロールの名前をメモしてください。

![sf-type-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/7f83a8a3-ba1e-cdb4-e3b2-71b78731fc4f.png)

次にIAM画面に遷移します。
画面上部にある検索窓に`iam`と入力し、検索結果に`IAM`が表示されるので、クリックしてください。

![sf-type-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/88ec321b-0717-7ce3-9255-fc65715c44cb.png)

左メニュー`ロール`をクリックしてください。

![sf-type-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/9ea44ae7-70c5-04fb-d56b-0d9e6023be65.png)

検索窓に先ほどメモしたIAMロールの名前を入力し、表示されたロールのリンクをクリックしてください。

![sf-type-29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/0ca671a5-11c6-8ba8-6625-a0b5f36e82da.png)

`許可`タブを選択すると`許可ポリシー`が表示されます。  
その右上の`許可を追加`プルダウンを選択し、`ポリシーのアタッチ`をクリックしてください。

![sf-type-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bf5ae3f7-bbe7-5add-812f-ed69b1c2770c.png)

検索窓に`S3Full`と入力し、Enterしてください。  
ポリシー`AmazonS3FullAccess`が表示されるのでチェックし、右下の`許可を追加`をクリックしてください。

![sf-type-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/6473e041-70ce-570b-639f-89a4d06d32eb.png)

`許可ポリシー`の一覧に`AmazonS3FullAccess`が表示されたら追加完了です。

![sf-type-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/85107463-585b-8a25-a4a5-a1ddec67272a.png)

以上の手順を完了すると先ほどのエラーは発生しなくなります。
以降はS3バケットにファイルがない場合とある場合でステートマシンを試してみます。

#### 実行！（ファイルがない場合）

再度ステートマシンを実行してください。  
ステートマシンの実行に失敗したと思います。  
`GetObject`ステートを選択し、`出力`タブでエラーを確認します。  
`Cause`に出力されるメッセージに`The specifed key does not exist.`とあります。  
これは[Task、Passタイプのステートの追加と遷移先指定](#task、passタイプのステートの追加と遷移先指定)の`GetObject`ステート追加時に指定したオブジェクト（`finish.txt`）がS3バケットに存在しないことが原因です。  
処理の流れを箇条書きしてみます。

1. `GetObject`ステートが実行され、指定されたファイルを取得しようと試みる
2. `GetObject`ステートでファイルが取得できないため、エラーが発生する
3. `GetObject`ステートで指定したキャッチャーにより、`Pass-Fail`ステートに遷移する
4. `Pass-Fail`ステートの出力が`Choice`ステートに渡される
5. `Choice`ステートの条件分岐で入力Jsonの`error`キーの値に`hoge`が含まれているため、`Fail`ステートに遷移する

![sf-type-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/05bd8b62-3b6e-7051-146c-736f796effba.png)

#### 実行！（ファイルがある場合）

S3バケットに`finish.txt`を作成後、ステートマシンを実行します。  
S3バケット一覧画面に遷移し、作成したS3バケット名をクリックしてください。

![sf-type-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/f59a7af5-42fc-a07b-3762-a7260ebb44c4.png)

`アップロード`ボタンを押下してください。

![sf-type-22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/62de44f5-8e8c-9781-9d6e-07e3920c897e.png)

`finish.txt`ファイルをドラッグ&ドロップし、右下の`アップロード`ボタンをクリックしてください。

![sf-type-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/bbc64838-ef3d-9f71-11d9-b331f5610901.png)

成功メッセージが上部に表示されればアップロード完了です。

![sf-type-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/505c0eda-375b-55c8-c082-7073cfa99023.png)

再度ステートマシンを実行してください。  
ファイルが存在するので`GetObject`ステートはエラーにならず、`Success`ステートを追加し、ステートマシンの実行に成功しました。  
処理の流れを箇条書きしてみます。

1. `GetObject`ステートが実行され、指定されたファイルを取得しようと試みる
2. `GetObject`ステートでファイルが取得できたため、`Pass-Success`ステートに遷移する
3. `Pass-Success`ステートの出力が`Choice`ステートに渡される
4. `Choice`ステートの条件分岐で入力Jsonの`error`キーの値に`hoge`が含まれないため、`Success`ステートに遷移する

![sf-type-25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545707/04f710f9-2691-52de-8e02-f62a164b0c6b.png)

## 参考

- https://www.youtube.com/watch?v=PGyasNJ1QTQ&t=140s
- https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/welcome.html
- https://www.slideshare.net/AmazonWebServicesJapan/20190522-aws-black-belt-online-seminar-aws-step-functions
- https://aws.amazon.com/jp/step-functions/use-cases/
- https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/amazon-states-language-common-fields.html
- https://dev.classmethod.jp/articles/stepfunctions-parameters-inter-states/
- https://tech.nri-net.com/entry/aws_history_and_chronology_all