はじめに
https://dshimizu.hatenablog.com/entry/2024/01/21/000000 で AWS CDK を触ってみました。 しばらく時間が空いてしまったので、復習もかねつつ、今回は AWS CDK in TypeScript で VPC の作成をやってみました。
環境
Debian 12 の環境を利用します。
% uname -svor Linux 6.1.0-12-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.52-1 (2023-09-07) GNU/Linux
Node.js は 18.14.0 を使ってます。
% node -v v18.14.0
npx は 9.3.1 を使ってます。
% npx -v 9.3.1
TypeScript は 4.9.5 を使ってます。
% npx tsc -v Version 4.9.5
% npx cdk --version 2.212.0 (build 9f2b78c )
AWS CLI の設定では、 sandbox
という名前の認証プロファイルを作成して、このプロファイルを利用することとします。
~/aws/credentials
[sandbox] aws_access_key_id=******************** aws_secret_access_key=****************************************
~/aws/config
[default] region = ap-northeast-1 output = json [profile sandbox] region = ap-northeast-1 output = json source_profile = default role_arn = arn:aws:iam::**********:role/AdminRole
AWS CDKアプリケーションを作る
AWS CDKアプリケーションを作ってみます。
AWS CDKアプリケーションのプロジェクトの作成
AWS CDKアプリケーションのプロジェクトを作成してみます。
プロジェクト用のディレクトリを作成して、移動します。ここではテスト用に cdk-sandbox-vpc
というディレクトリにしています。
% mkdir cdk-sandbox-vpc && cd cdk-sandbox-vpc
cdk init
では app
, lib
, sample-app
の 3 つのテンプレートが選択できるようです。通常のプロジェクトとして開始する場合は app
を利用するようです。省略した場合は app
がデフォルトになるようです。
lib
は CDK ライブラリを作成する場合に利用し、 sample-app
はその名の通りでサンプルアプリケーションが動くCDKコードが生成されるようです。
% npx cdk init Available templates: * app: Template for a CDK Application └─ cdk init app --language=[csharp|fsharp|go|java|javascript|python|typescript] * lib: Template for a CDK Construct Library └─ cdk init lib --language=typescript * sample-app: Example CDK Application with some constructs └─ cdk init sample-app --language=[csharp|fsharp|go|java|javascript|python|typescript]
言語は Typescript にします。
% cdk init --language typescript Applying project template app for typescript # Welcome to your CDK TypeScript project This is a blank project for CDK development with TypeScript. The `cdk.json` file tells the CDK Toolkit how to execute your app. ## Useful commands * `npm run build` compile typescript to js * `npm run watch` watch for changes and compile * `npm run test` perform the jest unit tests * `npx cdk deploy` deploy this stack to your default AWS account/region * `npx cdk diff` compare deployed stack with current state * `npx cdk synth` emits the synthesized CloudFormation template Executing npm install... ✅ All done!
以下のようなファイルが生成されます。
% tree -L 2 . ├── README.md ├── bin │ └── cdk-sandbox-vpc.ts ├── cdk.json ├── jest.config.js ├── lib │ └── cdk-sandbox-stack-vpc.ts ├── node_modules │ ├── @ampproject : │ ├── yn │ └── yocto-queue ├── package-lock.json ├── package.json ├── test │ └── cdk-sandbox-vpc.test.ts └── tsconfig.json 221 directories, 9 files
bin/cdk-sandbox-vpc.ts
というファイルが生成されており、これが CDK アプリケーションのエントリーポイント(アプリケーションの中で一番最初に呼び出される部分)となります。
../lib/cdk-sandbox-vpc-stack
から CdkSandboxVpcStack
というクラスを読み込んで、インスタンスを生成しているようです。
#!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { CdkSandboxVpcStack } from '../lib/cdk-sandbox-vpc-stack'; const app = new cdk.App(); new CdkSandboxVpcStack(app, 'CdkSandboxVpcStack', { /* If you don't specify 'env', this stack will be environment-agnostic. * Account/Region-dependent features and context lookups will not work, * but a single synthesized template can be deployed anywhere. */ /* Uncomment the next line to specialize this stack for the AWS Account * and Region that are implied by the current CLI configuration. */ // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, /* Uncomment the next line if you know exactly what Account and Region you * want to deploy the stack to. */ // env: { account: '123456789012', region: 'us-east-1' }, /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ });
bin/cdk-sandbox-vpc.ts
で読み込まれている lib/cdk-sandbox-vpc-stack.ts
というファイルが生成されているので見てみます。
SQS を作成するための定義のサンプルがコメントアウト状態であるだけでした。
lib/
配下にリソースを作成・管理するファイルを生成していくのがベースとなるように思いました。
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; // import * as sqs from 'aws-cdk-lib/aws-sqs'; export class CdkSandboxVpcStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // The code that defines your stack goes here // example resource // const queue = new sqs.Queue(this, 'CdkSandboxVpcQueue', { // visibilityTimeout: cdk.Duration.seconds(300) // }); } }
AWS CDK で VPC 作成
AWS CDK で VPC を作成してみます。以下のワークショップの内容をベースに作成してみます。
ちなみにAWS CDK の VPC 関連のドキュメントは下記のようです。
- L1コンストラクト: class CfnVPC (construct) · AWS CDK
- L2コンストラクト: class Vpc (construct) · AWS CDK
lib/cdk-sandbox-vpc-stack.ts
を以下のように変更して VPC を作成してみます。
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; export class CdkSandboxVpcStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // L2 コンストラクト const vpc = new ec2.Vpc(this, 'SandboxVPC', { ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), }) } }
この状態でデプロイしてみます。
% npx cdk diff --profile sandbox Stack CdkSandboxVpcStack Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff) IAM Statement Changes ┌───┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────┬───────────┐ │ │ Resource │ Effect │ Action │ Principal │ Condition │ ├───┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤ │ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role.Arn} │ Allow │ sts:AssumeRole │ Service:lambda.amazonaws.com │ │ ├───┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤ │ + │ arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${SandboxVPC4161466E.DefaultSecurityGroup} │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │ │ │ │ │ ec2:AuthorizeSecurityGroupIngress │ │ │ │ │ │ │ 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"} │ └───┴────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘ (NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299) 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"},"il-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]} Resources [+] AWS::EC2::VPC SandboxVPC SandboxVPC4161466E [+] AWS::EC2::Subnet SandboxVPC/PublicSubnet1/Subnet SandboxVPCPublicSubnet1SubnetB9280C70 [+] AWS::EC2::RouteTable SandboxVPC/PublicSubnet1/RouteTable SandboxVPCPublicSubnet1RouteTable35C17C45 [+] AWS::EC2::SubnetRouteTableAssociation SandboxVPC/PublicSubnet1/RouteTableAssociation SandboxVPCPublicSubnet1RouteTableAssociationD8A992A4 [+] AWS::EC2::Route SandboxVPC/PublicSubnet1/DefaultRoute SandboxVPCPublicSubnet1DefaultRouteCD80277D [+] AWS::EC2::EIP SandboxVPC/PublicSubnet1/EIP SandboxVPCPublicSubnet1EIPBE92916E [+] AWS::EC2::NatGateway SandboxVPC/PublicSubnet1/NATGateway SandboxVPCPublicSubnet1NATGatewayD0904435 [+] AWS::EC2::Subnet SandboxVPC/PublicSubnet2/Subnet SandboxVPCPublicSubnet2SubnetC4239C5C [+] AWS::EC2::RouteTable SandboxVPC/PublicSubnet2/RouteTable SandboxVPCPublicSubnet2RouteTable238E5110 [+] AWS::EC2::SubnetRouteTableAssociation SandboxVPC/PublicSubnet2/RouteTableAssociation SandboxVPCPublicSubnet2RouteTableAssociation3B41EDF2 [+] AWS::EC2::Route SandboxVPC/PublicSubnet2/DefaultRoute SandboxVPCPublicSubnet2DefaultRoute4EAE4B03 [+] AWS::EC2::EIP SandboxVPC/PublicSubnet2/EIP SandboxVPCPublicSubnet2EIPC06FF94C [+] AWS::EC2::NatGateway SandboxVPC/PublicSubnet2/NATGateway SandboxVPCPublicSubnet2NATGateway1AF48573 [+] AWS::EC2::Subnet SandboxVPC/PrivateSubnet1/Subnet SandboxVPCPrivateSubnet1Subnet7AF1D8EE [+] AWS::EC2::RouteTable SandboxVPC/PrivateSubnet1/RouteTable SandboxVPCPrivateSubnet1RouteTableC6704B6B [+] AWS::EC2::SubnetRouteTableAssociation SandboxVPC/PrivateSubnet1/RouteTableAssociation SandboxVPCPrivateSubnet1RouteTableAssociationA661342E [+] AWS::EC2::Route SandboxVPC/PrivateSubnet1/DefaultRoute SandboxVPCPrivateSubnet1DefaultRouteCE236D1A [+] AWS::EC2::Subnet SandboxVPC/PrivateSubnet2/Subnet SandboxVPCPrivateSubnet2Subnet993EE236 [+] AWS::EC2::RouteTable SandboxVPC/PrivateSubnet2/RouteTable SandboxVPCPrivateSubnet2RouteTableAA1701A5 [+] AWS::EC2::SubnetRouteTableAssociation SandboxVPC/PrivateSubnet2/RouteTableAssociation SandboxVPCPrivateSubnet2RouteTableAssociationE49D912F [+] AWS::EC2::Route SandboxVPC/PrivateSubnet2/DefaultRoute SandboxVPCPrivateSubnet2DefaultRoute1CA1A3C8 [+] AWS::EC2::InternetGateway SandboxVPC/IGW SandboxVPCIGW80B9AB55 [+] AWS::EC2::VPCGatewayAttachment SandboxVPC/VPCGW SandboxVPCVPCGWC3325084 [+] Custom::VpcRestrictDefaultSG SandboxVPC/RestrictDefaultSecurityGroupCustomResource SandboxVPCRestrictDefaultSecurityGroupCustomResourceE934C5D3 [+] 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
CdkSandboxVpcStack
という名前のCloudFormationスタックが作成され、Resources
の出力にある通り、これだけでVPC, プライベートサブネット * 2、パブリックサブネット * 2, NAT Gateway * 2 ... と必要そうなリソースが作成されました。
指定してないパラメーターはデフォルト値が定められているようで、この点が L1 コンストラクトを使う場合と比べてより抽象化されており、コードの記述量も減っていると思います。
ただ NAT Gateway とかはいらないのでもう少しカスタマイズします。
削除
一旦全てのリソースを削除します。CdkSandboxVpcStack
という名前のCloudFormationスタックが削除されます。
% npx cdk destroy --profile sandbox Are you sure you want to delete: CdkSandboxVpcStack (y/n)? y CdkSandboxVpcStack: destroying... [1/1] ✅ CdkSandboxVpcStack: destroyed
VPC 再作成
lib/cdk-sandbox-vpc-stack.ts
を以下のように変更します。
NAT Gateway は無しにして、AZは3つ、IPv6 を有効にします。
AWS CDK v2.121.0 で dual stack がサポートされ、 ipProtocol: ec2.IpProtocol.DUAL_STACK,
と記述するだけで有効化できるようです。
import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; export class CdkSandboxVpcStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc = new ec2.Vpc(this, 'SandboxVPC', { ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), ipProtocol: ec2.IpProtocol.DUAL_STACK, //ipv6Addresses: ec2.Ipv6Addresses.amazonProvided(), natGateways: 0, maxAzs: 3, subnetConfiguration: [ { name: 'PublicSubnet', cidrMask: 24, subnetType: ec2.SubnetType.PUBLIC, }, { name: 'PrivateSubnet', cidrMask: 24, subnetType: ec2.SubnetType.PRIVATE_ISOLATED, }, ], enableDnsHostnames: true, enableDnsSupport: true }); } }
再度デプロイしてみます。
% npx cdk deploy --profile sandbox ✨ Synthesis time: 7.79s 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 │ ├───┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤ │ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role.Arn} │ Allow │ sts:AssumeRole │ Service:lambda.amazonaws.com │ │ ├───┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────┼───────────┤ │ + │ arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:security-group/${SandboxVPC4161466E.DefaultSecurityGroup} │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │ │ │ │ │ ec2:AuthorizeSecurityGroupIngress │ │ │ │ │ │ │ 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"} │ └───┴────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘ (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 CdkSandboxVpcStack: deploying... [1/1] CdkSandboxVpcStack: creating CloudFormation changeset... ✅ CdkSandboxVpcStack ✨ Deployment time: 77.07s Stack ARN: arn:aws:cloudformation:ap-northeast-1:************:stack/CdkSandboxVpcStack/36198690-d23b-11ee-af56-0a4a4aa379bb ✨ Total time: 84.86s
再度、CdkSandboxVpcStack
という名前のCloudFormationスタックが作成されました。
合わせて、IPv6 が有効な Public Subnet, Private Subnet を3つずつ含む VPC リソースを作成できました。