【初心者向け】AWS CDK TypeScript 入門

記事タイトルとURLをコピーする

はじめに

AWS CDK (以下、CDK)に入門したのでその内容を記事にまとめました。

以下の公式 Workshop Studio の記事を参考にしています。

catalog.workshops.aws

本記事のゴールとしてはCDKで基本的なVPCを作成することです。

前提と必要なもの

実行する環境はCloud9を利用します。
Cloud9のセットアップ方法については今回は省略します。
その他、CDKを実行するために必要なものとしては以下の通り。

  • AWS CDK
  • Node.js
  • NPM
  • TypeScript

CDKインストール 確認

Cloud9 で起動したインスタンスにはCDKがデフォルトインストールされています。

## CDK バージョンを確認
cdk --version
2.87.0 (build 9fca790)

Node.js と NPM

CDKはNode.jsのパッケージ(NPM)のうちの一つです。 どちらもTypeScriptでCDKを動作させるために必要不可欠なものです。

## Node.js バージョンを確認
node --version
v16.20.1

## NPM バージョンを確認
npm --version
8.19.4

TypeScript Compiler を最新バージョンにアップグレード

CDKは以下の言語がサポートされていますが、今回はTypeScriptで動作確認します。

  • TypeScript
  • JavaScript
  • Python
  • Java
  • C#
  • Go

www.typescriptlang.org

Cloud9へデフォルトインストールされているTypeScriptから最新のバージョンへアップグレードします。

## TypeScript 現在のバージョン
tsc --version
Version 3.7.5

## npmでアップグレード
npm -g upgrade typescript

## アップグレード後のバージョン
tsc --version
Version 5.1.6

TypeScript 基礎

いきなりCDKでTypeScriptを実行させる前に、TypeScriptの基礎を少し学びます。

プロジェクトの準備

まずは、TypeScriptで動作するプロジェクトの準備を行います。

## ディレクトリを作成
mkdir test-cdk

## カレントディレクトリ移動
cd test-cdk

## npm 初期化 … package.json が出力される
npm init -y

Wrote to /home/ec2-user/environment/test-cdk/package.json:

{
  "name": "test-cdk",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"

}

npm 初期化をすることで、package.jsonが自動作成されます。 package.jsonはnpmパッケージに関する設定情報が記述されるファイルです。

nodeパッケージ インストール

実際にパッケージをインストールします。 今回は@types/node ライブラリをインストールします。 @types/node とはNode.jsの型定義(TypeScriptの型情報)を含むパッケージでTypeScriptをJavaScriptへコンパイルするために必要なものです。

## インストール
npm install @types/node

## package.json 確認
cat package.json
{
  "name": "test-cdk",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/node": "^20.4.5"
  }
}

"dependencies" セクションに @types/node が追加されていることを確認できます。

TypeScriptの実行

## TypeScript 初期化 … tsconfig.json
tsc --init

Created a new tsconfig.json with:                                                                             
                                                                                                           TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true

tsc init を実行することで、tsconfig.jsonが自動生成されます。

tsconfig.json を編集

TypeScriptをJavaScritpへコンパイルする際に変換されたJavaScriptを出力するディレクトリを指定します。

## tsconfig.jsonを編集
vim tsconfig.json
  • 変更前
// "outDir": "./",                        /* Redirect output structure to the directory. */
  • 変更後
"outDir": "./dist",                       /* Redirect output structure to the directory. */

TypeScript ファイル作成

実行させるTypeScriptファイルを作成します。

  • helloWorld.ts
console.log('Hello, World!');

TypeScript 実行

helloWorld.tsを展開し、 Run をクリックして実行します。

cdk > test-cdk > helloWorld.ts

コンパイルが実行されて変換された後に、 Hello, World! が出力されていることを確認できます。

Run セクション

以上がTypeScriptの基礎です。
Hello Worldを出力しただけですが、TypeScriptがどうやって動作するかについて少し理解できたと思います。

CDK プロジェクトの準備

ここからCDKで実際にVPCのリソースを作成していきます。

## カレントディレクトリ 移動
cd ~/environment/

## プロジェクトディレクトリ作成
mkdir cdk-workshop

## CDK 初期化
cdk init sample-app --language typescript

CDK ディレクトリ構成 確認

必要なディレクトリなどが自動生成されます。
以下の構成です。

cdk-workshop/
├── bin/
│   └── cdk-workshop.ts
├── lib/
│   └── cdk-workshop-stack.ts
├── node_modules/
├── test/
│   └── cdk-workshop.test.ts
├── .git/
├── cdk.json
├── jest.config.js
├── package.json
├── README.md
├── tsconfig.json
├── .gitignore
└── .npmignore

主要なファイルについて説明します。

bin/cdk-workshop.ts

エントリーポイント という始めに実行されるtsファイルです。

#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { CdkWorkshopStack } from '../lib/cdk-workshop-stack';

const app = new cdk.App();
new CdkWorkshopStack(app, 'CdkWorkshopStack');

lib/cdk-workshop-stackをimportしてインスタンス化しています。 lib/cdk-workshop-staskを確認します。

lib/cdk-workshop-stack.ts

メインスタック という、リソース定義の要となるtsファイルです。

import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Construct } from 'constructs';

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

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

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

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}
  • aws-snsやaws-sqsがimportされています。
    サンプルとしてAWS SNSとSQSなどのリソースが作成されます。
    ここに作成したいリソースを記述していくのが基本的な使い方となります。

  • もう一つのポイントは export class CdkWorkshopStack extends Stack { の記述です。
    このexportの記述があることで、bin/cdk-workshop.ts ファイルに import { CdkWorkshopStack } from '../lib/cdk-workshop-stack'; と記述して呼び出すことができます。

cdk コマンド

cdkコマンドでリソースを作成します。 作成する前にCloudFormationのテンプレートを出力して確認することができます。

cdk synth

cdk synth とタイプすると、定義したアプリケーションで作成されるリソースがCloudFormationのテンプレートとして出力されます。

cdk synth

…

Resources:
  CdkWorkshopQueue50D9D426:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 300
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    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
  CdkWorkshopQueueCdkWorkshopStackCdkWorkshopTopicD7BE96438B5AD106:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: sqs
      TopicArn:
        Ref: CdkWorkshopTopicD368A42F
      Endpoint:
        Fn::GetAtt:
          - CdkWorkshopQueue50D9D426
          - Arn
    DependsOn:
      - CdkWorkshopQueuePolicyAF2494A5
    Metadata:
      aws:cdk:path: CdkWorkshopStack/CdkWorkshopQueue/CdkWorkshopStackCdkWorkshopTopicD7BE9643/Resource
  CdkWorkshopTopicD368A42F:
    Type: AWS::SNS::Topic
    Metadata:
      aws:cdk:path: 
〜省略〜

tsファイルへ定義しているとおり、SNSやSQSのリソースが作成されることを確認できます。

cdk bootstrap

アプリケーションを初めてデプロイする前に、前段階として cdk bootstrap を実行する必要があります。
これを実行することで AWS アカウントの指定したリージョンにデプロイできます。
今回はCloud9を実行しているリージョンへ自動的にデプロイされるようになっています。

## cdk bootstrap実行 … デプロイされるAWSアカウントとリージョンが出力される
cdk bootstrap

 ⏳  Bootstrapping environment aws://【アカウント番号】/ap-northeast-1...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize.

 ✨ hotswap deployment skipped - no changes were detected (use --force to override)

 ✅  Environment aws://【アカウント番号】/ap-northeast-1 bootstrapped (no changes).

cdk deploy

いよいよ、デプロイを実行します。

## デプロイ実行 … 
cdk deploy

✨  Synthesis time: 13.87s

…
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬──────────────────────┬────────┬──────────────────────┬──────────────────────┬─────────────────────────┐
│   │ Resource             │ Effect │ Action               │ Principal            │ Condition               │
├───┼──────────────────────┼────────┼──────────────────────┼──────────────────────┼─────────────────────────┤
│ + │ ${CdkWorkshopQueue.A │ Allow  │ sqs:SendMessage      │ Service:sns.amazonaw │ "ArnEquals": {          │
│   │ rn}                  │        │                      │ s.com                │   "aws:SourceArn": "${C
│   │                      │        │                      │                      │ dkWorkshopTopic}"       │
│   │                      │        │                      │                      │ }                       │
└───┴──────────────────────┴────────┴──────────────────────┴──────────────────────┴─────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)?  

警告が表示されますが、 y + Enter して進みます。

途中、CloudFormationのステータスが表示され、定義したリソースが作成されていること確認できます。

## 【y を Enter】
CdkWorkshopStack: deploying... [1/1]
CdkWorkshopStack: creating CloudFormation changeset...
[███████████████████▎······································] (2/6)

10:10:45 PM | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack | CdkWorkshopStack
10:10:50 PM | CREATE_IN_PROGRESS   | AWS::SQS::Queue        | CdkWorkshopQueue

完了しました。

 ✅  CdkWorkshopStack

✨  Deployment time: 92.26s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:【アカウント番号】:stack/CdkWorkshopStack/c0742490-2e5c-11ee-ab2b-0ee353da8cf9

✨  Total time: 106.13s

AWS マネジメントコンソールのCloudFormationの画面から作成されたリソースを確認しましょう。

CloudFormation > スタック > CdkWorkshopStack

サンプルアプリケーションのクリーンアップ

サンプルとして生成されたSNSやSQSのリソースは不要なので削除します。

lib/cdk-workshop-stack.ts 編集

cdk-workshop-stack.tsへ定義されているSNSとSQS関連の記述を削除します。

## lib/cdk-workshop-stack.ts 編集
vim lib/cdk-workshop-stack.ts

変更後は以下のような内容になります。

  • lib/cdk-workshop-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkWorkshopStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
  }
}

cdk diff

CDKでは、変更をデプロイする前に変更差分を確認することができます。

## 差分を確認 … 変更後の内容が出力される
cdk diff

Stack CdkWorkshopStack
current credentials could not be used to assume 'arn:aws:iam::【アカウント番号】:role/cdk-hnb659fds-lookup-role-【アカウント番号】-ap-northeast-1', but are for the right account. Proceeding anyway.
(To get rid of this warning, please upgrade to bootstrap version >= 8)
current credentials could not be used to assume 'arn:aws:iam::【アカウント番号】:role/cdk-hnb659fds-deploy-role-【アカウント番号】-ap-northeast-1', but are for the right account. Proceeding anyway.
IAM Statement Changes
┌───┬───────────────────────────────────┬────────┬─────────────────┬───────────────────────────────────┬─────────────────────────────────────┐
│   │ Resource                          │ Effect │ Action          │ Principal                         │ Condition                           │
├───┼───────────────────────────────────┼────────┼─────────────────┼───────────────────────────────────┼─────────────────────────────────────┤
│ - │ ${CdkWorkshopQueue.Arn}           │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com         │ "ArnEquals": {                      │
│   │                                   │        │                 │                                   │   "aws:SourceArn": "${CdkWorkshopTo
│   │                                   │        │                 │                                   │ pic}"                               │
│   │                                   │        │                 │                                   │ }                                   │
└───┴───────────────────────────────────┴────────┴─────────────────┴───────────────────────────────────┴─────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[-] AWS::SQS::Queue CdkWorkshopQueue CdkWorkshopQueue50D9D426 destroy
[-] AWS::SQS::QueuePolicy CdkWorkshopQueue/Policy CdkWorkshopQueuePolicyAF2494A5 destroy
[-] AWS::SNS::Subscription CdkWorkshopQueue/CdkWorkshopStackCdkWorkshopTopicD7BE9643 CdkWorkshopQueueCdkWorkshopStackCdkWorkshopTopicD7BE96438B5AD106 destroy
[-] AWS::SNS::Topic CdkWorkshopTopic CdkWorkshopTopicD368A42F destroy

SNSやSQSのリソースが削除されることがわかります。

cdk deploy

それでは変更をデプロイしましょう。

cdk deploy

リソースが削除されていく様子が確認できます。

VPCをデプロイ

VPCをデプロイします。

ドキュメントを参考にしながらコーディングを進めていきます。

docs.aws.amazon.com

まずは、ドキュメントからVPCのリソースを探します。 左側のセクションから、aws-cdk-lib aws_ec2を展開します。

AWS CDK > aws-cdk-lib aws_ec2

Overviewをクリックします。

Overview
Overviewには該当リソースを宣言するために必要な import 文についての記載があります。

まずはこれにならって、lib/cdk-workshop-stack.ts へ import文を追記します。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
  
export class CdkWorkshopStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
  }
}

ちなみに、Cloud9上ではvimでファイルを編集できますが、 左セクションからファイルを選択して編集することで、コードの入力補完やコード生成が自動的に効いてコーディングがしやすくなります。

続いて、実装部分を追記します。 Constructsの中からVpcを選択します。

Constructs > Vpc
Exampleへサンプルコードが記載されています。

サンプルにならってVPCを宣言します。

  • lib/cdk-workshop-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
  
export class CdkWorkshopStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
  
    const vpc = new ec2.Vpc(this, "cdk-workshop-vpc", {
    ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16')
  })
  }
}

具体的に宣言したのはVPCのIPアドレスとVPCの名前だけです。 この状態でどの様なリソースが生成されるか確認してみましょう。

cdk synth

cdk synth
…
Resources:
  cdkworkshopvpcB521BF0D:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
…
  cdkworkshopvpcPublicSubnet1SubnetA142096B:
    Type: AWS::EC2::Subnet

…
      CidrBlock: 10.0.0.0/18
…

  cdkworkshopvpcPublicSubnet1EIP19F89EFE:
    Type: AWS::EC2::EIP

…
  cdkworkshopvpcPublicSubnet1NATGatewayA3D141A6:
    Type: AWS::EC2::NatGateway

…
  cdkworkshopvpcPublicSubnet2Subnet93C22F38:
    Type: AWS::EC2::Subnet
…

      CidrBlock: 10.0.64.0/18

…
  cdkworkshopvpcPublicSubnet2EIP7B9FC902:
    Type: AWS::EC2::EIP

…
  cdkworkshopvpcPublicSubnet2NATGatewayE9F4B63A:
    Type: AWS::EC2::NatGateway

…
  cdkworkshopvpcPrivateSubnet1Subnet90A70C18:
    Type: AWS::EC2::Subnet

…
      CidrBlock: 10.0.128.0/18

…
  cdkworkshopvpcPrivateSubnet2SubnetAFC4C1F8:
    Type: AWS::EC2::Subnet

…
      CidrBlock: 10.0.192.0/18

…
  cdkworkshopvpcIGW6B581DEB:
    Type: AWS::EC2::InternetGateway

VPCのリソースを実装しただけなのに以下のリソースが作成されることがわかりました。

  • 2AZへまたがったサブネットが4つ(プライベートサブネットx2、パブリックサブネットx2)
  • それぞれのAZごとに1つずつEIPを関連付けたNatGateway
  • 1つのInternetGateway

これは、Construct Propsを確認することで理解できます。

Vpc > Construct Props

この Construct Props には、そのリソースを宣言するためのパラメータが記載されています。
先程宣言したipAddressesもここに記載があり、Nameの値の最後に ? が記述されていることが確認できます。 これはその Construct Props がオプションであることを示しています。
つまり、 ? が書かれている Construct Props は設定しなくてもリソースを作成することができるということです。
では宣言しない場合はその設定値はどうなるのか。
それは、 Construct Props の各項目ごとの default:… の部分で確認することができます。

Construct Props

subnetConfigurationに、サブネットのデフォルト値も記載されています。

subnetConfiguration

cdk deploy

実際に確認したリソースが作成されることを確認しましょう。

cdk deploy

リソースのクリーンアップ

最後にCDKのプロジェクトを全て削除します。 AWS CloudFormation コンソールで、 CdkWorkshopStack のスタックを削除するか、以下のコマンドで削除できます。

## CDK スタックの削除
cdk destroy

Are you sure you want to delete: CdkWorkshopStack (y/n)? のメッセージが表示されたら、y + Enter でスタックが削除されていくことを確認できます。

スタックが削除できたら、Cloud9の環境自体の削除も忘れず実施してください。

最後に

普段CloudFormationでリソースを作成することが多い私にとっては、以下のような点がCDKのメリットだと思いました。

  • synth,diffが便利
  • 推奨値としてのデフォルト設定
  • 最小の記述で構築できる
  • 依存関係を自動的に考慮してくれる
  • テストしやすい

このあたりは別途深掘りして記事にまとめたいと思っています。

以上、CDK初心者向けの入門記事でした。 どなたかのお役に立てれば幸いです。

折戸 亮太(執筆記事の一覧)

2021年10月1日入社
クラウドインテグレーション部技術1課

屋根裏エンジニア