継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

プログラマブルにAWS リソース構成に役立つ「 AWS CDK 」

タダです。

昨年、「AWS Cloud Development Kit」(以下、AWS CDK) が開発者プレビューですが、発表されました。

aws.amazon.com

github.com

AWS CDK」の特徴は、プラグラマブルに CloudFormation のプロビジョングができるので、アプリケーションの開発からインフラまで一貫した開発を行えるのがメリットになります。

普段は、 CloudFormaiton を使ってプロビジョニングを行なっているので、 CloudFormation との比較を考慮しつつ触っていきます!

TL;DR

  • CDK のワークショップを使って、 CDK を実践した
    • サンプルプロジェクトとサーバーレス構成を作った
  • CDK を使ってみて CloudFormation との比較をした

AWS CDK の概要

AWS CDK」 はコードでクラウドインフラストラクチャを定義し、CloudFormation でプロビジョニングするためのオープンソースのソフトウェア開発フレームワークです。

サポートされている言語は以下の通りです。今後は Python もサポート予定とのことです。

AWS CDK の実践

早速、CDK Workshopを参考に 「AWS CDK」を実践をしていきます。

準備作業

環境は、AWS Cloud(以下、Cloud9) を使います。「AWS CDK」には以下の要件が必要です。

  • AWS CLI
  • Node.js (バージョンが8.11以上)

Cloud9 には AWS CLI がインストールされているのでインストール不要です。

また、Cloud9 のデフォルトの Node.js ではバージョンが満たしてないため、バージョンをあげます。

ワークショップでは、バージョンを「v8.12.0」にする必要があるため左記バージョンまでアップデートします。

$ node -v
v6.16.0
$ nvm install v8.12.0
Downloading https://nodejs.org/dist/v8.12.0/node-v8.12.0-linux-x64.tar.xz...
####################################################################################################################################################################### 100.0%
Now using node v8.12.0 (npm v6.4.1)

$ node -v
v8.12.0

次に、「AWS CDK」も導入します。ワークショップでは、バージョンを意図的に「0.22」を指定しています。

$ npm install -g aws-cdk@0.22.0
/home/ec2-user/.nvm/versions/node/v8.12.0/bin/cdk -> /home/ec2-user/.nvm/versions/node/v8.12.0/lib/node_modules/aws-cdk/bin/cdk
+ aws-cdk@0.22.0
added 276 packages from 255 contributors in 18.188s

$ cdk --version
0.22.0 (build 644ebf5)

東京リージョンにリソースを作りたいので、「AWS_DEFAULT_REGION」の変数をセットします。

AWS CDK」のコマンドでcdk doctor コマンドを打つと現状の設定を確認できて便利なので、上記の変数をセットした後確認します。

$ export AWS_DEFAULT_REGION=ap-northeast-1
$ cdk doctor
ℹ️ CDK Version: 0.22.0 (build 644ebf5)
ℹ️ AWS environment variables:
  - AWS_DEFAULT_REGION = ap-northeast-1
  - AWS_CLOUDWATCH_HOME = /opt/aws/apitools/mon
  - AWS_PATH = /opt/aws
  - AWS_AUTO_SCALING_HOME = /opt/aws/apitools/as
  - AWS_ELB_HOME = /opt/aws/apitools/elb
ℹ️ No CDK environment variables

AWS CDK プロジェクトの作成

次に TypeScript のサンプルプロジェクトを作成していきます。

$ mkdir cdk-workshop && cd cdk-workshop
$ cdk init sample-app --language typescript
Applying project template sample-app for typescript
Initializing a new git repository...
Executing npm install...
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN cdk-workshop@0.1.0 No repository field.
npm WARN cdk-workshop@0.1.0 No license field.

# Useful commands

* `npm run build`   compile typescript to js
* `npm run watch`   watch for changes and compile
* `cdk deploy`      deploy this stack to your default AWS account/region
* `cdk diff`        compare deployed stack with current state
* `cdk synth`       emits the synthesized CloudFormation template

プロジェクト関連ファイルについて解説があったためまとめます。

  • lib/cdk-workshop-stackts.tsはCDKアプリケーションのメインスタックが定義される
  • bin/cdk-workshop.tsはCDKアプリケーションのエントリポイントになる
  • lib/cdk-workshop-stack.tsで定義されているスタックをロードされる
  • package.json はnpmモジュールのマニフェストファイルである
  • cdk.jsonはツールキットにアプリの実行方法を指示する
    • 今回はnode bin/cdk-workshop.jsになる
  • tsconfig.jsonはTypeScriptの設定が定義される
  • node_modulesディレクトリにはnpmによって管理され、プロジェクトのすべての依存関係を含まれる

f:id:sadayoshi_tada:20190216174502p:plain

サンプルプロジェクトでは、以下のリソースを作ります。

  • SQS キュー
  • SNS トピック
  • SQS キューを SNS トピックでサブスクライブ
import sns = require('@aws-cdk/aws-sns');
import sqs = require('@aws-cdk/aws-sqs');
import cdk = require('@aws-cdk/cdk');

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const queue = new sqs.Queue(this, 'CdkWorkshopQueue', {
      visibilityTimeoutSec: 300
    });

    const topic = new sns.Topic(this, 'CdkWorkshopTopic');

    topic.subscribeQueue(queue);
  }
}

アプリケーションコードから CloudFormation テンプレートの確認とデプロイ

cdk synthを実行すると CDK で作る、 CloudFormation のテンプレートを確認できます

$ cdk synth
Resources:
  CdkWorkshopQueue50D9D426:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 300
    Metadata:
      aws:cdk:path: CdkWorkshopStack/CdkWorkshopQueue/Resource
  CdkWorkshopQueuePolicyAF2494A5:
    Type: AWS::SQS::QueuePolicy
    Properties:
      PolicyDocument:
        Statement:
          - Action: sqs:SendMessage
            Condition:
              ArnEquals:
                aws:SourceArn:
                  Ref: CdkWorkshopTopicD368A42F
            Effect: Allow
            Principal:
              Service: sns.amazonaws.com
            Resource:
              Fn::GetAtt:
                - CdkWorkshopQueue50D9D426
                - Arn
        Version: "2012-10-17"
      Queues:
        - Ref: CdkWorkshopQueue50D9D426
    Metadata:
      aws:cdk:path: CdkWorkshopStack/CdkWorkshopQueue/Policy/Resource
  CdkWorkshopTopicD368A42F:
    Type: AWS::SNS::Topic
    Metadata:
      aws:cdk:path: CdkWorkshopStack/CdkWorkshopTopic/Resource
  CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7:
    Type: AWS::SNS::Subscription
    Properties:
      Endpoint:
        Fn::GetAtt:
          - CdkWorkshopQueue50D9D426
          - Arn
      Protocol: sqs
      TopicArn:
        Ref: CdkWorkshopTopicD368A42F
    Metadata:
      aws:cdk:path: CdkWorkshopStack/CdkWorkshopTopic/CdkWorkshopQueueSubscription/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Modules: aws-cdk=0.22.0,@aws-cdk/aws-cloudwatch=0.22.0,@aws-cdk/aws-iam=0.22.0,@aws-cdk/aws-kms=0.22.0,@aws-cdk/aws-s3-notifications=0.22.0,@aws-cdk/aws-sns=0.22.0,@aws-cdk/aws-sqs=0.22.0,@aws-cdk/cdk=0.22.0,@aws-cdk/cx-api=0.22.0,jsii-runtime=node.js/v8.12.0

続いて、CloudFormation のデプロイを行なっていきます。

AWS CDK」アプリを初めてデプロイするときに「bootstrap stack」をインストールする必要があります。

「bootstrap stack」では、CloudFormation テンプレートを格納するS3が用意されます。

$ cdk bootstrap
   Bootstrapping environment xxxxxxxxxxx/ap-northeast-1...
CDKToolkit: creating CloudFormation changeset...
 0/2 | 04:30:07 | CREATE_IN_PROGRESS   | AWS::S3::Bucket | StagingBucket 
 0/2 | 04:30:09 | CREATE_IN_PROGRESS   | AWS::S3::Bucket | StagingBucket Resource creation Initiated
 1/2 | 04:30:30 | CREATE_COMPLETE      | AWS::S3::Bucket | StagingBucket 
 2/2 | 04:30:32 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CDKToolkit 
   Environment xxxxxxxxxxx/ap-northeast-1 bootstrapped.

コマンドが成功すると、 CloudFormation スタックが作成されS3バケットが作成されます。 f:id:sadayoshi_tada:20190216174416p:plain

$ cdk deploy
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────────────────┬────────┬─────────────────┬───────────────────────────┬──────────────────────────────────────────┐
│   │ Resource                │ Effect │ Action          │ Principal                 │ Condition                                │
├───┼─────────────────────────┼────────┼─────────────────┼───────────────────────────┼──────────────────────────────────────────┤
│ + │ ${CdkWorkshopQueue.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": {                           │
│   │                         │        │                 │                           │   "aws:SourceArn": "${CdkWorkshopTopic}" │
│   │                         │        │                 │                           │ }                                        │
└───┴─────────────────────────┴────────┴─────────────────┴───────────────────────────┴──────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np)

Do you wish to deploy these changes (y/n)? y
CdkWorkshopStack: deploying...
CdkWorkshopStack: creating CloudFormation changeset...
 0/6 | 04:38:15 | CREATE_IN_PROGRESS   | AWS::SNS::Topic        | CdkWorkshopTopic (CdkWorkshopTopicD368A42F) 
 0/6 | 04:38:16 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata     | CDKMetadata 
 0/6 | 04:38:16 | CREATE_IN_PROGRESS   | AWS::SQS::Queue        | CdkWorkshopQueue (CdkWorkshopQueue50D9D426) 
 0/6 | 04:38:16 | CREATE_IN_PROGRESS   | AWS::SNS::Topic        | CdkWorkshopTopic (CdkWorkshopTopicD368A42F) Resource creation Initiated
 0/6 | 04:38:16 | CREATE_IN_PROGRESS   | AWS::SQS::Queue        | CdkWorkshopQueue (CdkWorkshopQueue50D9D426) Resource creation Initiated
 1/6 | 04:38:17 | CREATE_COMPLETE      | AWS::SQS::Queue        | CdkWorkshopQueue (CdkWorkshopQueue50D9D426) 
 1/6 | 04:38:18 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata     | CDKMetadata Resource creation Initiated
 2/6 | 04:38:18 | CREATE_COMPLETE      | AWS::CDK::Metadata     | CDKMetadata 
 3/6 | 04:38:27 | CREATE_COMPLETE      | AWS::SNS::Topic        | CdkWorkshopTopic (CdkWorkshopTopicD368A42F) 
 3/6 | 04:38:29 | CREATE_IN_PROGRESS   | AWS::SNS::Subscription | CdkWorkshopTopic/CdkWorkshopQueueSubscription (CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7) 
 3/6 | 04:38:30 | CREATE_IN_PROGRESS   | AWS::SQS::QueuePolicy  | CdkWorkshopQueue/Policy (CdkWorkshopQueuePolicyAF2494A5) 
 3/6 | 04:38:31 | CREATE_IN_PROGRESS   | AWS::SNS::Subscription | CdkWorkshopTopic/CdkWorkshopQueueSubscription (CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7) Resource creation Initiated
 3/6 | 04:38:31 | CREATE_IN_PROGRESS   | AWS::SQS::QueuePolicy  | CdkWorkshopQueue/Policy (CdkWorkshopQueuePolicyAF2494A5) Resource creation Initiated
 4/6 | 04:38:31 | CREATE_COMPLETE      | AWS::SNS::Subscription | CdkWorkshopTopic/CdkWorkshopQueueSubscription (CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7) 
 5/6 | 04:38:31 | CREATE_COMPLETE      | AWS::SQS::QueuePolicy  | CdkWorkshopQueue/Policy (CdkWorkshopQueuePolicyAF2494A5) 
 6/6 | 04:38:33 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CdkWorkshopStack 

   CdkWorkshopStack

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxx:stack/CdkWorkshopStack/a5bd1260-31a4-11e9-87b8-0ec110ab8a1e

CloudFormation のコンソールを確認するとスタックが出来上がっているのを確認できました。

f:id:sadayoshi_tada:20190216174343p:plain

次の作業のためにサンプルで作成したリソースを削除します。

lib/cdk-workshop-stack.tsを以下のように編集します。

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

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

  }
}

変更後のリソースの変化の確認の仕方は、cdk diffで確認可能です。

$ cdk diff
IAM Statement Changes
┌───┬─────────────────────────────────┬────────┬─────────────────┬───────────────────────────┬──────────────────────────────────────────────────┐
│   │ Resource                        │ Effect │ Action          │ Principal                 │ Condition                                        │
├───┼─────────────────────────────────┼────────┼─────────────────┼───────────────────────────┼──────────────────────────────────────────────────┤
│ - │ ${CdkWorkshopQueue50D9D426.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": {                                   │
│   │                                 │        │                 │                           │   "aws:SourceArn": "${CdkWorkshopTopicD368A42F}" │
│   │                                 │        │                 │                           │ }                                                │
└───┴─────────────────────────────────┴────────┴─────────────────┴───────────────────────────┴──────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np)

Resources
[-] AWS::SQS::Queue CdkWorkshopQueue50D9D426 destroy
[-] AWS::SQS::QueuePolicy CdkWorkshopQueuePolicyAF2494A5 destroy
[-] AWS::SNS::Topic CdkWorkshopTopicD368A42F destroy
[-] AWS::SNS::Subscription CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7 destroy

変更内容が問題なければ、再度cdk deployを実行します。

$ cdk deploy
CdkWorkshopStack: deploying...
CdkWorkshopStack: creating CloudFormation changeset...
 0/6 | 06:00:36 | UPDATE_IN_PROGRESS   | AWS::CDK::Metadata | CDKMetadata 
 1/6 | 06:00:38 | UPDATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata 
 1/6 | 06:00:40 | UPDATE_COMPLETE_CLEA | AWS::CloudFormation::Stack | CdkWorkshopStack 
 1/6 | 06:00:42 | DELETE_IN_PROGRESS   | AWS::SNS::Subscription | CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7 
 1/6 | 06:00:42 | DELETE_IN_PROGRESS   | AWS::SQS::QueuePolicy | CdkWorkshopQueuePolicyAF2494A5 
 2/6 | 06:00:43 | DELETE_COMPLETE      | AWS::SNS::Subscription | CdkWorkshopTopicCdkWorkshopQueueSubscription88D211C7 
 3/6 | 06:00:44 | DELETE_COMPLETE      | AWS::SQS::QueuePolicy | CdkWorkshopQueuePolicyAF2494A5 
 3/6 | 06:00:45 | DELETE_IN_PROGRESS   | AWS::SNS::Topic    | CdkWorkshopTopicD368A42F 
 3/6 | 06:00:46 | DELETE_IN_PROGRESS   | AWS::SQS::Queue    | CdkWorkshopQueue50D9D426 
 4/6 | 06:00:46 | DELETE_COMPLETE      | AWS::SNS::Topic    | CdkWorkshopTopicD368A42F 
4/6 Currently in progress: CdkWorkshopStack, CdkWorkshopQueue50D9D426

   CdkWorkshopStack

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxx:stack/CdkWorkshopStack/a5bd1260-31a4-11e9-87b8-0ec110ab8a1e

Lambda と API Gateway の構成方法

次に、AWS Lambda(以下、Lambda)、Amazon API Gateway(以下、APIGW)のサーバーレス構成を CDK で行います。

まず最初に、Lambda のコードを作成します。

binやlibと同じ階層に lambda/hello.js を作成し、以下のコードを記載します。

APIGW の URI にアクセスすると、「Hello, CDK! You've hit /(アクセスしたパス)」と返すコードになります。

exports.handler = async function(event) {
  console.log('request:', JSON.stringify(event, undefined, 2));
  return {
    statusCode: 200,
    headers: { 'Content-Type': 'text/plain' },
    body: `Hello, CDK! You've hit ${event.path}\n`
  };
};

加えて、 Constructs ライブラリを追加します。このライブラリは AWS サービスごとにあり、AWSのベストプラクティスに従った設定を内包しています。

今回でいうと、 Lambda を定義したり、 APIGW を定義するには上記のライブラリをインストールする必要があります。

$ npm install @aws-cdk/aws-lambda@0.22.0
npm WARN cdk-workshop@0.1.0 No repository field.
npm WARN cdk-workshop@0.1.0 No license field.

+ @aws-cdk/aws-lambda@0.22.0
updated 1 package and audited 1901 packages in 3.194s
found 0 vulnerabilities
$ npm install @aws-cdk/aws-apigateway@0.22.0
npm WARN cdk-workshop@0.1.0 No repository field.
npm WARN cdk-workshop@0.1.0 No license field.

+ @aws-cdk/aws-apigateway@0.22.0
added 1 package from 1 contributor and audited 2476 packages in 6.122s
found 0 vulnerabilities

APIGW と Lambda を統合するコードを lib/cdk-workshop-stack.tsに追記します。

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

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    
    const hello = new lambda.Function(this, 'HelloHandler', {
      runtime: lambda.Runtime.NodeJS810,
      code: lambda.Code.asset('lambda'),
      handler: 'hello.handler'
    });
    
    new apigw.LambdaRestApi(this, 'Endpoint', {
      handler:hello
    });
  }
}

cdk diff で変更箇所の確認してみます。

$ cdk diff
The CdkWorkshopStack stack uses assets, which are currently not accounted for in the diff output! See https://github.com/awslabs/aws-cdk/issues/395
IAM Statement Changes
┌───┬─────────────────────────────────┬────────┬───────────────────────┬──────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                        │ Effect │ Action                │ Principal                        │ Condition                                                                                                                                               │
├───┼─────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Endpoint/CloudWatchRole.Arn}  │ Allow  │ sts:AssumeRole        │ Service:apigateway.amazonaws.com │                                                                                                                                                         │
├───┼─────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloHandler}                 │ Allow  │ lambda:InvokeFunction │ Service:apigateway.amazonaws.com │ "ArnLike": {                                                                                                                                            │
│   │                                 │        │                       │                                  │   "AWS:SourceArn": "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${EndpointEEF1FD8F}/${Endpoint/DeploymentStage.prod}/*/"         │
│   │                                 │        │                       │                                  │ }                                                                                                                                                       │
├───┼─────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloHandler}                 │ Allow  │ lambda:InvokeFunction │ Service:apigateway.amazonaws.com │ "ArnLike": {                                                                                                                                            │
│   │                                 │        │                       │                                  │   "AWS:SourceArn": "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${EndpointEEF1FD8F}/test-invoke-stage/*/"                        │
│   │                                 │        │                       │                                  │ }                                                                                                                                                       │
├───┼─────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloHandler}                 │ Allow  │ lambda:InvokeFunction │ Service:apigateway.amazonaws.com │ "ArnLike": {                                                                                                                                            │
│   │                                 │        │                       │                                  │   "AWS:SourceArn": "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${EndpointEEF1FD8F}/${Endpoint/DeploymentStage.prod}/*/{proxy+}" │
│   │                                 │        │                       │                                  │ }                                                                                                                                                       │
├───┼─────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloHandler}                 │ Allow  │ lambda:InvokeFunction │ Service:apigateway.amazonaws.com │ "ArnLike": {                                                                                                                                            │
│   │                                 │        │                       │                                  │   "AWS:SourceArn": "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${EndpointEEF1FD8F}/test-invoke-stage/*/{proxy+}"                │
│   │                                 │        │                       │                                  │ }                                                                                                                                                       │
├───┼─────────────────────────────────┼────────┼───────────────────────┼──────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloHandler/ServiceRole.Arn} │ Allow  │ sts:AssumeRole        │ Service:lambda.amazonaws.com     │                                                                                                                                                         │
└───┴─────────────────────────────────┴────────┴───────────────────────┴──────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────┘
IAM Policy Changes
┌───┬─────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                    │ Managed Policy ARN                                                                      │
├───┼─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Endpoint/CloudWatchRole}  │ arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs │
├───┼─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${HelloHandler/ServiceRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole          │
└───┴─────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np)

Parameters
[+] Parameter HelloHandler/Code/S3Bucket HelloHandlerCodeS3Bucket4359A483: {"Type":"String","Description":"S3 bucket for asset \"CdkWorkshopStack/HelloHandler/Code\""}
[+] Parameter HelloHandler/Code/S3VersionKey HelloHandlerCodeS3VersionKey07D12610: {"Type":"String","Description":"S3 key for asset version \"CdkWorkshopStack/HelloHandler/Code\""}

Resources
[+] AWS::IAM::Role HelloHandler/ServiceRole HelloHandlerServiceRole11EF7C63 
[+] AWS::Lambda::Function HelloHandler HelloHandler2E4FBA4D 
[+] AWS::Lambda::Permission HelloHandler/ApiPermission.ANY.. HelloHandlerApiPermissionANYAC4E141E 
[+] AWS::Lambda::Permission HelloHandler/ApiPermission.Test.ANY.. HelloHandlerApiPermissionTestANYDDD56D72 
[+] AWS::Lambda::Permission HelloHandler/ApiPermission.ANY..{proxy+} HelloHandlerApiPermissionANYproxy90E90CD6 
[+] AWS::Lambda::Permission HelloHandler/ApiPermission.Test.ANY..{proxy+} HelloHandlerApiPermissionTestANYproxy9803526C 
[+] AWS::ApiGateway::RestApi Endpoint EndpointEEF1FD8F 
[+] AWS::ApiGateway::Deployment Endpoint/Deployment EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf 
[+] AWS::ApiGateway::Stage Endpoint/DeploymentStage.prod EndpointDeploymentStageprodB78BEEA0 
[+] AWS::IAM::Role Endpoint/CloudWatchRole EndpointCloudWatchRoleC3C64E0F 
[+] AWS::ApiGateway::Account Endpoint/Account EndpointAccountB8304247 
[+] AWS::ApiGateway::Resource Endpoint/{proxy+} Endpointproxy39E2174E 
[+] AWS::ApiGateway::Method Endpoint/{proxy+}/ANY EndpointproxyANYC09721C5 
[+] AWS::ApiGateway::Method Endpoint/ANY EndpointANY485C938B 

Outputs
[+] Output Endpoint/Endpoint Endpoint8024A810: {"Value":{"Fn::Join":["",["https://",{"Ref":"EndpointEEF1FD8F"},".execute-api.",{"Ref":"AWS::Region"},".",{"Ref":"AWS::URLSuffix"},"/",{"Ref":"EndpointDeploymentStageprodB78BEEA0"},"/"]]},"Export":{"Name":"CdkWorkshopStack:Endpoint8024A810"}}

それでは、デプロイしてみます。

$ cdk deploy
Do you wish to deploy these changes (y/n)? y
CdkWorkshopStack: deploying...
Updated: lambda (zip)
CdkWorkshopStack: creating CloudFormation changeset...
  0/16 | 06:53:20 | CREATE_IN_PROGRESS   | AWS::IAM::Role              | HelloHandler/ServiceRole (HelloHandlerServiceRole11EF7C63) 
  0/16 | 06:53:20 | CREATE_IN_PROGRESS   | AWS::IAM::Role              | Endpoint/CloudWatchRole (EndpointCloudWatchRoleC3C64E0F) 
  0/16 | 06:53:20 | CREATE_IN_PROGRESS   | AWS::ApiGateway::RestApi    | Endpoint (EndpointEEF1FD8F) 
  0/16 | 06:53:21 | CREATE_IN_PROGRESS   | AWS::IAM::Role              | Endpoint/CloudWatchRole (EndpointCloudWatchRoleC3C64E0F) Resource creation Initiated
  0/16 | 06:53:21 | CREATE_IN_PROGRESS   | AWS::IAM::Role              | HelloHandler/ServiceRole (HelloHandlerServiceRole11EF7C63) Resource creation Initiated
  0/16 | 06:53:21 | CREATE_IN_PROGRESS   | AWS::ApiGateway::RestApi    | Endpoint (EndpointEEF1FD8F) Resource creation Initiated
  1/16 | 06:53:21 | CREATE_COMPLETE      | AWS::ApiGateway::RestApi    | Endpoint (EndpointEEF1FD8F) 
  1/16 | 06:53:21 | UPDATE_IN_PROGRESS   | AWS::CDK::Metadata          | CDKMetadata 
  2/16 | 06:53:24 | UPDATE_COMPLETE      | AWS::CDK::Metadata          | CDKMetadata 
  2/16 | 06:53:25 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Resource   | Endpoint/{proxy+} (Endpointproxy39E2174E) 
  2/16 | 06:53:26 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Resource   | Endpoint/{proxy+} (Endpointproxy39E2174E) Resource creation Initiated
  3/16 | 06:53:26 | CREATE_COMPLETE      | AWS::ApiGateway::Resource   | Endpoint/{proxy+} (Endpointproxy39E2174E) 
  4/16 | 06:53:39 | CREATE_COMPLETE      | AWS::IAM::Role              | Endpoint/CloudWatchRole (EndpointCloudWatchRoleC3C64E0F) 
  5/16 | 06:53:39 | CREATE_COMPLETE      | AWS::IAM::Role              | HelloHandler/ServiceRole (HelloHandlerServiceRole11EF7C63) 
  5/16 | 06:53:42 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Account    | Endpoint/Account (EndpointAccountB8304247) 
  5/16 | 06:53:43 | CREATE_IN_PROGRESS   | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) 
  5/16 | 06:53:43 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Account    | Endpoint/Account (EndpointAccountB8304247) Resource creation Initiated
  6/16 | 06:53:43 | CREATE_COMPLETE      | AWS::ApiGateway::Account    | Endpoint/Account (EndpointAccountB8304247) 
  6/16 | 06:53:44 | CREATE_IN_PROGRESS   | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) Resource creation Initiated
  7/16 | 06:53:44 | CREATE_COMPLETE      | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) 
  7/16 | 06:53:47 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.Test.ANY.. (HelloHandlerApiPermissionTestANYDDD56D72) 
  7/16 | 06:53:47 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.Test.ANY..{proxy+} (HelloHandlerApiPermissionTestANYproxy9803526C) 
  7/16 | 06:53:48 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Method     | Endpoint/{proxy+}/ANY (EndpointproxyANYC09721C5) 
  7/16 | 06:53:48 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Method     | Endpoint/ANY (EndpointANY485C938B) 
  7/16 | 06:53:48 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.Test.ANY.. (HelloHandlerApiPermissionTestANYDDD56D72) Resource creation Initiated
  7/16 | 06:53:48 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.Test.ANY..{proxy+} (HelloHandlerApiPermissionTestANYproxy9803526C) Resource creation Initiated
  7/16 | 06:53:48 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Method     | Endpoint/{proxy+}/ANY (EndpointproxyANYC09721C5) Resource creation Initiated
  7/16 | 06:53:48 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Method     | Endpoint/ANY (EndpointANY485C938B) Resource creation Initiated
  8/16 | 06:53:49 | CREATE_COMPLETE      | AWS::ApiGateway::Method     | Endpoint/{proxy+}/ANY (EndpointproxyANYC09721C5) 
  9/16 | 06:53:49 | CREATE_COMPLETE      | AWS::ApiGateway::Method     | Endpoint/ANY (EndpointANY485C938B) 
  9/16 | 06:53:52 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Deployment | Endpoint/Deployment (EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf) 
  9/16 | 06:53:53 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Deployment | Endpoint/Deployment (EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf) Resource creation Initiated
 10/16 | 06:53:53 | CREATE_COMPLETE      | AWS::ApiGateway::Deployment | Endpoint/Deployment (EndpointDeployment318525DA37c0e38727e25b4317827bf43e918fbf) 
 10/16 | 06:53:56 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Stage      | Endpoint/DeploymentStage.prod (EndpointDeploymentStageprodB78BEEA0) 
 10/16 | 06:53:57 | CREATE_IN_PROGRESS   | AWS::ApiGateway::Stage      | Endpoint/DeploymentStage.prod (EndpointDeploymentStageprodB78BEEA0) Resource creation Initiated
 11/16 | 06:53:58 | CREATE_COMPLETE      | AWS::ApiGateway::Stage      | Endpoint/DeploymentStage.prod (EndpointDeploymentStageprodB78BEEA0) 
 12/16 | 06:53:58 | CREATE_COMPLETE      | AWS::Lambda::Permission     | HelloHandler/ApiPermission.Test.ANY.. (HelloHandlerApiPermissionTestANYDDD56D72) 
 13/16 | 06:53:58 | CREATE_COMPLETE      | AWS::Lambda::Permission     | HelloHandler/ApiPermission.Test.ANY..{proxy+} (HelloHandlerApiPermissionTestANYproxy9803526C) 
 13/16 | 06:54:02 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.ANY..{proxy+} (HelloHandlerApiPermissionANYproxy90E90CD6) 
 13/16 | 06:54:02 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.ANY.. (HelloHandlerApiPermissionANYAC4E141E) 
 13/16 | 06:54:02 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.ANY.. (HelloHandlerApiPermissionANYAC4E141E) Resource creation Initiated
 13/16 | 06:54:03 | CREATE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHandler/ApiPermission.ANY..{proxy+} (HelloHandlerApiPermissionANYproxy90E90CD6) Resource creation Initiated
 14/16 | 06:54:13 | CREATE_COMPLETE      | AWS::Lambda::Permission     | HelloHandler/ApiPermission.ANY.. (HelloHandlerApiPermissionANYAC4E141E) 
 15/16 | 06:54:13 | CREATE_COMPLETE      | AWS::Lambda::Permission     | HelloHandler/ApiPermission.ANY..{proxy+} (HelloHandlerApiPermissionANYproxy90E90CD6) 
 15/16 | 06:54:16 | UPDATE_COMPLETE_CLEA | AWS::CloudFormation::Stack  | CdkWorkshopStack 
 16/16 | 06:54:17 | UPDATE_COMPLETE      | AWS::CloudFormation::Stack  | CdkWorkshopStack 

   CdkWorkshopStack

Outputs:
CdkWorkshopStack.Endpoint8024A810 = https://vam8k4pk2e.execute-api.ap-northeast-1.amazonaws.com/prod/

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/CdkWorkshopStack/a5bd1260-31a4-11e9-87b8-0ec110ab8a1e

デプロイされたエンドポイントに対して、アクセスしてみたら想定通りの結果が帰ってきました。

$ curl https://vam8k4pk2e.execute-api.ap-northeast-1.amazonaws.com/prod/
Hello, CDK! You've hit /

DynamoDB リソースの追加

次に、Amazon DynamoDB(以下、DynamoDB)のリソースを追加します。

アクセスした URI のパスを DynamoDB のテーブルに記録する処理を定義するものを定義します。

まずは、DynamoDB の Constructs ライブラリをインストールします。

$ npm install @aws-cdk/aws-dynamodb@0.22.0
npm WARN cdk-workshop@0.1.0 No repository field.
npm WARN cdk-workshop@0.1.0 No license field.

+ @aws-cdk/aws-dynamodb@0.22.0
added 3 packages from 1 contributor and audited 2564 packages in 10.132s
found 0 vulnerabilities

lambda/hitcounter.js に以下のコードを追加します。

const { DynamoDB, Lambda } = require('aws-sdk');

exports.handler = async function(event) {
  console.log("request:", JSON.stringify(event, undefined, 2));

  // create AWS SDK clients
  const dynamo = new DynamoDB();
  const lambda = new Lambda();

  // update dynamo entry for "path" with hits++
  await dynamo.updateItem({
    TableName: process.env.HITS_TABLE_NAME, # DynamoDBテーブルの名前
    Key: { path: { S: event.path } },
    UpdateExpression: 'ADD hits :incr',
    ExpressionAttributeValues: { ':incr': { N: '1' } }
  }).promise();

  // call downstream function and capture response
  const resp = await lambda.invoke({
    FunctionName: process.env.DOWNSTREAM_FUNCTION_NAME, #Lambda関数名
    Payload: JSON.stringify(event)
  }).promise();

  console.log('downstream response:', JSON.stringify(resp, undefined, 2));

  // return response back to upstream caller
  return JSON.parse(resp.Payload);
};

lib/hitcounter.tsは次のように編集します。

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

export interface HitCounterProps {
  /** the function for which we want to count url hits **/
  downstream: lambda.Function;
}

export class HitCounter extends cdk.Construct {
  /** allows accessing the counter function */
  public readonly handler: lambda.Function;

  constructor(scope: cdk.Construct, id: string, props: HitCounterProps) {
    super(scope, id);

    const table = new dynamodb.Table(this, 'Hits');
    table.addPartitionKey({ name: 'path', type: dynamodb.AttributeType.String });

    this.handler = new lambda.Function(this, 'HitCounterHandler', {
      runtime: lambda.Runtime.NodeJS810,
      handler: 'hitcounter.handler',
      code: lambda.Code.asset('lambda'),
      environment: {
        DOWNSTREAM_FUNCTION_NAME: props.downstream.functionName,
        HITS_TABLE_NAME: table.tableName
      }
    });

    // grant the lambda role read/write permissions to our table
    table.grantReadWriteData(this.handler.role);

    // grant the lambda role invoke permissions to the downstream function
    props.downstream.grantInvoke(this.handler.role);
  }
}

lib/cdk-workshop-stack.ts は次のように編集します。

import cdk = require('@aws-cdk/cdk');
import lambda = require('@aws-cdk/aws-lambda');
import apigw = require('@aws-cdk/aws-apigateway');
import { HitCounter } from './hitcounter';

export class CdkWorkshopStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const hello = new lambda.Function(this, 'HelloHandler', {
      runtime: lambda.Runtime.NodeJS810,
      code: lambda.Code.asset('lambda'),
      handler: 'hello.handler'
    });

    const helloWithCounter = new HitCounter(this, 'HelloHitCounter', {
      downstream: hello
    });

    // defines an API Gateway REST API resource backed by our "hello" function.
    new apigw.LambdaRestApi(this, 'Endpoint', {
      handler: helloWithCounter.handler
    });
  }
}

それではデプロイします。

$ cdk deploy
CdkWorkshopStack: deploying...
Updated: lambda (zip)
Updated: lambda (zip)
CdkWorkshopStack: creating CloudFormation changeset...
 0/6 | 07:43:28 | UPDATE_IN_PROGRESS   | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) 
 1/6 | 07:43:29 | UPDATE_COMPLETE      | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) 
 1/6 | 07:43:36 | UPDATE_IN_PROGRESS   | AWS::Lambda::Function       | HelloHitCounter/HitCounterHandler (HelloHitCounterHitCounterHandlerDAEA7B37) 
 2/6 | 07:43:37 | UPDATE_COMPLETE      | AWS::Lambda::Function       | HelloHitCounter/HitCounterHandler (HelloHitCounterHitCounterHandlerDAEA7B37) 

   CdkWorkshopStack

Outputs:
CdkWorkshopStack.Endpoint8024A810 = https://vam8k4pk2e.execute-api.ap-northeast-1.amazonaws.com/prod/

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:xxxxxxxxxxxx:stack/CdkWorkshopStack/a5bd1260-31a4-11e9-87b8-0ec110ab8a1e

URI にアクセスしてみます。

 $ curl -i https://vam8k4pk2e.execute-api.ap-northeast-1.amazonaws.com/prod/
Hello, CDK! You've hit /
 $ curl -i https://vam8k4pk2e.execute-api.ap-northeast-1.amazonaws.com/prod/hello
Hello, CDK! You've hit /hello
 $ curl -i https://vam8k4pk2e.execute-api.ap-northeast-1.amazonaws.com/prod/test
Hello, CDK! You've hit /test

3回アクセスしたので、 DynamoDB のテーブルに3つレコードが登録されていることを確認できました。 f:id:sadayoshi_tada:20190216175856p:plain

最後は、作ったリソースを削除しますが、cdk destroy を実行します。

$ cdk destroy
Are you sure you want to delete: CdkWorkshopStack (y/n)? y
CdkWorkshopStack: destroying...
   0 | 07:55:23 | DELETE_IN_PROGRESS   | AWS::CloudFormation::Stack  | CdkWorkshopStack User Initiated
   0 | 07:55:25 | DELETE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.Test.ANY.. (HelloHitCounterHitCounterHandlerApiPermissionTestANY1DD26B5F) 
   0 | 07:55:25 | DELETE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.Test.ANY..{proxy+} (HelloHitCounterHitCounterHandlerApiPermissionTestANYproxyDBD04C8D) 
   0 | 07:55:25 | DELETE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.ANY.. (HelloHitCounterHitCounterHandlerApiPermissionANYBC1D138C) 
   0 | 07:55:25 | DELETE_IN_PROGRESS   | AWS::CDK::Metadata          | CDKMetadata 
   0 | 07:55:25 | DELETE_IN_PROGRESS   | AWS::ApiGateway::Account    | Endpoint/Account (EndpointAccountB8304247) 
   0 | 07:55:26 | DELETE_IN_PROGRESS   | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.ANY..{proxy+} (HelloHitCounterHitCounterHandlerApiPermissionANYproxy4F7D3D9D) 
   1 | 07:55:26 | DELETE_COMPLETE      | AWS::ApiGateway::Account    | Endpoint/Account (EndpointAccountB8304247) 
   1 | 07:55:27 | DELETE_IN_PROGRESS   | AWS::IAM::Role              | Endpoint/CloudWatchRole (EndpointCloudWatchRoleC3C64E0F) 
   2 | 07:55:28 | DELETE_COMPLETE      | AWS::CDK::Metadata          | CDKMetadata 
   3 | 07:55:28 | DELETE_COMPLETE      | AWS::IAM::Role              | Endpoint/CloudWatchRole (EndpointCloudWatchRoleC3C64E0F) 
   4 | 07:55:36 | DELETE_COMPLETE      | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.Test.ANY..{proxy+} (HelloHitCounterHitCounterHandlerApiPermissionTestANYproxyDBD04C8D) 
   5 | 07:55:36 | DELETE_COMPLETE      | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.ANY.. (HelloHitCounterHitCounterHandlerApiPermissionANYBC1D138C) 
   6 | 07:55:36 | DELETE_COMPLETE      | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.ANY..{proxy+} (HelloHitCounterHitCounterHandlerApiPermissionANYproxy4F7D3D9D) 
   7 | 07:55:36 | DELETE_COMPLETE      | AWS::Lambda::Permission     | HelloHitCounter/HitCounterHandler/ApiPermission.Test.ANY.. (HelloHitCounterHitCounterHandlerApiPermissionTestANY1DD26B5F) 
   7 | 07:55:37 | DELETE_IN_PROGRESS   | AWS::ApiGateway::Stage      | Endpoint/DeploymentStage.prod (EndpointDeploymentStageprodB78BEEA0) 
   8 | 07:55:39 | DELETE_COMPLETE      | AWS::ApiGateway::Stage      | Endpoint/DeploymentStage.prod (EndpointDeploymentStageprodB78BEEA0) 
   8 | 07:55:40 | DELETE_IN_PROGRESS   | AWS::ApiGateway::Deployment | Endpoint/Deployment (EndpointDeployment318525DA741e521b7179af57a3425e580c915cc2) 
   9 | 07:55:41 | DELETE_COMPLETE      | AWS::ApiGateway::Deployment | Endpoint/Deployment (EndpointDeployment318525DA741e521b7179af57a3425e580c915cc2) 
   9 | 07:55:42 | DELETE_IN_PROGRESS   | AWS::ApiGateway::Method     | Endpoint/{proxy+}/ANY (EndpointproxyANYC09721C5) 
   9 | 07:55:42 | DELETE_IN_PROGRESS   | AWS::ApiGateway::Method     | Endpoint/ANY (EndpointANY485C938B) 
  10 | 07:55:42 | DELETE_COMPLETE      | AWS::ApiGateway::Method     | Endpoint/{proxy+}/ANY (EndpointproxyANYC09721C5) 
  11 | 07:55:42 | DELETE_COMPLETE      | AWS::ApiGateway::Method     | Endpoint/ANY (EndpointANY485C938B) 
  11 | 07:55:43 | DELETE_IN_PROGRESS   | AWS::ApiGateway::Resource   | Endpoint/{proxy+} (Endpointproxy39E2174E) 
  11 | 07:55:44 | DELETE_IN_PROGRESS   | AWS::Lambda::Function       | HelloHitCounter/HitCounterHandler (HelloHitCounterHitCounterHandlerDAEA7B37) 
  12 | 07:55:44 | DELETE_COMPLETE      | AWS::ApiGateway::Resource   | Endpoint/{proxy+} (Endpointproxy39E2174E) 
  13 | 07:55:44 | DELETE_COMPLETE      | AWS::Lambda::Function       | HelloHitCounter/HitCounterHandler (HelloHitCounterHitCounterHandlerDAEA7B37) 
  13 | 07:55:45 | DELETE_IN_PROGRESS   | AWS::ApiGateway::RestApi    | Endpoint (EndpointEEF1FD8F) 
  13 | 07:55:45 | DELETE_IN_PROGRESS   | AWS::IAM::Policy            | HelloHitCounter/HitCounterHandler/ServiceRole/DefaultPolicy (HelloHitCounterHitCounterHandlerServiceRoleDefaultPolicy1487A60A) 
  14 | 07:55:46 | DELETE_COMPLETE      | AWS::ApiGateway::RestApi    | Endpoint (EndpointEEF1FD8F) 
  15 | 07:55:47 | DELETE_COMPLETE      | AWS::IAM::Policy            | HelloHitCounter/HitCounterHandler/ServiceRole/DefaultPolicy (HelloHitCounterHitCounterHandlerServiceRoleDefaultPolicy1487A60A) 
  15 | 07:55:48 | DELETE_IN_PROGRESS   | AWS::DynamoDB::Table        | HelloHitCounter/Hits (HelloHitCounterHits7AAEBF80) 
  15 | 07:55:48 | DELETE_IN_PROGRESS   | AWS::IAM::Role              | HelloHitCounter/HitCounterHandler/ServiceRole (HelloHitCounterHitCounterHandlerServiceRoleD45002B8) 
  15 | 07:55:48 | DELETE_IN_PROGRESS   | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) 
  16 | 07:55:49 | DELETE_COMPLETE      | AWS::Lambda::Function       | HelloHandler (HelloHandler2E4FBA4D) 
  17 | 07:55:49 | DELETE_COMPLETE      | AWS::IAM::Role              | HelloHitCounter/HitCounterHandler/ServiceRole (HelloHitCounterHitCounterHandlerServiceRoleD45002B8) 
  17 | 07:55:50 | DELETE_IN_PROGRESS   | AWS::IAM::Role              | HelloHandler/ServiceRole (HelloHandlerServiceRole11EF7C63) 
  18 | 07:55:51 | DELETE_COMPLETE      | AWS::IAM::Role              | HelloHandler/ServiceRole (HelloHandlerServiceRole11EF7C63) 

   CdkWorkshopStack: destroyed

AWS CDK と CloudFormation の比較

AWS CDK」を使ってみて CloudFormation と使い分けする場合の比較をしてみようと思います。

AWS CDK と CloudFormation を比較してみてのメリット

  • 普段書き慣れている言語であれば、YAMLJSON の CloudFormation よりは書きやすい
  • 感覚的にリソースのデプロイが CloudFormation よりは簡易
    • !Ref のような関数を使わなくても関連リソースの定義ができるのはよかった
    • Lambda の IAM ロール設定が table.grantReadWriteData(this.handler.role); だけなので、CloudFormation のように ARN を指定しなくていいのでシンプルだと感じた
  • IDEで書いているため、コードの中でのライブラリ定義の参照元などをすぐに調べやすい

AWS CDK と CloudFormation を比較してみてのデメリット

  • サポートされてる言語の習得が必須であること
  • 自由度が高く CloudFormation テンプレートを書けるのでプロジェクト内での運用ルールを考える必要はありそう
  • CloudFormation のサポートしている JSONYAMLの方が開発者が馴染むのなら CloudFormation の方が良い

まとめ

AWS CDK」を使った AWS リソース構成の実践と、 CloudFormation との使い分けを自分なりに考察してみました。

現状サポートされている言語が少ないものの、個人的には今後の Python サポートが楽しみです。

プログマブルに条件分岐や繰り返しの処理などを組み合わせて、 CloudFormation だけではできなかった運用ができそうです。

なお、 Constructs ライブラリのレファレンスはこちらです。 awslabs.github.io

本リリースまでに CloudFormation で作ったソースを置き換えたりしてみて更に使い込んでみたいです。