はじめに
CloudFormationではスタックを分割して管理することが多い印象でした。
一方で、CDKについて調べていくと、Constructで機能ごとに構造化し、Stackはデプロイ単位として扱う設計が紹介されていることが多くありました。
今回はaws-cdk init app --language typescriptで作成したプロジェクトをコンストラクトで構造化してみようと思います。
やってみる
前提
プロジェクトの初期化やbootstrapは完了している状態で開始します。
実施手順は以下の記事を参照いただければと思います。
初期生成されるスタックファイルは以下のような内容です。
サンプルでは、スタックの中に直接リソースを定義するような記載となっているため、別途コンストラクトを定義したファイルを作成して呼び出す形に変更します。
import * as cdk from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';
export class CdkServerlessSiteStack 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, 'CdkServerlessSiteQueue', {
// visibilityTimeout: cdk.Duration.seconds(300)
// });
}
}
コンストラクトファイルの作成
今回は、ネットワーク関連のリソース(VPCなど)を定義するnetwork.tsと、アプリケーション関連のリソース(EC2など)を定義するapp.tsを作成します。
network.ts
network.tsではVPCを作成するコンストラクトを定義します。
NetworkPropsインターフェースを定義することで、VPCのCIDRを外部から受け取れるようにしています。
また、作成したVPCはpublic readonly vpcとして公開しているため、他のスタックやコンストラクトから参照可能です。
import { Construct } from "constructs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
export interface NetworkProps {
vpcCidr: string;
}
export class Network extends Construct {
public readonly vpc: ec2.Vpc;
constructor(scope: Construct, id: string, props: NetworkProps) {
super(scope, id);
this.vpc = new ec2.Vpc(this, "VPC", {
ipAddresses: ec2.IpAddresses.cidr(props.vpcCidr),
maxAzs: 2,
natGateways: 0,
});
}
}
app.ts
app.tsではEC2インスタンスを作成するコンストラクトを定義します。
AppPropsインターフェースではec2.IVpcを受け取るようにしており、network.tsで作成したVPCを利用することができます。
今回はパブリックサブネットにAL2023のEC2インスタンスを作成し、Apacheを起動するように設定しています。
import { Construct } from "constructs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
export interface AppProps {
vpc: ec2.IVpc;
}
export class App extends Construct {
constructor(scope: Construct, id: string, props: AppProps) {
super(scope, id);
const instance = new ec2.Instance(this, "Instance", {
vpc: props.vpc,
instanceType: ec2.InstanceType.of(
ec2.InstanceClass.T3,
ec2.InstanceSize.MICRO,
),
machineImage: ec2.MachineImage.latestAmazonLinux2023({
cachedInContext: true,
}),
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
ssmSessionPermissions: true,
});
instance.connections.allowFromAnyIpv4(ec2.Port.HTTP);
instance.userData.addCommands(
"dnf install -y httpd",
"systemctl enable httpd",
"systemctl start httpd",
);
}
}
スタックファイルの修正
次にスタックファイルを編集していきます。
作成した各コンストラクトをインポートし、スタック内で利用しています。
Networkで作成したVPCはnetwork.vpcとして取得し、Appへ渡しています。
また、vpcCidrの値はエントリポイントとなるファイルで指定できるようにしています。
import * as cdk from "aws-cdk-lib/core";
import { Construct } from "constructs";
import { App } from "./constructs/app";
import { Network } from "./constructs/network";
export interface CdkServerlessSiteStackProps extends cdk.StackProps {
vpcCidr: string;
}
export class CdkServerlessSiteStack extends cdk.Stack {
constructor(
scope: Construct,
id: string,
props: CdkServerlessSiteStackProps,
) {
super(scope, id, props);
const network = new Network(this, "Network", {
vpcCidr: props.vpcCidr,
});
new App(this, "App", {
vpc: network.vpc,
});
}
}
エントリーポイントファイルの修正
最後にエントリーポイントとなるbin/projectname.tsを編集します。
スタック作成時のアカウント/リージョンを示すenvや各リソースに付与するタグ、VPCのCIDRを指定します。
vpcCidrはスタックのプロパティとして渡され、Networkコンストラクトで利用されます。
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib/core";
import { CdkServerlessSiteStack } from "../lib/cdk-serverless-site-stack";
const app = new cdk.App();
new CdkServerlessSiteStack(app, "CdkServerlessSiteStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
vpcCidr: "10.0.0.0/16",
});
cdk.Tags.of(app).add("Env", "Education");
以上でコードの修正は完了です。
動作確認
実際に問題なくデプロイできるか確認してみます。
cdk deployコマンドでスタックをデプロイしていきます。
$ npx cdk deploy
✨ Synthesis time: 4.17s
CdkServerlessSiteStack: creating CloudFormation changeset...
.... # 省略
CdkServerlessSiteStack: deploying... [1/1]
✅ CdkServerlessSiteStack
✨ Deployment time: 173.31s
Stack ARN:
arn:aws:cloudformation:us-east-1:123456789123:stack/CdkServerlessSiteStack/2b11b0b0-6dd4-11f1-90b9-0ed7e84b531b
✨ Total time: 194.87s
特に問題なくデプロイできました。
AWSコンソール上でもCloudFormationの画面からリソースが作成できていることが確認できます。
コンストラクトごとにグループ分けされているのがわかりやすいです。

動作確認ができたら、作成したリソースは削除しておきます。
$ npx cdk destroy
Are you sure you want to delete: CdkServerlessSiteStack (y/n) y
CdkServerlessSiteStack: destroying... [1/1]
✅ CdkServerlessSiteStack: destroyed
最後に
今回はCDKで初期作成されるコードをコンストラクトで構造化してみました。
リソース数が増えるにつれてコード量も多くなるため、コンストラクトで構造化することで、可読性向上に繋がるなと感じました。
参考
