From 6afdb53ce0cc43ddd42dc8bdaad8732b29cb6571 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Sun, 11 May 2025 21:27:25 -0700 Subject: [PATCH 01/21] adding unit test to check for EgressOnlyInternetGateWay --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index f5ae1743eae53..7e0f16184c70f 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2748,6 +2748,49 @@ describe('vpc', () => { }); }); + test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'DualStackStack'); + + // WHEN + const vpc = new Vpc(stack, "Vpc", { + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: "public", + }, + ], + }); + + // THEN + Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway",0) + }); + test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'DualStackStack'); + + // WHEN + const vpc = new Vpc(stack, "Vpc", { + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: "public", + }, + { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + name: "private", + } + ], + }); + + // THEN + Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway",1) + }); + test('error should occur if IPv6 properties are provided for a non-dual-stack VPC', () => { // GIVEN const app = new App(); From d29d608761c44646b93ace8d7d20082ddd66da05 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Sun, 11 May 2025 21:30:05 -0700 Subject: [PATCH 02/21] check for privateSubnet to create EgressOnlyInternetGateWay --- packages/aws-cdk-lib/aws-ec2/lib/vpc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts index 0279df6a967a4..1d23c572910a4 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts @@ -1656,7 +1656,7 @@ export class Vpc extends VpcBase { } // Create an Egress Only Internet Gateway and attach it if necessary - if (this.useIpv6 && this.privateSubnets) { + if (this.useIpv6 && this.privateSubnets.length>0) { const eigw = new CfnEgressOnlyInternetGateway(this, 'EIGW6', { vpcId: this.vpcId, }); From d1286018df1d39b024a2c248563fe0f487333ea9 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 09:50:05 -0700 Subject: [PATCH 03/21] reformatted vpc.test.ts --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index 7e0f16184c70f..a6693337337cb 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2645,9 +2645,9 @@ describe('vpc', () => { [{ maxAzs: 2, reservedAzs: 1 }, { maxAzs: 3 }], [{ maxAzs: 2, reservedAzs: 2 }, { maxAzs: 3, reservedAzs: 1 }], [{ maxAzs: 2, reservedAzs: 1, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS }] }, - { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS }] }], + { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS }] }], [{ maxAzs: 2, reservedAzs: 1, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, reserved: true }] }, - { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, reserved: true }] }], + { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, reserved: true }] }], [{ maxAzs: 2, reservedAzs: 1, ipAddresses: IpAddresses.cidr('192.168.0.0/16') }, { maxAzs: 3, ipAddresses: IpAddresses.cidr('192.168.0.0/16') }], [{ availabilityZones: ['dummy1a', 'dummy1b'], reservedAzs: 1 }, { availabilityZones: ['dummy1a', 'dummy1b', 'dummy1c'] }], ])('subnets should remain the same going from %p to %p', (propsWithReservedAz, propsWithUsedReservedAz) => { @@ -2748,47 +2748,47 @@ describe('vpc', () => { }); }); - test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { + test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN const vpc = new Vpc(stack, "Vpc", { - ipProtocol: IpProtocol.DUAL_STACK, - subnetConfiguration: [ - { - subnetType: SubnetType.PUBLIC, - name: "public", - }, - ], + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: "public", + }, + ], }); // THEN - Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway",0) + Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway", 0) }); - test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN const vpc = new Vpc(stack, "Vpc", { - ipProtocol: IpProtocol.DUAL_STACK, - subnetConfiguration: [ - { - subnetType: SubnetType.PUBLIC, - name: "public", - }, - { - subnetType: SubnetType.PRIVATE_WITH_EGRESS, - name: "private", - } - ], + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: "public", + }, + { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + name: "private", + } + ], }); // THEN - Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway",1) + Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway", 1) }); test('error should occur if IPv6 properties are provided for a non-dual-stack VPC', () => { @@ -2807,13 +2807,13 @@ function getTestStack(): Stack { return new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } -function toCfnTags(tags: any): Array<{Key: string; Value: string}> { - return Object.keys(tags).map( key => { +function toCfnTags(tags: any): Array<{ Key: string; Value: string }> { + return Object.keys(tags).map(key => { return { Key: key, Value: tags[key] }; }); } -function hasTags(expectedTags: Array<{Key: string; Value: string}>) { +function hasTags(expectedTags: Array<{ Key: string; Value: string }>) { return { Properties: { Tags: Match.arrayWith(expectedTags), From b32b7a4aeec7dcab5f2ca6799d5ea13b4f21c7a5 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 10:03:19 -0700 Subject: [PATCH 04/21] reformatted vpc.test.ts --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index a6693337337cb..d97beee434ecb 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2645,9 +2645,9 @@ describe('vpc', () => { [{ maxAzs: 2, reservedAzs: 1 }, { maxAzs: 3 }], [{ maxAzs: 2, reservedAzs: 2 }, { maxAzs: 3, reservedAzs: 1 }], [{ maxAzs: 2, reservedAzs: 1, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS }] }, - { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS }] }], + { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS }] }], [{ maxAzs: 2, reservedAzs: 1, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, reserved: true }] }, - { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, reserved: true }] }], + { maxAzs: 3, subnetConfiguration: [{ cidrMask: 22, name: 'Public', subnetType: SubnetType.PUBLIC }, { cidrMask: 23, name: 'Private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, reserved: true }] }], [{ maxAzs: 2, reservedAzs: 1, ipAddresses: IpAddresses.cidr('192.168.0.0/16') }, { maxAzs: 3, ipAddresses: IpAddresses.cidr('192.168.0.0/16') }], [{ availabilityZones: ['dummy1a', 'dummy1b'], reservedAzs: 1 }, { availabilityZones: ['dummy1a', 'dummy1b', 'dummy1c'] }], ])('subnets should remain the same going from %p to %p', (propsWithReservedAz, propsWithUsedReservedAz) => { @@ -2748,47 +2748,47 @@ describe('vpc', () => { }); }); - test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { + test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN const vpc = new Vpc(stack, "Vpc", { - ipProtocol: IpProtocol.DUAL_STACK, - subnetConfiguration: [ - { - subnetType: SubnetType.PUBLIC, - name: "public", - }, - ], + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: "public", + }, + ], }); // THEN - Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway", 0) + Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway",0) }); - test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN - const vpc = new Vpc(stack, "Vpc", { - ipProtocol: IpProtocol.DUAL_STACK, - subnetConfiguration: [ - { - subnetType: SubnetType.PUBLIC, - name: "public", - }, - { - subnetType: SubnetType.PRIVATE_WITH_EGRESS, - name: "private", - } - ], + const vpc = new Vpc(stack, 'Vpc', { + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + name: 'private', + }, + ], }); // THEN - Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway", 1) + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway',1); }); test('error should occur if IPv6 properties are provided for a non-dual-stack VPC', () => { @@ -2807,13 +2807,13 @@ function getTestStack(): Stack { return new Stack(undefined, 'TestStack', { env: { account: '123456789012', region: 'us-east-1' } }); } -function toCfnTags(tags: any): Array<{ Key: string; Value: string }> { - return Object.keys(tags).map(key => { +function toCfnTags(tags: any): Array<{Key: string; Value: string}> { + return Object.keys(tags).map( key => { return { Key: key, Value: tags[key] }; }); } -function hasTags(expectedTags: Array<{ Key: string; Value: string }>) { +function hasTags(expectedTags: Array<{Key: string; Value: string}>) { return { Properties: { Tags: Match.arrayWith(expectedTags), From b57049748fe59b2d3a65941ef549e747dca367d5 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 10:05:08 -0700 Subject: [PATCH 05/21] reformatted vpc.test.ts --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index d97beee434ecb..8675285742230 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2754,18 +2754,18 @@ describe('vpc', () => { const stack = new Stack(app, 'DualStackStack'); // WHEN - const vpc = new Vpc(stack, "Vpc", { + const vpc = new Vpc(stack, 'Vpc', { ipProtocol: IpProtocol.DUAL_STACK, subnetConfiguration: [ { subnetType: SubnetType.PUBLIC, - name: "public", + name: 'public', }, ], }); // THEN - Template.fromStack(stack).resourceCountIs("AWS::EC2::EgressOnlyInternetGateway",0) + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway',0) }); test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { // GIVEN From d3d669bd2be4512f2e63ff33848c8dccbed275e8 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 10:18:55 -0700 Subject: [PATCH 06/21] reformatted vpc.test.ts --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index 8675285742230..ad75617213bd4 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2748,47 +2748,47 @@ describe('vpc', () => { }); }); - test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { + test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN const vpc = new Vpc(stack, 'Vpc', { - ipProtocol: IpProtocol.DUAL_STACK, - subnetConfiguration: [ - { - subnetType: SubnetType.PUBLIC, - name: 'public', - }, - ], + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + ], }); // THEN - Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway',0) + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 0); }); - test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN const vpc = new Vpc(stack, 'Vpc', { - ipProtocol: IpProtocol.DUAL_STACK, - subnetConfiguration: [ - { - subnetType: SubnetType.PUBLIC, - name: 'public', - }, - { - subnetType: SubnetType.PRIVATE_WITH_EGRESS, - name: 'private', - }, - ], + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + name: 'private', + }, + ], }); // THEN - Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway',1); + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); test('error should occur if IPv6 properties are provided for a non-dual-stack VPC', () => { From da5c46aa28208c4f10bf82e81dc0c029bda37e6c Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 15:35:21 -0700 Subject: [PATCH 07/21] add feature flag to vpc dual stack change --- packages/aws-cdk-lib/aws-ec2/lib/vpc.ts | 8 ++- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 53 +++++++++++++++++-- packages/aws-cdk-lib/cx-api/lib/features.ts | 13 +++++ 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts index 1d23c572910a4..61a2b2add77b7 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts @@ -1656,7 +1656,13 @@ export class Vpc extends VpcBase { } // Create an Egress Only Internet Gateway and attach it if necessary - if (this.useIpv6 && this.privateSubnets.length>0) { + + const redundantEgressOnlyGatewayRemovalFeatureFlag = + FeatureFlags.of(this).isEnabled(cxapi.ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC); + + if ((this.useIpv6 && !redundantEgressOnlyGatewayRemovalFeatureFlag && this.privateSubnets) || + (this.useIpv6 && redundantEgressOnlyGatewayRemovalFeatureFlag && this.privateSubnets.length > 0) + ) { const eigw = new CfnEgressOnlyInternetGateway(this, 'EIGW6', { vpcId: this.vpcId, }); diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index ad75617213bd4..b495879da198e 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -1,7 +1,7 @@ import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Annotations, Match, Template } from '../../assertions'; import { App, CfnOutput, CfnResource, Fn, Lazy, Stack, Tags } from '../../core'; -import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP } from '../../cx-api'; +import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP, ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC } from '../../cx-api'; import { AclCidr, AclTraffic, @@ -2747,8 +2747,7 @@ describe('vpc', () => { }, }); }); - - test('EgressOnlyInternetGateWay is not created when no private subnet configured in dual stack', () => { + test('(default)EgressOnlyInternetGateWay is created when no private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); @@ -2765,9 +2764,9 @@ describe('vpc', () => { }); // THEN - Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 0); + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); - test('EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + test('(default)EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); @@ -2791,6 +2790,50 @@ describe('vpc', () => { Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); + test('(feature flag ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC)EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'DualStackStack'); + // WHEN + stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); + const vpc = new Vpc(stack, 'Vpc', { + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + { + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + name: 'private', + }, + ], + }); + + // THEN + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); + }); + test(' (feature flag ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC)EgressOnlyInternetGateWay is not created when private subnet configured in dual stack', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'DualStackStack'); + + + // WHEN + stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); + const vpc = new Vpc(stack, 'Vpc', { + ipProtocol: IpProtocol.DUAL_STACK, + subnetConfiguration: [ + { + subnetType: SubnetType.PUBLIC, + name: 'public', + }, + ], + }); + // THEN + Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 0); + }); + test('error should occur if IPv6 properties are provided for a non-dual-stack VPC', () => { // GIVEN const app = new App(); diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 026535906bfcd..388754f64d472 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -139,6 +139,7 @@ export const PIPELINE_REDUCE_CROSS_ACCOUNT_ACTION_ROLE_TRUST_SCOPE = '@aws-cdk/p export const S3_TRUST_KEY_POLICY_FOR_SNS_SUBSCRIPTIONS = '@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions'; export const USE_RESOURCEID_FOR_VPCV2_MIGRATION = '@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration'; export const S3_PUBLIC_ACCESS_BLOCKED_BY_DEFAULT = '@aws-cdk/aws-s3:publicAccessBlockedByDefault'; +export const ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC = '@aws-cdk/ec2:removeEgressOnlyGatewayFromPublicSubnetVPC'; export const FLAGS: Record = { ////////////////////////////////////////////////////////////////////// @@ -1605,6 +1606,18 @@ export const FLAGS: Record = { introducedIn: { v2: 'V2NEXT' }, recommendedValue: true, }, + + ////////////////////////////////////////////////////////////////////// + [ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC]: { + type: FlagType.BugFix, + summary: 'Remove EgressOnlyGateway resource when a a double stack vpc has only public subnets', + detailsMd: ` + When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. A + `, + introducedIn: { v2: '2.196.0' }, + defaults: { v2: false }, + recommendedValue: true, + }, }; const CURRENT_MV = 'v2'; From 71390de991eb3516f07cd94a9eb2f2905ab78672 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 15:44:41 -0700 Subject: [PATCH 08/21] added feature flag to vpc dual stack change --- packages/aws-cdk-lib/cx-api/lib/features.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 388754f64d472..8071ad76eab25 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -1614,7 +1614,7 @@ export const FLAGS: Record = { detailsMd: ` When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. A `, - introducedIn: { v2: '2.196.0' }, + introducedIn: { v2: 'V2NEXT' }, defaults: { v2: false }, recommendedValue: true, }, From 26fc0e7cbd499de5cad4fbf74591cee49562ee1b Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 16:02:12 -0700 Subject: [PATCH 09/21] removed default value --- packages/aws-cdk-lib/cx-api/lib/features.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 8071ad76eab25..0810dd85f62ed 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -1615,7 +1615,6 @@ export const FLAGS: Record = { When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. A `, introducedIn: { v2: 'V2NEXT' }, - defaults: { v2: false }, recommendedValue: true, }, }; From d0bf216587906e8f80fb371be2ea737d8bcebdb6 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 16:10:28 -0700 Subject: [PATCH 10/21] reformmated --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index b495879da198e..e30509752476b 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2817,10 +2817,9 @@ describe('vpc', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); - + stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); // WHEN - stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); const vpc = new Vpc(stack, 'Vpc', { ipProtocol: IpProtocol.DUAL_STACK, subnetConfiguration: [ @@ -2830,6 +2829,7 @@ describe('vpc', () => { }, ], }); + // THEN Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 0); }); From 1a6bca1b55c647af497b5302679b04a2c6fb96a0 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 16:26:47 -0700 Subject: [PATCH 11/21] built --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 2 -- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index e30509752476b..28ab80985e5df 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2818,7 +2818,6 @@ describe('vpc', () => { const app = new App(); const stack = new Stack(app, 'DualStackStack'); stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); - // WHEN const vpc = new Vpc(stack, 'Vpc', { ipProtocol: IpProtocol.DUAL_STACK, @@ -2829,7 +2828,6 @@ describe('vpc', () => { }, ], }); - // THEN Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 0); }); diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index e7112c6f9c9e2..60fb574331d54 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -2117,6 +2117,13 @@ When BlockPublicAccess is not set at all, s3's default behavior will be to set a The previous behavior in cdk before this feature was; if only some of the BlockPublicAccessOptions were set (not all 4), then the ones undefined would default to false. This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. +### @aws-cdk/ec2:removeEgressOnlyGatewayFromPublicSubnetVPC + +*Remove EgressOnlyGateway resource when a a double stack vpc has only public subnets* + +Flag type: Backwards incompatible bugfix + +When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. A | Since | Default | Recommended | From f3ae227dd08f716d872798927f29dcc3df3aea24 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Mon, 12 May 2025 16:41:42 -0700 Subject: [PATCH 12/21] updated-md --- packages/aws-cdk-lib/cx-api/lib/features.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 0810dd85f62ed..ff7e9937fe1b9 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -1612,7 +1612,7 @@ export const FLAGS: Record = { type: FlagType.BugFix, summary: 'Remove EgressOnlyGateway resource when a a double stack vpc has only public subnets', detailsMd: ` - When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. A + When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. `, introducedIn: { v2: 'V2NEXT' }, recommendedValue: true, From 7d15d77d1a5865547ff634157aea708fee1349fb Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 09:19:11 -0700 Subject: [PATCH 13/21] updated test description Co-authored-by: paulhcsun <47882901+paulhcsun@users.noreply.github.com> --- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index 28ab80985e5df..6886c3e7d2c36 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -2813,7 +2813,7 @@ describe('vpc', () => { // THEN Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); - test(' (feature flag ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC)EgressOnlyInternetGateWay is not created when private subnet configured in dual stack', () => { + test(' (feature flag ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC)EgressOnlyInternetGateWay is not created when no private subnets are configured in dual stack', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); From 24d1a10c5434d3281d9835e146d2ff4de0efa556 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 09:53:58 -0700 Subject: [PATCH 14/21] updated feature flag name --- packages/aws-cdk-lib/aws-ec2/lib/vpc.ts | 8 ++++---- packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts | 14 +++++++------- packages/aws-cdk-lib/cx-api/lib/features.ts | 12 ++++++++++++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts index 61a2b2add77b7..7f37a9c652077 100644 --- a/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts +++ b/packages/aws-cdk-lib/aws-ec2/lib/vpc.ts @@ -1657,11 +1657,11 @@ export class Vpc extends VpcBase { // Create an Egress Only Internet Gateway and attach it if necessary - const redundantEgressOnlyGatewayRemovalFeatureFlag = - FeatureFlags.of(this).isEnabled(cxapi.ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC); + const isRequirePrivateSubnetsForEgressOnlyIgw = + FeatureFlags.of(this).isEnabled(cxapi.EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY); - if ((this.useIpv6 && !redundantEgressOnlyGatewayRemovalFeatureFlag && this.privateSubnets) || - (this.useIpv6 && redundantEgressOnlyGatewayRemovalFeatureFlag && this.privateSubnets.length > 0) + if ((this.useIpv6 && !isRequirePrivateSubnetsForEgressOnlyIgw && this.privateSubnets) || + (this.useIpv6 && isRequirePrivateSubnetsForEgressOnlyIgw && this.privateSubnets.length > 0) ) { const eigw = new CfnEgressOnlyInternetGateway(this, 'EIGW6', { vpcId: this.vpcId, diff --git a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts index 6886c3e7d2c36..fa69738fa2617 100644 --- a/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts +++ b/packages/aws-cdk-lib/aws-ec2/test/vpc.test.ts @@ -1,7 +1,7 @@ import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Annotations, Match, Template } from '../../assertions'; import { App, CfnOutput, CfnResource, Fn, Lazy, Stack, Tags } from '../../core'; -import { EC2_RESTRICT_DEFAULT_SECURITY_GROUP, ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC } from '../../cx-api'; +import { EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY, EC2_RESTRICT_DEFAULT_SECURITY_GROUP } from '../../cx-api'; import { AclCidr, AclTraffic, @@ -2747,7 +2747,7 @@ describe('vpc', () => { }, }); }); - test('(default)EgressOnlyInternetGateWay is created when no private subnet configured in dual stack', () => { + test('EgressOnlyIGW is created if no private subnet configured in dual stack and feature flag EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY is not enabled', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); @@ -2766,7 +2766,7 @@ describe('vpc', () => { // THEN Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); - test('(default)EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + test('EgressOnlyIGW is created if a private subnet is configured in dual stack and feature flag EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY is not enabled', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); @@ -2790,12 +2790,12 @@ describe('vpc', () => { Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); - test('(feature flag ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC)EgressOnlyInternetGateWay is created when private subnet configured in dual stack', () => { + test('EgressOnlyIGW is created if a private subnet is configured in dual stack and feature flag EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY is enabled', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); // WHEN - stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); + stack.node.setContext(EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY, true); const vpc = new Vpc(stack, 'Vpc', { ipProtocol: IpProtocol.DUAL_STACK, subnetConfiguration: [ @@ -2813,11 +2813,11 @@ describe('vpc', () => { // THEN Template.fromStack(stack).resourceCountIs('AWS::EC2::EgressOnlyInternetGateway', 1); }); - test(' (feature flag ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC)EgressOnlyInternetGateWay is not created when no private subnets are configured in dual stack', () => { + test('EgressOnlyIGW is not created if no private subnet is configured in dual stack and feature flag EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY is enabled', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'DualStackStack'); - stack.node.setContext(ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC, true); + stack.node.setContext(EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY, true); // WHEN const vpc = new Vpc(stack, 'Vpc', { ipProtocol: IpProtocol.DUAL_STACK, diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index ff7e9937fe1b9..a83bca7de4d8d 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -137,6 +137,7 @@ export const DYNAMODB_TABLE_RETAIN_TABLE_REPLICA = '@aws-cdk/aws-dynamodb:retain export const LOG_USER_POOL_CLIENT_SECRET_VALUE = '@aws-cdk/cognito:logUserPoolClientSecretValue'; export const PIPELINE_REDUCE_CROSS_ACCOUNT_ACTION_ROLE_TRUST_SCOPE = '@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope'; export const S3_TRUST_KEY_POLICY_FOR_SNS_SUBSCRIPTIONS = '@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions'; +export const EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY = '@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway'; export const USE_RESOURCEID_FOR_VPCV2_MIGRATION = '@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration'; export const S3_PUBLIC_ACCESS_BLOCKED_BY_DEFAULT = '@aws-cdk/aws-s3:publicAccessBlockedByDefault'; export const ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC = '@aws-cdk/ec2:removeEgressOnlyGatewayFromPublicSubnetVPC'; @@ -1579,6 +1580,17 @@ export const FLAGS: Record = { }, ////////////////////////////////////////////////////////////////////// + [EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY]: { + type: FlagType.BugFix, + summary: 'When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC.', + detailsMd: ` + When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. + `, + introducedIn: { v2: 'V2NEXT' }, + recommendedValue: true, + }, + + /// /////////////////////////////////////////////////////////////////// [USE_RESOURCEID_FOR_VPCV2_MIGRATION]: { type: FlagType.ApiDefault, summary: 'When enabled, use resource IDs for VPC V2 migration', From 55eaff17742acc8adee910dfe5c7373973265a2c Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 10:10:23 -0700 Subject: [PATCH 15/21] renamed featureflag --- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 7 +++++-- packages/aws-cdk-lib/recommended-feature-flags.json | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 60fb574331d54..cbf7006e093a9 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -101,6 +101,7 @@ Flags come in three types: | [@aws-cdk/core:aspectPrioritiesMutating](#aws-cdkcoreaspectprioritiesmutating) | When set to true, Aspects added by the construct library on your behalf will be given a priority of MUTATING. | 2.189.1 | new default | | [@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions](#aws-cdks3-notificationsadds3trustkeypolicyforsnssubscriptions) | Add an S3 trust policy to a KMS key resource policy for SNS subscriptions. | 2.195.0 | fix | | [@aws-cdk/aws-s3:publicAccessBlockedByDefault](#aws-cdkaws-s3publicaccessblockedbydefault) | When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined. | V2NEXT | fix | +| [@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway](#aws-cdkaws-ec2requireprivatesubnetsforegressonlyinternetgateway) | When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC. | V2NEXT | fix | @@ -186,6 +187,7 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true } } ``` @@ -2118,12 +2120,13 @@ The previous behavior in cdk before this feature was; if only some of the BlockP This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. ### @aws-cdk/ec2:removeEgressOnlyGatewayFromPublicSubnetVPC +### @aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway -*Remove EgressOnlyGateway resource when a a double stack vpc has only public subnets* +*When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC.* Flag type: Backwards incompatible bugfix -When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. A +When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. | Since | Default | Recommended | diff --git a/packages/aws-cdk-lib/recommended-feature-flags.json b/packages/aws-cdk-lib/recommended-feature-flags.json index b847d4afb96b6..7668ba2594af0 100644 --- a/packages/aws-cdk-lib/recommended-feature-flags.json +++ b/packages/aws-cdk-lib/recommended-feature-flags.json @@ -73,4 +73,5 @@ "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true } \ No newline at end of file From 61facf40a36586cc9af57d6a2215812c9bf3f009 Mon Sep 17 00:00:00 2001 From: Ike Nefcy Date: Mon, 12 May 2025 21:22:35 -0700 Subject: [PATCH 16/21] fix(s3): updating blockPublicAccess to enable default true settings (under feature flag) (#33702) V3 but I think we got there Closes #32811 By default when you create an s3 bucket, all public access is already blocked. However if you then use CDK to specify 1 or more access point you want to unblock, all undefined block types will be auto set to false, and when it deploys you will see everything uncheck even if you only wanted to uncheck 1 thing. So to fix this we should instead default all values to true when at least 1 option is specified, to mimic to experience when a user in the console unchecks the boxes. deprecating `BLOCK_ACLS` method of `BlockPublicAccess`. Adding `BLOCK_ACLS_ONLY`. ``` public static readonly BLOCK_ACLS_ONLY = new BlockPublicAccess({ blockPublicAcls: true, blockPublicPolicy: false, ignorePublicAcls: true, restrictPublicBuckets: false, }); ``` This is just a general revamp to match what the feature will bring, it's separate from the feature itself. The point being that for any shortcut methods like this, we should be specifying all 4 options to ensure the default true behavior remains. Created function `setBlockPublicAccessDefaults()` ``` /** * Function to set the blockPublicAccessOptions to a true default if not defined. * If no blockPublicAccessOptions are specified at all, this is already the case as an s3 default in aws * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html */ private setBlockPublicAccessDefaults(blockPublicAccessOptions: BlockPublicAccessOptions) { return { blockPublicAcls: blockPublicAccessOptions.blockPublicAcls ?? true, blockPublicPolicy: blockPublicAccessOptions.blockPublicPolicy ?? true, ignorePublicAcls: blockPublicAccessOptions.ignorePublicAcls ?? true, restrictPublicBuckets: blockPublicAccessOptions.restrictPublicBuckets ?? true, }; } ``` but this method is only called if the FF is enabled ``` let blockPublicAccess: BlockPublicAccessOptions | undefined = props.blockPublicAccess; if (props.blockPublicAccess && FeatureFlags.of(this).isEnabled(cxapi.S3_BLOCK_PUBLIC_ACCESS_OPTION_AUTO_TRUE)) { blockPublicAccess = this.setBlockPublicAccessDefaults(props.blockPublicAccess); } ``` Of course the FF itself was added. Added tests that are duplicates of others, just testing for both behaviors with and without the FF. ``` describe('bucket with custom block public access setting', () => { ... test('S3_BLOCK_PUBLIC_ACCESS_OPTION_AUTO_TRUE Enabled', () => { const app = new cdk.App({ context: { [cxapi.S3_BLOCK_PUBLIC_ACCESS_OPTION_AUTO_TRUE]: true, }, }); const stack = new cdk.Stack(app); new s3.Bucket(stack, 'MyBucket', { blockPublicAccess: new s3.BlockPublicAccess({ restrictPublicBuckets: false }), }); Template.fromStack(stack).templateMatches({ 'Resources': { 'MyBucketF68F3FF0': { 'Type': 'AWS::S3::Bucket', 'Properties': { 'PublicAccessBlockConfiguration': { 'BlockPublicAcls': true, 'BlockPublicPolicy': true, 'IgnorePublicAcls': true, 'RestrictPublicBuckets': false, }, }, 'DeletionPolicy': 'Retain', 'UpdateReplacePolicy': 'Retain', }, }, }); }); ``` ``` describe('bucket with custom block public access setting', () => { ... test('S3_BLOCK_PUBLIC_ACCESS_OPTION_AUTO_TRUE Enabled', () => { const app = new cdk.App({ context: { [cxapi.S3_BLOCK_PUBLIC_ACCESS_OPTION_AUTO_TRUE]: true, }, }); const stack = new cdk.Stack(app); new s3.Bucket(stack, 'MyBucket', { blockPublicAccess: new s3.BlockPublicAccess({ restrictPublicBuckets: false }), }); Template.fromStack(stack).templateMatches({ 'Resources': { 'MyBucketF68F3FF0': { 'Type': 'AWS::S3::Bucket', 'Properties': { 'PublicAccessBlockConfiguration': { 'BlockPublicAcls': true, 'BlockPublicPolicy': true, 'IgnorePublicAcls': true, 'RestrictPublicBuckets': false, }, }, 'DeletionPolicy': 'Retain', 'UpdateReplacePolicy': 'Retain', }, }, }); }); ``` Also added an integ that just tests different combinations of the blocking. https://github.com/aws/aws-cdk/blob/51ffe2112e048f5866e5c0d811377b4deca7920d/packages/%40aws-cdk-testing/framework-integ/test/aws-s3/test/integ.bucket-block-access.ts#L1-L42 There was no `BlockPublicAccess` integ before so I did not add the context for the FF disabled anywhere. The tests should still be working since it's not used that often. But if the team needs me to, I can add a 2nd integ with the old behavior - [X] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* rebased main --- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 39 ++++++++++++++++++- .../recommended-feature-flags.json | 2 +- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index cbf7006e093a9..931117ef559d4 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -186,7 +186,7 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@aws-cdk/aws-dynamodb:retainTableReplica": true, "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, - "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true, "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true } } @@ -2119,7 +2119,42 @@ When BlockPublicAccess is not set at all, s3's default behavior will be to set a The previous behavior in cdk before this feature was; if only some of the BlockPublicAccessOptions were set (not all 4), then the ones undefined would default to false. This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. -### @aws-cdk/ec2:removeEgressOnlyGatewayFromPublicSubnetVPC + + +| Since | Default | Recommended | +| ----- | ----- | ----- | +| (not in v1) | | | +| V2NEXT | `false` | `true` | + + +### @aws-cdk/aws-s3:publicAccessBlockedByDefault + +*When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined.* + +Flag type: Backwards incompatible bugfix + +When BlockPublicAccess is not set at all, s3's default behavior will be to set all options to true in aws console. +The previous behavior in cdk before this feature was; if only some of the BlockPublicAccessOptions were set (not all 4), then the ones undefined would default to false. +This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. +The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. + + +| Since | Default | Recommended | +| ----- | ----- | ----- | +| (not in v1) | | | +| V2NEXT | `false` | `true` | + + +### @aws-cdk/aws-s3:publicAccessBlockedByDefault + +*When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined.* + +Flag type: Backwards incompatible bugfix + +When BlockPublicAccess is not set at all, s3's default behavior will be to set all options to true in aws console. +The previous behavior in cdk before this feature was; if only some of the BlockPublicAccessOptions were set (not all 4), then the ones undefined would default to false. +This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. +The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. ### @aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway *When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC.* diff --git a/packages/aws-cdk-lib/recommended-feature-flags.json b/packages/aws-cdk-lib/recommended-feature-flags.json index 7668ba2594af0..0af7fa64e3b68 100644 --- a/packages/aws-cdk-lib/recommended-feature-flags.json +++ b/packages/aws-cdk-lib/recommended-feature-flags.json @@ -72,6 +72,6 @@ "@aws-cdk/aws-dynamodb:retainTableReplica": true, "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true, "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true - "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true } \ No newline at end of file From 7c20d4ca2999c7630786a03bb38601538b8df6d9 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 09:53:58 -0700 Subject: [PATCH 17/21] updated feature flag name resolved merge conflict --- packages/aws-cdk-lib/cx-api/lib/features.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index a83bca7de4d8d..6b20d81014b84 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -140,7 +140,6 @@ export const S3_TRUST_KEY_POLICY_FOR_SNS_SUBSCRIPTIONS = '@aws-cdk/s3-notificati export const EC2_REQUIRE_PRIVATE_SUBNETS_FOR_EGRESSONLYINTERNETGATEWAY = '@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway'; export const USE_RESOURCEID_FOR_VPCV2_MIGRATION = '@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration'; export const S3_PUBLIC_ACCESS_BLOCKED_BY_DEFAULT = '@aws-cdk/aws-s3:publicAccessBlockedByDefault'; -export const ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC = '@aws-cdk/ec2:removeEgressOnlyGatewayFromPublicSubnetVPC'; export const FLAGS: Record = { ////////////////////////////////////////////////////////////////////// From e45ac6348f278516d5d4ef59653bc7619a557b45 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 10:10:23 -0700 Subject: [PATCH 18/21] renamed featureflag resolved merge conflict resolved merge conflicts --- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 29 ------------------- .../recommended-feature-flags.json | 4 +-- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 931117ef559d4..6e2583e93a1d8 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -2108,7 +2108,6 @@ When this feature flag is enabled, a S3 trust policy will be added to the KMS ke | (not in v1) | | | | 2.195.0 | `false` | `true` | - ### @aws-cdk/aws-s3:publicAccessBlockedByDefault *When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined.* @@ -2127,34 +2126,6 @@ The new behavior from this feature will allow a user, for example, to set 1 of t | V2NEXT | `false` | `true` | -### @aws-cdk/aws-s3:publicAccessBlockedByDefault - -*When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined.* - -Flag type: Backwards incompatible bugfix - -When BlockPublicAccess is not set at all, s3's default behavior will be to set all options to true in aws console. -The previous behavior in cdk before this feature was; if only some of the BlockPublicAccessOptions were set (not all 4), then the ones undefined would default to false. -This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. -The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| V2NEXT | `false` | `true` | - - -### @aws-cdk/aws-s3:publicAccessBlockedByDefault - -*When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined.* - -Flag type: Backwards incompatible bugfix - -When BlockPublicAccess is not set at all, s3's default behavior will be to set all options to true in aws console. -The previous behavior in cdk before this feature was; if only some of the BlockPublicAccessOptions were set (not all 4), then the ones undefined would default to false. -This is counter intuitive to the console behavior where the options would start in true state and a user would uncheck the boxes as needed. -The new behavior from this feature will allow a user, for example, to set 1 of the 4 BlockPublicAccessOpsions to false, and on deployment the other 3 will remain true. ### @aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway *When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC.* diff --git a/packages/aws-cdk-lib/recommended-feature-flags.json b/packages/aws-cdk-lib/recommended-feature-flags.json index 0af7fa64e3b68..625fe4ae7c8ab 100644 --- a/packages/aws-cdk-lib/recommended-feature-flags.json +++ b/packages/aws-cdk-lib/recommended-feature-flags.json @@ -72,6 +72,6 @@ "@aws-cdk/aws-dynamodb:retainTableReplica": true, "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, - "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true, - "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true } \ No newline at end of file From 1a16300eab2e37d2f0711cb3294ae22f78b1ed81 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 11:35:31 -0700 Subject: [PATCH 19/21] reformatted feature.ts --- packages/aws-cdk-lib/cx-api/lib/features.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index 6b20d81014b84..1208979616281 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -1617,17 +1617,6 @@ export const FLAGS: Record = { introducedIn: { v2: 'V2NEXT' }, recommendedValue: true, }, - - ////////////////////////////////////////////////////////////////////// - [ENABLE_E2_REMOVE_EGRESSONLYGATEWAY_FROM_PUBLIC_SUBNET_VPC]: { - type: FlagType.BugFix, - summary: 'Remove EgressOnlyGateway resource when a a double stack vpc has only public subnets', - detailsMd: ` - When this feature flag is enabled, EgressOnlyGateway resource will not be created when you create a vpc with only public subnets. - `, - introducedIn: { v2: 'V2NEXT' }, - recommendedValue: true, - }, }; const CURRENT_MV = 'v2'; From 9336981fa01329d22032dfae67848780fe8530d3 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 11:38:33 -0700 Subject: [PATCH 20/21] reformatted feature_flags.md --- packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 6e2583e93a1d8..7aef148b24512 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -2108,6 +2108,7 @@ When this feature flag is enabled, a S3 trust policy will be added to the KMS ke | (not in v1) | | | | 2.195.0 | `false` | `true` | + ### @aws-cdk/aws-s3:publicAccessBlockedByDefault *When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined.* From 22c1a6a10e107b3f71c2a9e018317c0773162bb3 Mon Sep 17 00:00:00 2001 From: Maharaj Haider Date: Wed, 14 May 2025 11:51:26 -0700 Subject: [PATCH 21/21] updated README.md --- packages/aws-cdk-lib/cx-api/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/aws-cdk-lib/cx-api/README.md b/packages/aws-cdk-lib/cx-api/README.md index 4bc2a079a1b43..38db2a32d6a01 100644 --- a/packages/aws-cdk-lib/cx-api/README.md +++ b/packages/aws-cdk-lib/cx-api/README.md @@ -732,3 +732,18 @@ _cdk.json_ } } ``` + +* `@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway` + +When this feature flag is enabled, EgressOnlyGateway is created only for dual-stack VPC with private subnets + +When this feature flag is disabled, EgressOnlyGateway resource is created for all dual-stack VPC regardless of subnet type + +_cdk.json_ + +```json +{ + "context": { + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true + } +} \ No newline at end of file