From 4a1b701970ea659e78db687a257a4cd3affb46c1 Mon Sep 17 00:00:00 2001 From: Bharat Parmar Date: Tue, 17 Jun 2025 17:19:03 +0530 Subject: [PATCH 1/2] fix: gateway and internet routing --- src/constructs/network.ts | 87 ++++++++++++++++------------- src/constructs/routeTableManager.ts | 24 ++++++++ 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/src/constructs/network.ts b/src/constructs/network.ts index 5d69ea3..69c9d3d 100644 --- a/src/constructs/network.ts +++ b/src/constructs/network.ts @@ -154,10 +154,10 @@ export class Network extends Construct { public readonly securityGroupOutputs: { [key: string]: ec2.SecurityGroup } = {}; // Store Security Group outputs public readonly endpointOutputs: { [key: string]: ec2.InterfaceVpcEndpoint | ec2.GatewayVpcEndpoint } = {}; // Store Endpoint outputs private peeringConnectionIds: PeeringConnectionInternalType = {}; - public readonly natProvider!: ec2.NatProvider; constructor(scope: Construct, id: string, props: VPCProps) { super(scope, id); this.vpc = new ec2.Vpc(this, 'VPC', props.vpc); + if (props.peeringConfigs) { const convertPeeringConfig: Map = ObjToStrMap(props.peeringConfigs); convertPeeringConfig.forEach((createVpcPeering, key) => { @@ -175,8 +175,27 @@ export class Network extends Construct { this.peeringConnectionIds[key] = peeringConnectionIdByKey; }); } + + const internetGateway = new ec2.CfnInternetGateway( + this, + 'InternetGateway', + {}, + ); + new ec2.CfnVPCGatewayAttachment(this, 'VPCGatewayAttachement', { + internetGatewayId: internetGateway.ref, + vpcId: this.vpc.vpcId, + }); + + // Initialize NAT provider after collecting all subnets + const natProvider = props.natEipAllocationIds?.length === this.natSubnets?.length && props.natEipAllocationIds?.length > 0 + ? ec2.NatProvider.gateway({ + eipAllocationIds: props.natEipAllocationIds, + }) : ec2.NatProvider.gateway(); + + + // First pass: collect all subnets props.subnets.forEach((subnetProps) => { - let subnet = this.createSubnet(subnetProps, this.vpc, this.peeringConnectionIds); + let subnet = this.createSubnet(subnetProps, this.vpc); this.subnets[subnetProps.subnetGroupName] = subnet; subnet.forEach((sb) => { if (sb instanceof ec2.PublicSubnet) { @@ -197,40 +216,36 @@ export class Network extends Construct { } }); }); - const internetGateway = new ec2.CfnInternetGateway( - this, - 'InternetGateway', - {}, - ); - const att = new ec2.CfnVPCGatewayAttachment(this, 'VPCGatewayAttachement', { - internetGatewayId: internetGateway.ref, - vpcId: this.vpc.vpcId, - }); - this.pbSubnets.forEach((pb) => { - pb.addDefaultInternetRoute(internetGateway.ref, att); - }); - if (this.natSubnets.length > 0) { - if (props.natEipAllocationIds && this.natSubnets.length != props.natEipAllocationIds?.length) { - // eslint-disable-next-line max-len - throw new Error( - 'natEipAllocationIds and natSubnets length should be equal', - ); - } - - if (props.natEipAllocationIds?.length == this.natSubnets?.length) { - this.natProvider = ec2.NatProvider.gateway({ - eipAllocationIds: props.natEipAllocationIds, - }); - } else { - this.natProvider = ec2.NatProvider.gateway(); - } - this.natProvider.configureNat({ + // Configure NAT after collecting all subnets + if (this.natSubnets.length > 0) { + natProvider.configureNat({ vpc: this.vpc, natSubnets: this.natSubnets, privateSubnets: this.pvSubnets, }); } + + // Second pass: configure routes after NAT is configured + props.subnets.forEach((subnetProps) => { + const routeTableManager = new RouteTableManager(this, `${subnetProps.subnetGroupName}RouteTableManager`, { + vpc: this.vpc, + subnetGroupName: subnetProps.subnetGroupName, + routes: subnetProps.routes, + peeringConnectionId: this.peeringConnectionIds, + subnetType: subnetProps.subnetType, + natProvider: natProvider, + internetGateway: internetGateway + }); + this.subnets[subnetProps.subnetGroupName].forEach((subnet, index) => { + routeTableManager.associateSubnet(subnet, index); + }); + }); + + // this.pbSubnets.forEach((pb) => { + // pb.addDefaultInternetRoute(internetGateway.ref, att); + // }); + new CfnOutput(this, 'VpcId', { value: this.vpc.vpcId }); // Add VPC endpoints if specified in the props if (props?.vpcEndpoints) { @@ -245,7 +260,7 @@ export class Network extends Construct { } } - createSubnet(option: ISubnetsProps, vpc: ec2.Vpc, peeringConnectionId?: PeeringConnectionInternalType) { + createSubnet(option: ISubnetsProps, vpc: ec2.Vpc) { const subnets: ec2.Subnet[] = []; const SUBNETTYPE_TAG = 'aws-cdk:subnet-type'; const SUBNETNAME_TAG = 'aws-cdk:subnet-name'; @@ -257,14 +272,6 @@ export class Network extends Construct { ); } - // Create a single RouteTableManager for the entire subnet group if migration is enabled - const routeTableManager = new RouteTableManager(this, `${option.subnetGroupName}RouteTableManager`, { - vpc, - subnetGroupName: option.subnetGroupName, - routes: option.routes, - peeringConnectionId, - }); - option.availabilityZones.forEach((az, index) => { let subnet: ec2.PrivateSubnet | ec2.PublicSubnet = option.subnetType === ec2.SubnetType.PUBLIC @@ -288,7 +295,6 @@ export class Network extends Construct { mapPublicIpOnLaunch: false, }, ); - routeTableManager.associateSubnet(subnet, index); Tags.of(subnet).add(SUBNETNAME_TAG, option.subnetGroupName); Tags.of(subnet).add(SUBNETTYPE_TAG, option.subnetType); if (option.tags != undefined) { @@ -299,6 +305,7 @@ export class Network extends Construct { } subnets.push(subnet); }); + const nacl = new ec2.NetworkAcl(this, `${option.subnetGroupName}NACL`, { vpc: vpc, subnetSelection: { diff --git a/src/constructs/routeTableManager.ts b/src/constructs/routeTableManager.ts index e7055ff..8655f87 100644 --- a/src/constructs/routeTableManager.ts +++ b/src/constructs/routeTableManager.ts @@ -8,6 +8,9 @@ export interface RouteTableManagerProps { readonly subnetGroupName: string; readonly routes?: AddRouteOptions[]; readonly peeringConnectionId?: { [key: string]: ec2.CfnVPCPeeringConnection }; + readonly subnetType: ec2.SubnetType; + readonly natProvider: ec2.NatProvider; + readonly internetGateway: ec2.CfnInternetGateway } export class RouteTableManager extends Construct { @@ -56,6 +59,27 @@ export class RouteTableManager extends Construct { }); } }); + + // Add default routes based on subnet type + if (props.subnetType === ec2.SubnetType.PUBLIC) { + // Add internet route for public subnets + new ec2.CfnRoute(this.nestedStack, `${props.subnetGroupName}-InternetRoute`, { + routeTableId: this.routeTable.ref, + destinationCidrBlock: '0.0.0.0/0', + gatewayId: props.internetGateway.ref, + }); + } else if (props.subnetType === ec2.SubnetType.PRIVATE_WITH_EGRESS) { + // Add NAT route for private subnets + const natGateway = props.natProvider.configuredGateways[0]; + console.log('natGateway', natGateway); + if (natGateway) { + new ec2.CfnRoute(this.nestedStack, `${props.subnetGroupName}-NatRoute`, { + routeTableId: this.routeTable.ref, + destinationCidrBlock: '0.0.0.0/0', + natGatewayId: natGateway.gatewayId, + }); + } + } } public associateSubnet(subnet: ec2.ISubnet, index: number): void { From 8c262de581d20a989bf6d481e928a78be6a05a40 Mon Sep 17 00:00:00 2001 From: Bharat Parmar Date: Tue, 17 Jun 2025 17:39:48 +0530 Subject: [PATCH 2/2] fix: gateway and internet routing --- API.md | 19 +------------------ src/constructs/network.ts | 12 ++++++------ src/constructs/routeTableManager.ts | 2 +- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/API.md b/API.md index 0b7dadf..070ff12 100644 --- a/API.md +++ b/API.md @@ -58,7 +58,7 @@ Returns a string representation of this construct. ##### `createSubnet` ```typescript -public createSubnet(option: ISubnetsProps, vpc: Vpc, peeringConnectionId?: PeeringConnectionInternalType): Subnet[] +public createSubnet(option: ISubnetsProps, vpc: Vpc): Subnet[] ``` ###### `option`Required @@ -73,12 +73,6 @@ public createSubnet(option: ISubnetsProps, vpc: Vpc, peeringConnectionId?: Peeri --- -###### `peeringConnectionId`Optional - -- *Type:* PeeringConnectionInternalType - ---- - #### Static Functions | **Name** | **Description** | @@ -111,7 +105,6 @@ Any object. | --- | --- | --- | | node | constructs.Node | The tree node. | | endpointOutputs | {[ key: string ]: aws-cdk-lib.aws_ec2.InterfaceVpcEndpoint \| aws-cdk-lib.aws_ec2.GatewayVpcEndpoint} | *No description.* | -| natProvider | aws-cdk-lib.aws_ec2.NatProvider | *No description.* | | securityGroupOutputs | {[ key: string ]: aws-cdk-lib.aws_ec2.SecurityGroup} | *No description.* | | vpc | aws-cdk-lib.aws_ec2.Vpc | *No description.* | | natSubnets | aws-cdk-lib.aws_ec2.PublicSubnet[] | *No description.* | @@ -143,16 +136,6 @@ public readonly endpointOutputs: {[ key: string ]: InterfaceVpcEndpoint | Gatewa --- -##### `natProvider`Required - -```typescript -public readonly natProvider: NatProvider; -``` - -- *Type:* aws-cdk-lib.aws_ec2.NatProvider - ---- - ##### `securityGroupOutputs`Required ```typescript diff --git a/src/constructs/network.ts b/src/constructs/network.ts index 69c9d3d..ca13b1a 100644 --- a/src/constructs/network.ts +++ b/src/constructs/network.ts @@ -157,7 +157,7 @@ export class Network extends Construct { constructor(scope: Construct, id: string, props: VPCProps) { super(scope, id); this.vpc = new ec2.Vpc(this, 'VPC', props.vpc); - + if (props.peeringConfigs) { const convertPeeringConfig: Map = ObjToStrMap(props.peeringConfigs); convertPeeringConfig.forEach((createVpcPeering, key) => { @@ -181,7 +181,7 @@ export class Network extends Construct { 'InternetGateway', {}, ); - new ec2.CfnVPCGatewayAttachment(this, 'VPCGatewayAttachement', { + new ec2.CfnVPCGatewayAttachment(this, 'VPCGatewayAttachement', { internetGatewayId: internetGateway.ref, vpcId: this.vpc.vpcId, }); @@ -189,10 +189,10 @@ export class Network extends Construct { // Initialize NAT provider after collecting all subnets const natProvider = props.natEipAllocationIds?.length === this.natSubnets?.length && props.natEipAllocationIds?.length > 0 ? ec2.NatProvider.gateway({ - eipAllocationIds: props.natEipAllocationIds, - }) : ec2.NatProvider.gateway(); + eipAllocationIds: props.natEipAllocationIds, + }) : ec2.NatProvider.gateway(); + - // First pass: collect all subnets props.subnets.forEach((subnetProps) => { let subnet = this.createSubnet(subnetProps, this.vpc); @@ -235,7 +235,7 @@ export class Network extends Construct { peeringConnectionId: this.peeringConnectionIds, subnetType: subnetProps.subnetType, natProvider: natProvider, - internetGateway: internetGateway + internetGateway: internetGateway, }); this.subnets[subnetProps.subnetGroupName].forEach((subnet, index) => { routeTableManager.associateSubnet(subnet, index); diff --git a/src/constructs/routeTableManager.ts b/src/constructs/routeTableManager.ts index 8655f87..ee1b1f6 100644 --- a/src/constructs/routeTableManager.ts +++ b/src/constructs/routeTableManager.ts @@ -10,7 +10,7 @@ export interface RouteTableManagerProps { readonly peeringConnectionId?: { [key: string]: ec2.CfnVPCPeeringConnection }; readonly subnetType: ec2.SubnetType; readonly natProvider: ec2.NatProvider; - readonly internetGateway: ec2.CfnInternetGateway + readonly internetGateway: ec2.CfnInternetGateway; } export class RouteTableManager extends Construct {