AWS CDKでStepFunctionsを構築しました。

この記事は [ハンズラボ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/handslab) 7日目の記事です。

お疲れさまです。@naokiurです。
今年も担当させて頂きます。

最近は業務でAWSリソースを構築する際に、
CloudFormationやServerlessで、
コード化することを心がけています。
（用法用量と守り、目的を忘れないようにしつつ…）

おかげでYAML形式ファイルを書くことはだんだん慣れてきました。

そんな中、
[AWS CDK](https://aws.amazon.com/jp/cdk/)というものの存在を知り、
しかも[AWS CDKで、Javaと .NET 用が一般利用が可能に！！](https://aws.amazon.com/jp/blogs/news/aws-cloud-development-kit-cdk-java-and-net-are-now-generally-available/)

個人的にJavaも好きな自分、
せっかくなので、AWS CDKをJavaで試しました。

# Overview
自分もおすすめされたのですが、
AWSが公開している[CDK Workshop](https://cdkworkshop.com/)が、
CDK何もしらなかった自分にとってすごく良かったです！

# 環境
* MacBook Pro (Retina 13-inch、Early 2015)
* macOS Mojave 10.14.6
* Java 1.8.0_191
* IntelliJ IDEA CE 2019.2.4

# やったこと
最近業務でStepFunctionsを作成したので、
StepFunctionsの構築を、AWS CDKで実施しました。

# 内容

## cdkコマンドインストール
`npm install -g aws-cdk`

## プロジェクトテンプレート作成
以下コマンドで、Java用のプロジェクトテンプレートを生成することができます。

`cdk init --a {プロジェクト名} --language java`

<img width="334" alt="スクリーンショット 2019-12-07 16.35.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/f8d0eac8-ab9e-c304-804c-3ed9dba20463.png">


Mavenプロジェクトです。
個人的にちょっとなつかしいです。
ここでは、 `プロジェクト名 = sample`としました。

### AppとStack
プロジェクトテンプレートを生成すると、
`SampleApp`と `SampleStack`が生成されました。

Stackは名前の通り、
これがCloudFormationの1Stackになるのだ、
と認識しております。

デフォルトでは、
SQSとSNSを作成するStackになっているようです。

<img width="657" alt="スクリーンショット 2019-12-07 16.52.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/0f77ff19-7ab7-1556-86b0-2c8fb5f9d213.png">


### pom.xml
`core`とは別に、
AWSサービスごとに依存関係を記載しています。
使用するAWSサービスが増えた場合、依存関係を追加する必要があるようです。

<img width="530" alt="スクリーンショット 2019-12-07 16.52.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/6e764c5e-4553-9c76-a3bf-774222f51a14.png">


## StepFunctionsの構築
デフォルトのSQSとSNSは削除し、
LambdaとStepFunctionsをStackに追加します。

### 依存関係の追加
LambdaとStepFunctionsのライブラリをpom.xmlに追加します。

```xml:pom.xml
<dependency>
    <groupId>software.amazon.awscdk</groupId>
    <artifactId>lambda</artifactId>
    <version>1.18.0</version>
</dependency>
<dependency>
    <groupId>software.amazon.awscdk</groupId>
    <artifactId>stepfunctions</artifactId>
    <version>1.18.0</version>
</dependency>
<dependency>
    <groupId>software.amazon.awscdk</groupId>
    <artifactId>stepfunctions-tasks</artifactId>
    <version>1.18.0</version>
</dependency>
```
StepFunctionsに関しては、
`stepfunctions`だけでなく、
`stepfunctions-tasks`を追加する必要があります。

### Stackにリソースの追加
LambdaとStepFunctionsのリソースを追加します。
`{Resource}.Builder.hoge().fuga().build()`という形で、
リソースを生成することができることが多いようにお見受けしました。

個人的な好みですが、なんとなく記述が揃っていて見やすい気がします。

```SampleStack.java
public class SampleStack extends Stack {
    public SampleStack(final Construct parent, final String id) {
        this(parent, id, null);
    }

    public SampleStack(final Construct parent, final String id, final StackProps props) {
        super(parent, id, props);

        final Function hello = Function.Builder.create(this, "HelloHandler")
                .runtime(Runtime.PYTHON_3_6)
                .code(Code.fromAsset("lambda"))
                .handler("hello.lambda_handler")
                .build();

        final Function world = Function.Builder.create(this, "WorldHandler")
                .runtime(Runtime.PYTHON_3_6)
                .code(Code.fromAsset("lambda"))
                .handler("world.lambda_handler")
                .build();

        final Task helloTask = Task.Builder.create(this, "HelloTask")
                .task(InvokeFunction.Builder.create(hello).build())
                .build();

        final Task worldTask = Task.Builder.create(this, "WorldTask")
                .task(InvokeFunction.Builder.create(world).build())
                .build();

        final StateMachine machine = StateMachine.Builder.create(this, "SampleStateMachine")
                .definition(helloTask.next(worldTask))
                .build();
    }
}
```

### Lambdaファイルの格納
Lambda上で実際に動かすメソッドは、
`srcディレクトリと同列のディレクトリ配下`に格納する必要があるようです。

`実際にLambda上で動かすメソッドは、Javaでなくても問題ない`ので、
ここではPythonファイルを準備しました。

<img width="327" alt="スクリーンショット 2019-12-07 17.42.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/b4a061ad-494b-6548-90fd-1e7c9b14dac0.png">

## デプロイ
実行してなかったので、まず以下コマンドを実行します。

`cdk bootstrap`

デプロイ用のS3バケットを作成するStackを作成してくれます。


デプロイのために、
以下コマンドを実行します。

`mvn package`
`cdk deploy`

以上によって、
CloudFormation上にStackを作成できました。

<img width="781" alt="スクリーンショット 2019-12-07 17.53.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/31de489d-14ca-b06f-6106-a985308229b0.png">


### リソース
無事にStepFunctionsが作成され、
実行することができました！

<img width="781" alt="スクリーンショット 2019-12-07 17.53.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/330e1209-4f7a-522f-432d-5bb06fff0bea.png">
<img width="494" alt="スクリーンショット 2019-12-07 17.53.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129313/4425422c-bb76-1038-0b5c-248cab29222d.png">


# 感想
* 当然のことながら、各AWSリソースを構成するために必要なものは決まっていて、それがどのJavaクラスに当てはまるのか、というのを変換しつつ書く必要があるな、と感じました。
    * とはいえ、わかりやすい命名になっている（※個人の感想です）ので、そこまで手間ではないかなあ
    * `InvokeFunction`がどこにあるかわからなくて少しだけ迷いました
    * [（TypeScriptですが）サンプル](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-stepfunctions-readme.html)をちゃんと読めば迷わなかったはず…
* deployしたときにエラーが起きて、修正して、デプロイして、またエラー…ということは、 `全くなかった`です
    * 簡単な構成だからかもですが…
    * 書き間違いだったら、 `コンパイルエラーにして`くれる感じ
* **補完ってすばらしい！！！**

[ハンズラボ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/handslab) 、
8日目は、 @watarukura さんです！


# 参考にさせて頂きました
* https://cdkworkshop.com/