AWS CDKをKotlinで試した

**AWS CDKの0.19に対応し修正しました**


[AWS CDK(AWS Cloud Development Kit)](https://github.com/awslabs/aws-cdk)をKotlinで試しました。
公式ドキュメントの[Getting Started](https://awslabs.github.io/aws-cdk/getting-started.html)のJavaを参考に実装していきます。

awscdkのバージョン: 0.19.0
kotlinのバージョン: 1.3.11
gradleのバージョン: 5.0
OS: Mac

試したソースは [tiibun/awscdk-kotlin](https://github.com/tiibun/awscdk-kotlin) に置いてあります。

## cdkインストール

cdkコマンドラインツール（以下、cdk cli）をインストールします。
（kotlinで実装するのでコマンドラインツールは必要ないのではないかと思いましたが、deployするにはcdk cliが必要なようです。）

```sh
npm install -g aws-cdk
cdk --version
```

## build.gradle.kts

Javaの1.8を指定していたのでkotlinのオプションに1.8を指定します。
また、dependenciesにawscdk/cdkを追加します。
下記は一部のみ載せます。

```kotlin:build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

tasks.withType<KotlinCompile>().all {
    kotlinOptions.jvmTarget = "1.8"
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
    implementation("software.amazon.awscdk:cdk:0.19.0") // 追加
}
```
変わったパッケージ名ですね。

## Appのサブクラスを作成

`software.amazon.awscdk.App`を継承したクラスを作成します。

```kotlin:src/main/kotlin/samples/HelloAwsCdk.kt
package samples

import software.amazon.awscdk.App

fun main(args: Array<String>) {
    val app = App()
    app.run()
}
```

## cdk.jsonとapp.shを作成

[JavaScriptはcdk.jsonだけで良さそう](https://awslabs.github.io/aws-cdk/getting-started.html#configure-the-cdk-toolkit)ですが、Kotlin(Java)は一手間必要です。
ここは一部手順を公式ドキュメントと変えています。

### build.gradle.ktsにapplicationを追加

```kotlin:build.gradle.kts
plugins {
    application
}

application {
    mainClassName = "samples.HelloAwsCdkKt"
}
```

`main()`はktファイルのトップレベルに書いたのでsamples.HelloAwsCdkKtとKtが付いてます。

app.shで`gradlew run`を指定します。第１引数にappを指定しています。
TERMの設定はgradleのインジケーターを出力しないようにするためです。

```sh:app.sh
#!/bin/bash

TERM=dumb
exec ./gradlew run --args "app $@"
```

`cdk.json`を作成します。cdk.jsonはcdk cliのオプションを設定したファイルになります。

```json:cdk.json
{
    "app": "/bin/bash ./app.sh"
}
```

## cdk実行

AWS credentialsを設定して（割愛）、cdk cliを実行します。

```sh
cdk ls -l
```
Appの中身は空なので`[]`と出力されます。

## S3 Bucketを追加

いよいよAppの中身を追加します。公式ドキュメントの通り、S3バケットを追加します。
まずはStack（CloudFormationのスタック）を作成し、コンストラクタでバケットのインスタンスを生成します。
BucketのプロパティはBuilderパターンを使用しています。

```kotlin:src/main/kotlin/samples/MyStack.kt
package samples

import software.amazon.awscdk.App
import software.amazon.awscdk.Stack
import software.amazon.awscdk.services.s3.Bucket
import software.amazon.awscdk.services.s3.BucketProps

class MyStack(parent: App, name: String) : Stack(parent, name) {
    init {
        Bucket(this, "MyFirstBucket", BucketProps.builder()
                .withVersioned(true)
                .build())
    }
}
```

main関数では上記Stackを生成します。

```kotlin:src/main/kotlin/samples/HelloAwsCdk.kt
fun main(args: Array<String>) {
    val app = App()
    MyStack(app, "hello-cdk")
    app.run()
}
```

## cdk再度実行

```sh
cdk ls -l
```

とすると今度はスタック名とenvironment情報(1234はアカウント)が出ます。

```
-
    name: hello-cdk
    environment:
        name: 1234/ap-northeast-1
        account: '1234'
        region: ap-northeast-1
```

***注***
`[]`と表示される場合はAWSの認証がうまくいっていない可能性があります。
`cdk ls -l -v`とすると出力が詳細に表示されます。
（私は最初regionの設定がなくて`[]`になりました。）
また、`--profile`オプションでcredentialsのprofileを指定できます。
私はAssume Roleで多要素認証を指定していたためうまくいきませんでしたが、
[assume-role](https://github.com/remind101/assume-role)を使ってできました。

### CloudFormationテンプレート

次にCloudFormationテンプレートを表示します。

```sh
cdk synth hello-cdk
```
S3バケットの設定（バージョンあり）とCDKMetadataが表示されます。

```yaml
Resources:
    MyFirstBucketB8884501:
        Type: 'AWS::S3::Bucket'
        Properties:
            VersioningConfiguration:
                Status: Enabled
    CDKMetadata:
        Type: 'AWS::CDK::Metadata'
        Properties:
            Modules: '@aws-cdk/aws-iam=0.8.2,@aws-cdk/aws-kms=0.8.2,@aws-cdk/aws-s3=0.8.2,@aws-cdk/aws-s3-notifications=0.8.2,@aws-cdk/cdk=0.8.2,@aws-cdk/cx-api=0.8.2,js-base64=2.4.5'
```

## deploy

deployします。

```sh
cdk deploy
```

AWS上でCloudFormationスタックが作成されます。

削除する場合はdestroyします。

```sh
cdk destroy
```

## まとめ

CloudFormationテンプレートを操作するにはPython製の[troposphere](https://github.com/cloudtools/troposphere)やGo製の[GoFormation](https://github.com/awslabs/goformation)がありましたが、JavaScriptやJavaでも記述できるようになりました。しかもデプロイまでできます。
再利用がうまくできるといいですね。

## おまけ

awscdkのコードを見てもTypeScriptだけでJavaのコードは出てきません。
どうやってるのかと調べたら[awslabs/jsii](https://github.com/awslabs/jsii)というツールが使われているようです。
TypeScriptのクラス定義からJavaのコードを作っています。
なんか凄そうです。