Skip to content

Commit bacc625

Browse files
authored
feat: deploying routetable to nested stack and migrate to single route table per subnets isMigration (#18)
1 parent fcc9eb0 commit bacc625

File tree

3 files changed

+109
-17
lines changed

3 files changed

+109
-17
lines changed

API.md

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/constructs/network.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
44
import * as targets from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets';
55
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from 'aws-cdk-lib/custom-resources';
66
import { Construct } from 'constructs';
7+
import { RouteTableManager } from './routeTableManager';
78
import { ObjToStrMap } from '../utils/common';
89
export interface NetworkACL {
910
readonly cidr: ec2.AclCidr;
@@ -61,6 +62,7 @@ export interface ISubnetsProps {
6162
readonly routes?: AddRouteOptions[];
6263
readonly tags?: Record<string, string>;
6364
readonly useSubnetForNAT?: boolean;
65+
readonly isMigration?: boolean;
6466
}
6567
export interface VPCProps {
6668
readonly vpc: ec2.VpcProps;
@@ -256,6 +258,14 @@ export class Network extends Construct {
256258
);
257259
}
258260

261+
// Create a single RouteTableManager for the entire subnet group if migration is enabled
262+
const routeTableManager = option.isMigration ? new RouteTableManager(this, `${option.subnetGroupName}RouteTableManager`, {
263+
vpc,
264+
subnetGroupName: option.subnetGroupName,
265+
routes: option.routes,
266+
peeringConnectionId,
267+
}) : undefined;
268+
259269
option.availabilityZones.forEach((az, index) => {
260270
let subnet: ec2.PrivateSubnet | ec2.PublicSubnet =
261271
option.subnetType === ec2.SubnetType.PUBLIC
@@ -267,7 +277,6 @@ export class Network extends Construct {
267277
cidrBlock: option.cidrBlock[index],
268278
vpcId: vpc.vpcId,
269279
mapPublicIpOnLaunch: true,
270-
271280
},
272281
)
273282
: new ec2.PrivateSubnet(
@@ -280,31 +289,35 @@ export class Network extends Construct {
280289
mapPublicIpOnLaunch: false,
281290
},
282291
);
283-
option.routes?.forEach((route, routeIndex) => {
284-
if (peeringConnectionId != undefined && route.existingVpcPeeringRouteKey != undefined) {
285-
let routeId: ec2.CfnVPCPeeringConnection | undefined = peeringConnectionId[route.existingVpcPeeringRouteKey];
286-
if (routeId != undefined) {
292+
if (option.isMigration && routeTableManager) {
293+
routeTableManager.associateSubnet(subnet, index);
294+
} else {
295+
option.routes?.forEach((route, routeIndex) => {
296+
if (peeringConnectionId != undefined && route.existingVpcPeeringRouteKey != undefined) {
297+
let routeId: ec2.CfnVPCPeeringConnection | undefined = peeringConnectionId[route.existingVpcPeeringRouteKey];
298+
if (routeId != undefined) {
299+
subnet.addRoute(
300+
`${option.subnetGroupName}${routeIndex}RouteEntry`,
301+
{
302+
routerId: routeId.ref,
303+
routerType: route.routerType,
304+
destinationCidrBlock: route.destinationCidrBlock,
305+
},
306+
);
307+
}
308+
} else if (route.routerId != undefined) {
287309
subnet.addRoute(
288310
`${option.subnetGroupName}${routeIndex}RouteEntry`,
289311
{
290-
routerId: routeId.ref,
312+
routerId: route.routerId ?? '',
291313
routerType: route.routerType,
292314
destinationCidrBlock: route.destinationCidrBlock,
293315
},
294316
);
295317
}
296-
} else if (route.routerId != undefined) {
297-
subnet.addRoute(
298-
`${option.subnetGroupName}${routeIndex}RouteEntry`,
299-
{
300-
routerId: route.routerId ?? '',
301-
routerType: route.routerType,
302-
destinationCidrBlock: route.destinationCidrBlock,
303-
},
304-
);
305-
}
318+
});
319+
}
306320

307-
});
308321
Tags.of(subnet).add(SUBNETNAME_TAG, option.subnetGroupName);
309322
Tags.of(subnet).add(SUBNETTYPE_TAG, option.subnetType);
310323
if (option.tags != undefined) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { NestedStack, Tags } from 'aws-cdk-lib';
2+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
3+
import { Construct } from 'constructs';
4+
import { AddRouteOptions } from './network';
5+
6+
export interface RouteTableManagerProps {
7+
readonly vpc: ec2.Vpc;
8+
readonly subnetGroupName: string;
9+
readonly routes?: AddRouteOptions[];
10+
readonly peeringConnectionId?: { [key: string]: ec2.CfnVPCPeeringConnection };
11+
}
12+
13+
export class RouteTableManager extends Construct {
14+
public readonly routeTable: ec2.CfnRouteTable;
15+
private readonly nestedStack: NestedStack;
16+
17+
constructor(scope: Construct, id: string, props: RouteTableManagerProps) {
18+
super(scope, id);
19+
20+
// Create a nested stack for route table resources
21+
this.nestedStack = new NestedStack(this, `${props.subnetGroupName}RouteTableStack`, {
22+
description: `Route table stack for ${props.subnetGroupName} subnet group`,
23+
});
24+
25+
// Create a single route table for the subnet group in the nested stack
26+
this.routeTable = new ec2.CfnRouteTable(this.nestedStack, `${props.subnetGroupName}RouteTable`, {
27+
vpcId: props.vpc.vpcId,
28+
});
29+
// Add Name tag
30+
Tags.of(this.routeTable).add('Name', `${props.subnetGroupName}`);
31+
32+
// Add routes to the route table in the nested stack
33+
props.routes?.forEach((route) => {
34+
if (props.peeringConnectionId && route.existingVpcPeeringRouteKey) {
35+
const routeId = props.peeringConnectionId[route.existingVpcPeeringRouteKey];
36+
if (routeId) {
37+
const destinationCidr = route.destinationCidrBlock?.replace(/[./]/g, '-') ?? 'default';
38+
new ec2.CfnRoute(this.nestedStack, `${props.subnetGroupName}-${destinationCidr}-Route`, {
39+
routeTableId: this.routeTable.ref,
40+
destinationCidrBlock: route.destinationCidrBlock,
41+
gatewayId: route.routerType === ec2.RouterType.GATEWAY ? routeId.ref : undefined,
42+
natGatewayId: route.routerType === ec2.RouterType.NAT_GATEWAY ? routeId.ref : undefined,
43+
transitGatewayId: route.routerType === ec2.RouterType.TRANSIT_GATEWAY ? routeId.ref : undefined,
44+
vpcPeeringConnectionId: route.routerType === ec2.RouterType.VPC_PEERING_CONNECTION ? routeId.ref : undefined,
45+
});
46+
}
47+
} else if (route.routerId) {
48+
const destinationCidr = route.destinationCidrBlock?.replace(/[./]/g, '-') ?? 'default';
49+
new ec2.CfnRoute(this.nestedStack, `${props.subnetGroupName}-${destinationCidr}-Route`, {
50+
routeTableId: this.routeTable.ref,
51+
destinationCidrBlock: route.destinationCidrBlock,
52+
gatewayId: route.routerType === ec2.RouterType.GATEWAY ? route.routerId : undefined,
53+
natGatewayId: route.routerType === ec2.RouterType.NAT_GATEWAY ? route.routerId : undefined,
54+
transitGatewayId: route.routerType === ec2.RouterType.TRANSIT_GATEWAY ? route.routerId : undefined,
55+
vpcPeeringConnectionId: route.routerType === ec2.RouterType.VPC_PEERING_CONNECTION ? route.routerId : undefined,
56+
});
57+
}
58+
});
59+
}
60+
61+
public associateSubnet(subnet: ec2.ISubnet, index: number): void {
62+
// Create subnet-route table association in the nested stack
63+
new ec2.CfnSubnetRouteTableAssociation(this.nestedStack, `${subnet.node.id}RouteTableAssociation${index}`, {
64+
subnetId: subnet.subnetId,
65+
routeTableId: this.routeTable.ref,
66+
});
67+
}
68+
}

0 commit comments

Comments
 (0)