はじめに

本記事ではAWS CDKについて投稿します。

AWS CDKの学習を始めた理由は、以前はCloudFormationを使用してインフラストラクチャを構築していましたがコード量が多くなる、書いていて退屈、テストどうする?アプリのテストみたいにできんの?とか色々課題があったことがAWS CDKを学習するきっかけになりました。

AWS CDKとは(ざっくり)

CDKとはCloud Infrastructure Developer Kitの略でTypeScript、Java、Python、C#、.Net、Go言語などこれらの開発スキルがある該当するプログラミング言語を使用してソースコードでインフラストラクチャを定義します。

ソースコードでインフラストラクチャを構築できれば、同じ環境を複製したいときはマネージメントコンソールからの構築と比較すると遥かに工数を削減することができますし、手動作成から起因するオペレーティングミスの低減も期待ができます。

CDKには多くのメリットがありますが、その中でも私がAWS CDKの魅力を感じたのはユニットテストなどの従来のソフトウェア開発でも使われていたテスト手法がAWS CDKを使用すれば実現可能だということ。せっかくIaCでインフラ構築した後にコンソールで膨大にあるインフラソースを確認するのは割と苦痛だったためこれは期待したいところです。

参考公式ドキュメント : https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/home.html

前提条件

本記事では予め必要な環境が揃っているCloud9を使って進めていきます。

AWS CDKをローカル環境で実行するためには以下に示すものを事前に用意してください。

・AWSアカウントとユーザー

・AWS CLI

・IDE

・Node.js

・IDE

・AWS CDKツールキット

・ご使用になるプログラム言語(TypeScript、Python、Java等)

※ Cloud9のCloudFormationテンプレートをGitHubに置きました。
“practice-operation-cloud9.yaml”をそのままCloudFormationに流すとCloud9を使うことができます。

GitHub

※ CFnで構築されるCloud9構成図

AWS CDK for Cloud9

※本実施内容を進めるためにご自身のAWSアカウントのVPC内にCloud9が作成されていることが前提となります。

Cloud9では前述したAWS CDKやnpmなど既にインストールされている状態ですのですぐに開発を進めることができます。

試しに確認してみましょう。

$ node --version
Node.jsバージョン確認結果

CDKのバージョン確認

※ CDKコマンド実行時にCloud9を動かしているAmazonLinuxにインストールされているnode.jsのバージョンが古いと怒られていますが、ここは気にせず進めていきます。

CDKプロジェクトの作成

まずはCDK用の空のディレクトリを作成後に作成したディレクトリに移動します。
この時に作成するディレクトリ名がCDKのプロジェクト名になります。

$ mkdir workcdk && cd workcdk

次に”cdk init --language <CDKで使用できるプログラム言語を指定>” コマンドでプロジェクトを作成します。※ 私はCDKの中でも情報量が多いTypeScriptを使用しています。

$ cdk init --language typescript

実行すると以下の内容が出力され作成したディレクトリにファイル群が自動生成されます。

※自動生成されたファイル群

以下に示すディレクトリとファイルが作成されます。
※ 使用している環境によって異なる場合もあります

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

VPCを作ってみよう                               

VPCをCDKで作成するためにはパッケージ ‘aws-cdk-lib/aws-ec2’モジュールをインポートする必要があります。他にCDKアプリケーションモジュールが必要です。VPCはEC2モジュールの仲間であるためVPCを作成するときは’aws-cdk-lib/aws-ec2’モジュールをインポートする必要があるという理解です( ・∇・)

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

[参考] :
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html

続いてVPCを作成するためにコードを追記していきます。

export class WorkcdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
   
    // ここに作成したいリソースを追加する!
  
    });
  }
}

コード追加後

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

  // VPCの作成
    const vpc = new ec2.Vpc(this, 'VPC', {
      ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
    });
  }
}

はい、CDKだとこれだけのコードでVPCを作成することができちゃいます!試しにデプロイ、っとその前にコードを保存し”cdk diff”コマンドで差分を比較しましょう(デプロイ前に必ずcdk diffコマンドで確認するようにした方がいいです)。

$ cdk diff

  │ Resource                                                │ Effect │ Action                                                  │ Principal                                               │ Condition │
├───┼─────────────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┼───────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Ro │ Allow  │ sts:AssumeRole                                          │ Service:lambda.amazonaws.com                            │           │
│   │ le.Arn}                                                 │        │                                                         │                                                         │           │
├───┼─────────────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┼───────────┤
│ + │ arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::Account │ Allow  │ ec2:AuthorizeSecurityGroupEgress                        │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvide │           │
│   │ Id}:security-group/${VPCB9E5F0B4.DefaultSecurityGroup}  │        │ ec2:AuthorizeSecurityGroupIngress                       │ r/Role}                                                 │           │
│   │                                                         │        │ ec2:RevokeSecurityGroupEgress                           │                                                         │           │
│   │                                                         │        │ ec2:RevokeSecurityGroupIngress                          │                                                         │           │
└───┴─────────────────────────────────────────────────────────┴────────┴─────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                                                   │ Managed Policy ARN                                                                           │
├───┼────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ {"Fn::Sub":"arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"} │
└───┴────────────────────────────────────────────────────────────┴────────────────────

Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}

Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}

Resources
[+] AWS::EC2::VPC VPC VPCB9E5F0B4 
[+] AWS::EC2::Subnet VPC/PublicSubnet1/Subnet VPCPublicSubnet1SubnetB4246D30 
[+] AWS::EC2::RouteTable VPC/PublicSubnet1/RouteTable VPCPublicSubnet1RouteTableFEE4B781 
[+] AWS::EC2::SubnetRouteTableAssociation VPC/PublicSubnet1/RouteTableAssociation VPCPublicSubnet1RouteTableAssociation0B0896DC 
[+] AWS::EC2::Route VPC/PublicSubnet1/DefaultRoute VPCPublicSubnet1DefaultRoute91CEF279 
[+] AWS::EC2::EIP VPC/PublicSubnet1/EIP VPCPublicSubnet1EIP6AD938E8 
[+] AWS::EC2::NatGateway VPC/PublicSubnet1/NATGateway VPCPublicSubnet1NATGatewayE0556630 
[+] AWS::EC2::Subnet VPC/PublicSubnet2/Subnet VPCPublicSubnet2Subnet74179F39 
[+] AWS::EC2::RouteTable VPC/PublicSubnet2/RouteTable VPCPublicSubnet2RouteTable6F1A15F1 
[+] AWS::EC2::SubnetRouteTableAssociation VPC/PublicSubnet2/RouteTableAssociation VPCPublicSubnet2RouteTableAssociation5A808732 
[+] AWS::EC2::Route VPC/PublicSubnet2/DefaultRoute VPCPublicSubnet2DefaultRouteB7481BBA 
[+] AWS::EC2::EIP VPC/PublicSubnet2/EIP VPCPublicSubnet2EIP4947BC00 
[+] AWS::EC2::NatGateway VPC/PublicSubnet2/NATGateway VPCPublicSubnet2NATGateway3C070193 
[+] AWS::EC2::Subnet VPC/PrivateSubnet1/Subnet VPCPrivateSubnet1Subnet8BCA10E0 
[+] AWS::EC2::RouteTable VPC/PrivateSubnet1/RouteTable VPCPrivateSubnet1RouteTableBE8A6027 
[+] AWS::EC2::SubnetRouteTableAssociation VPC/PrivateSubnet1/RouteTableAssociation VPCPrivateSubnet1RouteTableAssociation347902D1 
[+] AWS::EC2::Route VPC/PrivateSubnet1/DefaultRoute VPCPrivateSubnet1DefaultRouteAE1D6490 
[+] AWS::EC2::Subnet VPC/PrivateSubnet2/Subnet VPCPrivateSubnet2SubnetCFCDAA7A 
[+] AWS::EC2::RouteTable VPC/PrivateSubnet2/RouteTable VPCPrivateSubnet2RouteTable0A19E10E 
[+] AWS::EC2::SubnetRouteTableAssociation VPC/PrivateSubnet2/RouteTableAssociation VPCPrivateSubnet2RouteTableAssociation0C73D413 
[+] AWS::EC2::Route VPC/PrivateSubnet2/DefaultRoute VPCPrivateSubnet2DefaultRouteF4F5CFD2 
[+] AWS::EC2::InternetGateway VPC/IGW VPCIGWB7E252D3 
[+] AWS::EC2::VPCGatewayAttachment VPC/VPCGW VPCVPCGW99B986DC 
[+] Custom::VpcRestrictDefaultSG VPC/RestrictDefaultSecurityGroupCustomResource VPCRestrictDefaultSecurityGroupCustomResource59474679 
[+] AWS::IAM::Role Custom::VpcRestrictDefaultSGCustomResourceProvider/Role CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0 
[+] AWS::Lambda::Function Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E 

Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}


  Number of stacks with differences: 1

ちょっと長いですが、先ほど書いたコードに対して”cdk diff”コマンドで確認すると上記に示されているインフラリソースが構築されるそうです。想定だとVPCのみ作られるはずが、めっちゃ多い?w

“cdk synth”コマンドを使ってCloudFormationテンプレートを生成してみました・:*+.\(( °ω° ))/.:+

Resources:
  VPCB9E5F0B4:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/Resource
  VPCPublicSubnet1SubnetB4246D30:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      CidrBlock: 10.0.0.0/18
      MapPublicIpOnLaunch: true
      Tags:
        - Key: aws-cdk:subnet-name
          Value: Public
        - Key: aws-cdk:subnet-type
          Value: Public
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet1
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet1/Subnet
  VPCPublicSubnet1RouteTableFEE4B781:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet1
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet1/RouteTable
  VPCPublicSubnet1RouteTableAssociation0B0896DC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VPCPublicSubnet1RouteTableFEE4B781
      SubnetId:
        Ref: VPCPublicSubnet1SubnetB4246D30
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet1/RouteTableAssociation
  VPCPublicSubnet1DefaultRoute91CEF279:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: VPCIGWB7E252D3
      RouteTableId:
        Ref: VPCPublicSubnet1RouteTableFEE4B781
    DependsOn:
      - VPCVPCGW99B986DC
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet1/DefaultRoute
  VPCPublicSubnet1EIP6AD938E8:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet1
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet1/EIP
  VPCPublicSubnet1NATGatewayE0556630:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - VPCPublicSubnet1EIP6AD938E8
          - AllocationId
      SubnetId:
        Ref: VPCPublicSubnet1SubnetB4246D30
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet1
    DependsOn:
      - VPCPublicSubnet1DefaultRoute91CEF279
      - VPCPublicSubnet1RouteTableAssociation0B0896DC
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet1/NATGateway
  VPCPublicSubnet2Subnet74179F39:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      CidrBlock: 10.0.64.0/18
      MapPublicIpOnLaunch: true
      Tags:
        - Key: aws-cdk:subnet-name
          Value: Public
        - Key: aws-cdk:subnet-type
          Value: Public
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet2
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet2/Subnet
  VPCPublicSubnet2RouteTable6F1A15F1:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet2
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet2/RouteTable
  VPCPublicSubnet2RouteTableAssociation5A808732:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VPCPublicSubnet2RouteTable6F1A15F1
      SubnetId:
        Ref: VPCPublicSubnet2Subnet74179F39
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet2/RouteTableAssociation
  VPCPublicSubnet2DefaultRouteB7481BBA:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: VPCIGWB7E252D3
      RouteTableId:
        Ref: VPCPublicSubnet2RouteTable6F1A15F1
    DependsOn:
      - VPCVPCGW99B986DC
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet2/DefaultRoute
  VPCPublicSubnet2EIP4947BC00:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet2
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet2/EIP
  VPCPublicSubnet2NATGateway3C070193:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId:
        Fn::GetAtt:
          - VPCPublicSubnet2EIP4947BC00
          - AllocationId
      SubnetId:
        Ref: VPCPublicSubnet2Subnet74179F39
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PublicSubnet2
    DependsOn:
      - VPCPublicSubnet2DefaultRouteB7481BBA
      - VPCPublicSubnet2RouteTableAssociation5A808732
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PublicSubnet2/NATGateway
  VPCPrivateSubnet1Subnet8BCA10E0:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      CidrBlock: 10.0.128.0/18
      MapPublicIpOnLaunch: false
      Tags:
        - Key: aws-cdk:subnet-name
          Value: Private
        - Key: aws-cdk:subnet-type
          Value: Private
        - Key: Name
          Value: WorkcdkStack/VPC/PrivateSubnet1
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet1/Subnet
  VPCPrivateSubnet1RouteTableBE8A6027:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PrivateSubnet1
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet1/RouteTable
  VPCPrivateSubnet1RouteTableAssociation347902D1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VPCPrivateSubnet1RouteTableBE8A6027
      SubnetId:
        Ref: VPCPrivateSubnet1Subnet8BCA10E0
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet1/RouteTableAssociation
  VPCPrivateSubnet1DefaultRouteAE1D6490:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: VPCPublicSubnet1NATGatewayE0556630
      RouteTableId:
        Ref: VPCPrivateSubnet1RouteTableBE8A6027
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet1/DefaultRoute
  VPCPrivateSubnet2SubnetCFCDAA7A:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      CidrBlock: 10.0.192.0/18
      MapPublicIpOnLaunch: false
      Tags:
        - Key: aws-cdk:subnet-name
          Value: Private
        - Key: aws-cdk:subnet-type
          Value: Private
        - Key: Name
          Value: WorkcdkStack/VPC/PrivateSubnet2
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet2/Subnet
  VPCPrivateSubnet2RouteTable0A19E10E:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC/PrivateSubnet2
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet2/RouteTable
  VPCPrivateSubnet2RouteTableAssociation0C73D413:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VPCPrivateSubnet2RouteTable0A19E10E
      SubnetId:
        Ref: VPCPrivateSubnet2SubnetCFCDAA7A
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet2/RouteTableAssociation
  VPCPrivateSubnet2DefaultRouteF4F5CFD2:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: VPCPublicSubnet2NATGateway3C070193
      RouteTableId:
        Ref: VPCPrivateSubnet2RouteTable0A19E10E
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/PrivateSubnet2/DefaultRoute
  VPCIGWB7E252D3:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: WorkcdkStack/VPC
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/IGW
  VPCVPCGW99B986DC:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId:
        Ref: VPCIGWB7E252D3
      VpcId:
        Ref: VPCB9E5F0B4
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/VPCGW
  VPCRestrictDefaultSecurityGroupCustomResource59474679:
    Type: Custom::VpcRestrictDefaultSG
    Properties:
      ServiceToken:
        Fn::GetAtt:
          - CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E
          - Arn
      DefaultSecurityGroupId:
        Fn::GetAtt:
          - VPCB9E5F0B4
          - DefaultSecurityGroup
      Account:
        Ref: AWS::AccountId
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      aws:cdk:path: WorkcdkStack/VPC/RestrictDefaultSecurityGroupCustomResource/Default
  CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
      ManagedPolicyArns:
        - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: Inline
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:AuthorizeSecurityGroupIngress
                  - ec2:AuthorizeSecurityGroupEgress
                  - ec2:RevokeSecurityGroupIngress
                  - ec2:RevokeSecurityGroupEgress
                Resource:
                  - Fn::Join:
                      - ""
                      - - "arn:"
                        - Ref: AWS::Partition
                        - ":ec2:"
                        - Ref: AWS::Region
                        - ":"
                        - Ref: AWS::AccountId
                        - :security-group/
                        - Fn::GetAtt:
                            - VPCB9E5F0B4
                            - DefaultSecurityGroup
    Metadata:
      aws:cdk:path: WorkcdkStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Role
  CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket:
          Fn::Sub: cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}
        S3Key: 4b996a3e5a083d5c78c6f30a8571a94fb7ec557eecbe54dbc065faba0d9076e6.zip
      Timeout: 900
      MemorySize: 128
      Handler: __entrypoint__.handler
      Role:
        Fn::GetAtt:
          - CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0
          - Arn
      Runtime: nodejs18.x
      Description: Lambda function for removing all inbound/outbound rules from the VPC default security group
    DependsOn:
      - CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0
    Metadata:
      aws:cdk:path: WorkcdkStack/Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler
      aws:asset:path: asset.4b996a3e5a083d5c78c6f30a8571a94fb7ec557eecbe54dbc065faba0d9076e6
      aws:asset:property: Code

長い Σ੧(❛□❛✿)wwww なんだか細かく指定しなくてもサブネットとかインターネットゲートウェイとかも一緒にくっついてくるコンストラクトレベル2のVpcを指定しており、AWSのベストプラクティスに基づいて色々くっついて作成してくれるみたいです。とりあえず細かくは指定しないでざっくりとしたインフラが欲しいときなどのスピードが求められる場合はコンスラクトレベル2は超便利だと思います。細かく指定した場合はコンストラクトレベル3の”Cfn”を先頭にしてVPCなどのリソースを指定する必要があるみたいです。

まあ・・・とりあえずデプロイしてみようと思います_:(´ཀ`」 ∠):

※ AWS CDKを始めてAWSアカウント/リージョンにデプロイする時は “cdk bootstrap”コマンドを実行してください。これはAWS CDKを環境にデプロイするために必要なリソースが含まれたスタックを作成するために必要です。

[参考]: https://catalog.workshops.aws/typescript-and-cdk-for-beginner/ja-JP/40-cdk-introduction/10-create-project/40-cdk-deploy

$ cdk deploy

cdk deployを実行すると以下の追加されるリソースが出力されDeployしてもいいか聞かれるので良かったらYESのy、嫌だったらNoのnを入力します。今回はyで進めます!
※ CDKは直接APIを叩いてるTerraformとは違いTypescriptからコンパイルしてCloudFormationに変換する処理が伴うためデプロイ速度は遅め。

デプロイが完了しました!
AWSマネージメントコンソールからCloudFormationを確認していきます。

このデプロイにより VPC だけじゃなく、サブネット 4 つ、ルートテーブル 4 つ、インターネットゲートウェイ 1 つ、Elastic IP 2 つ、NAT ゲートウェイ “2 つ”も”作成されていました。そんなに頼んでないんだけど今回はコンストラクトの指定に”Vpc”を指定していたため、コンストラクトレベル2ということでAWSベストプラクティスに沿ってよしなに構築してくれたみたいです。

Construct(コンストラクト)とは

AWS CDKコンストラクトとはアプリの基本的な構成要素とのことでVPCやAPIGatewayなどそれぞれがConstruceという単位で提供されています。

例えば、s3.Bucketクラス(Construct)はAmazonS3バケットを、VPCクラス(Construct)はVPCを表します。

また、コンストラクトにはL1 〜 L3と3つの異なるレベルのコンストラクトがあります。

L1 Construct

最下層のクラスでコンストラクトレベル1で構築する場合はVPCやS3などのクラスの前に”Cfn”のプレフィックスを付けてあげて全てのリソースプロパティを明示的に宣言していく必要があります。

L2 Construct

コンストラクトL2になるとL1に比べると抽象的にAWSリソースを指定して作成することができます。
開発者などAWSインフラリソースに知見がない人でもスピーディーにAWSベストプラクティスに沿ったインフラストラクチャ構築を可能とします。

L3 Construct

コンストラクトL3になるとパターンと呼ばれる一般的な構成を事前に定義されているコンストラクトになります。例えば、LambdaRestApiコンストラクトはAWS Lambda関数とAmazon API Gatewayをスピーディに展開する時に使用します。

[参考] : https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/constructs.html

deployしたリソースの削除                           

最後に本記事で作成したリソースを削除します。今回はコンストラクトL2のVPCを使ってVPCを構築しているため、サブネット 4 つ、ルートテーブル 4 つ、インターネットゲートウェイ 1 つ、Elastic IP 2 つ、NAT ゲートウェイ “2 つ”も作成されています。特にNATゲートウェイは置いておくだけで月3000円程度かかってくるお高いサービスですので消します_:(´ཀ`」 ∠):

作成したCloudFormationスタックを削除するためはターミナルから”cdk destroy”コマンドを実行します。削除していいか” y/n “で聞かれますので迷わず ” y “で実行します。

$ cdk destroy

Are you sure you want to delete: WorkcdkStack (y/n)? y
WorkcdkStack: destroying... [1/1]
current credentials could not be used to assume 'arn:aws:iam::355126125825:role/cdk-hnb659fds-deploy-role-355126125825-ap-northeast-1', but are for the right account. Proceeding anyway.

   WorkcdkStack: destroyed

これで綺麗に掃除できたはずです。念の為、AWSマネージメントコンソールを確認しましたがデプロイ直後に生成されていたスタックがCloudFormationから削除されていました。もちろんVPCや付随して作成されていたリソースも削除されていました(便利)。

終わりに                             

ここまでご覧いただきありがとうございました。 実は私はプログラムコードについては趣味で書く程度であまりプロではないですがAWS CDKを触ることで少しだけお近づきになれた気がしました。特にCloudFormationを書いてる時と比べると”書いてる”よりも”作ってる感覚”があって楽しいと思いました。

このシリーズではAWS CDKについて私自身がインプットしたことをアウトプットする場として活用しながら少しでもAWS CDKの魅力についてお伝えできたらと思います。

【環境】

 ・ AWS Cloud9

・プログラム言語 : TypeScript

投稿者 izumi kikumura

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です