話題の記事

【awslabs探訪】AWS Cloud Development Kit (AWS CDK)を使ってみた

2018.08.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

中山(順)です

AWS Cloud Development Kit (AWS CDK)なるツールが存在するとtwitterで発見しました。

なんか、JavaScript, java, typescriptでCloudFormationのテンプレートを生成できるみたいです。 ちょっとよくわかんないのでさわってみたいと思います。

awslabs/aws-cdk

AWS Cloud Development Kit (AWS CDK) User Guide

やってみた

とりあえず、Getting Startedの通りにやってみたいと思います。

【2019年6月21日 追記】上記のドキュメントはリンク切れになっていました。チュートリアルをやってみたい方は以下のドキュメントをご覧ください。

Getting Started With the AWS CDK

検証環境

特に理由はありませんがAmazon Linux 2で動作確認しました。

動作要件

前提となるツールは以下の通りです。

  • Node.js (>= 8.11.x)
  • AWS CLI
  • git

Node.jsのインストール手順は以下の通りです。

curl --silent --location https://rpm.nodesource.com/setup_8.x | bash -

sudo yum -y install nodejs

インストール

まずはAWS-CDKのコマンドラインツールをインストールします。

sudo npm i -g aws-cdk

正常にインストールできたことの確認を兼ねてバージョンを確認します。

cdk --version
0.8.0 (build bb95676)

プロジェクトの初期化

プロジェクトを作成します。現時点ではJavaScript、Java、TypeScriptが選べるようです。 今回はTypeScriptを使ってみます。

mkdir hello-cdk
cd hello-cdk
cdk init app --language=typescript

生成されたファイルを確認します。

ls -l
total 104
drwxrwxr-x   2 ec2-user ec2-user    26 Aug  7 12:17 bin
-rw-rw-r--   1 ec2-user ec2-user    39 Aug  7 12:17 cdk.json
drwxrwxr-x 191 ec2-user ec2-user  8192 Aug  7 12:18 node_modules
-rw-rw-r--   1 ec2-user ec2-user   530 Aug  7 12:17 package.json
-rw-rw-r--   1 ec2-user ec2-user 74509 Aug  7 12:18 package-lock.json
-rw-r--r--   1 ec2-user ec2-user   320 Aug  7 12:17 README.md
-rw-r--r--   1 ec2-user ec2-user   631 Aug  7 12:17 tsconfig.json

tsconfig.jsonおよびpackage.jsonの内容を確認します。

cat tsconfig.json
{
    "compilerOptions": {
        "target":"ES2018",
        "module": "commonjs",
        "lib": ["es2016", "es2017.object", "es2017.string"],
        "declaration": true,
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "noImplicitThis": true,
        "alwaysStrict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": false,
        "inlineSourceMap": true,
        "inlineSources": true,
        "experimentalDecorators": true,
        "strictPropertyInitialization":false
    }
}
cat package.json
{
    "name": "hello-cdk",
    "version": "0.1.0",
    "main": "bin/index.js",
    "types": "bin/index.d.ts",
    "bin": {
        "hello-cdk": "bin/hello-cdk.js"
    },
    "scripts": {
        "build": "tsc",
        "watch": "tsc -w",
        "cdk": "cdk"
    },
    "devDependencies": {
        "@types/node": "^8.9.4",
        "typescript": "^2.8.3",
        "aws-cdk": "^0.8.0"
    },
    "dependencies": {
        "@aws-cdk/aws-sns": "^0.8.0",
        "@aws-cdk/aws-sqs": "^0.8.0",
        "@aws-cdk/cdk": "^0.8.0"
    }
}

今回はこれらのファイルに修正を加える必要はありません。

コアライブラリのインストール

AWS CDKを利用するうえで必須となるコアライブラリをインストールします。

sudo npm install @aws-cdk/cdk @types/node

アプリケーションの作成

まずは、リソースを何も作らない空っぽのアプリケーションを作成します。

FILE_NAME="index.ts"

cat << EOF > ${FILE_NAME}
import cdk = require('@aws-cdk/cdk');

class MyApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);
    }
}

process.stdout.write(new MyApp(process.argv).run());
EOF

cat ${FILE_NAME}
import cdk = require('@aws-cdk/cdk');

class MyApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);
    }
}

process.stdout.write(new MyApp(process.argv).run());

ファイルができたらコンパイルします。

npm run build

認証情報の設定

AWS CLIの認証情報を設定します。 アクセスキーを環境変数に設定したり、"~/.aws"配下のファイルに認証情報を設定しましょう。

最後に設定できていることを確認してください。 結果はインスタンスプロファイルを利用している場合の例です。

aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************JQ46         iam-role
secret_key     ****************9jQi         iam-role
    region                <not set>             None    None

アプリケーションの指定

コンパイルした結果生成される.jsファイルをプロジェクトのディレクトリ直下にある"cdk.json"へ設定します。

{
  "app": "node index.js"
}

スタックの一覧表示

現時点で存在するスタックの一覧を表示します。 もちろん一つもありません。

cdk ls -l
[]

スタックの定義

先ほど作成したコード内にスタックを作成するクラスを定義してます。 また、エントリーポイント(?)となるクラスにおいて定義したクラスのインスタンスを作成します。

import cdk = require('@aws-cdk/cdk');

class MyStack extends cdk.Stack {
    constructor(parent: cdk.App, id: string, props?: cdk.StackProps) {
        super(parent, id, props);
    }
}

class MyApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);
        
        new MyStack(this, 'hello-cdk');
    }
}

process.stdout.write(new MyApp(process.argv).run());

追記したらコンパイルします。

npm run build

スタックの一覧を確認します。 AWS CDK的にはこの時点でスタックが作成されるようですが、AWSアカウントにCFnスタックが実際に作成されるわけではありません。

cdk ls -l
-
    name: hello-cdk
    environment:
        name: xxxxxxxxxxxx/ap-northeast-1
        account: 'xxxxxxxxxxxx'
        region: ap-northeast-1

S3バケットを作ってみる

次は実際に何かリソースを作ってみましょう。 今回はS3バケットを作ります。

まずは必要なライブラリをインストールします。

sudo npm install @aws-cdk/aws-s3

次に、ライブラリをインポートしてリソースを作成する処理を追加します。 バケットに対してバージョニングの有効化を行っています。

import cdk = require('@aws-cdk/cdk');
import s3 = require('@aws-cdk/aws-s3');

class MyStack extends cdk.Stack {
    constructor(parent: cdk.App, id: string, props?: cdk.StackProps) {
        super(parent, id, props);
        
        new s3.Bucket(this, 'myfirstbucketxxxxxxxxxxxx', {
            versioned: true
        });
    }
}

class MyApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);
        
        new MyStack(this, 'hello-cdk');
    }
}

process.stdout.write(new MyApp(process.argv).run());

追記したらコンパイルします。

npm run build

CloudFormationテンプレートの確認

アプリケーションをデプロイする際に利用されることになるテンプレートを確認します。

cdk synth hello-cdk
Resources:
    myfirstbucketxxxxxxxxxxxxF02C9402:
        Type: 'AWS::S3::Bucket'
        Properties:
            VersioningConfiguration:
                Status: Enabled
    CDKMetadata:
        Type: 'AWS::CDK::Metadata'
        Properties:
            Modules: '@aws-cdk/aws-kms=0.8.0,@aws-cdk/aws-s3=0.8.0,@aws-cdk/cdk=0.8.0,@aws-cdk/cx-api=0.8.0,hello-cdk=0.1.0,js-base64=2.4.5'

デプロイ(1回目)

デプロイします。

cdk deploy
 ?  Starting deployment of stack hello-cdk...
[0/2] Tue Aug 07 2018 12:41:49 GMT+0000 (UTC)  CREATE_IN_PROGRESS  [AWS::CloudFormation::WaitConditionHandle] WaitCondition
[0/2] Tue Aug 07 2018 12:41:49 GMT+0000 (UTC)  CREATE_IN_PROGRESS  [AWS::CloudFormation::WaitConditionHandle] WaitCondition Resource creation Initiated
[1/2] Tue Aug 07 2018 12:41:49 GMT+0000 (UTC)  CREATE_COMPLETE     [AWS::CloudFormation::WaitConditionHandle] WaitCondition
[2/2] Tue Aug 07 2018 12:41:51 GMT+0000 (UTC)  CREATE_COMPLETE     [AWS::CloudFormation::Stack] hello-cdk
[0/4] Tue Aug 07 2018 12:42:06 GMT+0000 (UTC)  CREATE_IN_PROGRESS  [AWS::CDK::Metadata] CDKMetadata
[0/4] Tue Aug 07 2018 12:42:06 GMT+0000 (UTC)  CREATE_IN_PROGRESS  [AWS::S3::Bucket] myfirstbucketxxxxxxxxxxxxF02C9402
[0/4] Tue Aug 07 2018 12:42:08 GMT+0000 (UTC)  CREATE_IN_PROGRESS  [AWS::S3::Bucket] myfirstbucketxxxxxxxxxxxxF02C9402 Resource creation Initiated
[0/4] Tue Aug 07 2018 12:42:08 GMT+0000 (UTC)  CREATE_IN_PROGRESS  [AWS::CDK::Metadata] CDKMetadata Resource creation Initiated
[1/4] Tue Aug 07 2018 12:42:09 GMT+0000 (UTC)  CREATE_COMPLETE     [AWS::CDK::Metadata] CDKMetadata
[2/4] Tue Aug 07 2018 12:42:30 GMT+0000 (UTC)  CREATE_COMPLETE     [AWS::S3::Bucket] myfirstbucketxxxxxxxxxxxxF02C9402
[2/4] Tue Aug 07 2018 12:42:32 GMT+0000 (UTC)  UPDATE_COMPLETE_CLEANUP_IN_PROGRESS  [AWS::CloudFormation::Stack] hello-cdk
[2/4] Tue Aug 07 2018 12:42:34 GMT+0000 (UTC)  DELETE_IN_PROGRESS  [AWS::CloudFormation::WaitConditionHandle] WaitCondition
[3/4] Tue Aug 07 2018 12:42:34 GMT+0000 (UTC)  DELETE_COMPLETE     [AWS::CloudFormation::WaitConditionHandle] WaitCondition
[4/4] Tue Aug 07 2018 12:42:35 GMT+0000 (UTC)  UPDATE_COMPLETE     [AWS::CloudFormation::Stack] hello-cdk
 ?  Deployment of stack hello-cdk completed successfully, it has ARN arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/hello-cdk/3ec20540-9a3f-11e8-aede-503a369c8836

作成されたリソースを確認

作成したリソースを確認します。

まずはCFnスタックを確認します。

aws cloudformation describe-stacks \
    --stack-name hello-cdk
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/hello-cdk/3ec20540-9a3f-11e8-aede-503a369c8836",
            "LastUpdatedTime": "2018-08-07T12:42:01.908Z",
            "Tags": [],
            "EnableTerminationProtection": false,
            "CreationTime": "2018-08-07T12:41:46.142Z",
            "Capabilities": [
                "CAPABILITY_IAM",
                "CAPABILITY_NAMED_IAM"
            ],
            "StackName": "hello-cdk",
            "NotificationARNs": [],
            "StackStatus": "UPDATE_COMPLETE",
            "DisableRollback": false,
            "ChangeSetId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:changeSet/CDK-e81ddbf2-3173-4340-a15b-40a584000216/80337bb2-7eac-450b-9397-b16221a78716",
            "RollbackConfiguration": {}
        }
    ]
}

S3バケットを確認します。

まず、リソースが存在することの確認を兼ねて、CFnスタックからリソースIDを確認します。

aws cloudformation describe-stack-resources \
    --stack-name hello-cdk \
    --query StackResources[?ResourceType==\`AWS::S3::Bucket\`]
[
    {
        "StackId": "arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/hello-cdk/3ec20540-9a3f-11e8-aede-503a369c8836",
        "ResourceStatus": "CREATE_COMPLETE",
        "ResourceType": "AWS::S3::Bucket",
        "Timestamp": "2018-08-07T12:42:30.556Z",
        "StackName": "hello-cdk",
        "PhysicalResourceId": "hello-cdk-myfirstbucketxxxxxxxxxxxxf02c9402-y5xophnhjrw9",
        "LogicalResourceId": "myfirstbucketxxxxxxxxxxxxF02C9402"
    }
]

また、S3バケットのバージョニングが有効であることを確認します。

aws s3api get-bucket-versioning \
    --bucket "hello-cdk-myfirstbucketxxxxxxxxxxxxf02c9402-y5xophnhjrw9"
{
    "Status": "Enabled"
}

このように、アプリケーション通りにリソースが作成されることが確認できました。

アプリケーションの修正

修正する流れも確認しましょう。 サーバーサイド暗号化を有効化してみます。

import cdk = require('@aws-cdk/cdk');
import s3 = require('@aws-cdk/aws-s3');

class MyStack extends cdk.Stack {
    constructor(parent: cdk.App, id: string, props?: cdk.StackProps) {
        super(parent, id, props);
        
        new s3.Bucket(this, 'myfirstbucketxxxxxxxxxxxx', {
            versioned: true,
            encryption: s3.BucketEncryption.KmsManaged
        });
    }
}

class MyApp extends cdk.App {
    constructor(argv: string[]) {
        super(argv);
        
        new MyStack(this, 'hello-cdk');
    }
}

process.stdout.write(new MyApp(process.argv).run());

追記したらコンパイルします。

npm run build

差分を確認します。 以下のコマンドで、修正に伴いリソースがどのように変更されるかを確認できます。 CloudFormationの変更セットのようなものでしょうか。

cdk diff
[~] ?  Updating myfirstbucketxxxxxxxxxxxxF02C9402 (type: AWS::S3::Bucket)
 mq [+] .BucketEncryption:
     mq New value: {"ServerSideEncryptionConfiguration":[{"ServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms"}}]}

最後にデプロイします。

cdk deploy
 ?  Starting deployment of stack hello-cdk...
[0/2] Tue Aug 07 2018 14:27:13 GMT+0000 (UTC)  UPDATE_IN_PROGRESS  [AWS::S3::Bucket] myfirstbucketxxxxxxxxxxxxF02C9402
[1/2] Tue Aug 07 2018 14:27:35 GMT+0000 (UTC)  UPDATE_COMPLETE     [AWS::S3::Bucket] myfirstbucketxxxxxxxxxxxxF02C9402
[1/2] Tue Aug 07 2018 14:27:37 GMT+0000 (UTC)  UPDATE_COMPLETE_CLEANUP_IN_PROGRESS  [AWS::CloudFormation::Stack] hello-cdk
[2/2] Tue Aug 07 2018 14:27:38 GMT+0000 (UTC)  UPDATE_COMPLETE     [AWS::CloudFormation::Stack] hello-cdk
 ?  Deployment of stack hello-cdk completed successfully, it has ARN arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/hello-cdk/3ec20540-9a3f-11e8-aede-503a369c8836

削除

最後はdestroyコマンドで削除します。

cdk destroy
Are you sure you want to delete: hello-cdk (y/n)? y
 ?  Starting destruction of stack hello-cdk...
 ?  Stack hello-cdk successfully destroyed.

まとめ

CloudFormationのテンプレートでは宣言的な記述しかできませんが、このようにコードで記述できれば反復処理や分岐処理も書けますので、柔軟な運用ができそうです。 現時点でサポートしているコードは私自身どれも書き慣れていないのですが、いい機会なのでTypeScriptでも勉強してみるのもいいかなーと思いました。

プレビュー版ではありますが、サポートしているリソースは多そうですので気になる方はいろいろ試してみてはいかがでしょうか。

aws-cdk/packages/@aws-cdk/

現場からは以上です。