From 3bede9a4f48ad1641428782c817fa54e7e55b96f Mon Sep 17 00:00:00 2001 From: shikha372 Date: Tue, 14 Jan 2025 14:22:44 -0800 Subject: [PATCH] feat(VpcV2): add BYOIP IPv6 to VPCv2 --- .../@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts | 2 +- packages/@aws-cdk/aws-ec2-alpha/lib/util.ts | 86 ++++- .../@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts | 2 +- packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts | 99 ++++- .../test/integ.byoip-ipv6.js.snapshot/cdk.out | 1 + .../integ.byoip-ipv6.js.snapshot/integ.json | 12 + ...efaultTestDeployAssertCF40BD53.assets.json | 19 + ...aultTestDeployAssertCF40BD53.template.json | 36 ++ .../manifest.json | 155 ++++++++ .../integ.byoip-ipv6.js.snapshot/tree.json | 361 ++++++++++++++++++ .../vpc-byoip-ipv6.assets.json | 19 + .../vpc-byoip-ipv6.template.json | 176 +++++++++ .../aws-ec2-alpha/test/integ.byoip-ipv6.ts | 52 +++ .../aws-ec2-alpha/test/vpc-v2.test.ts | 50 ++- 14 files changed, 1045 insertions(+), 25 deletions(-) create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.ts diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts index ad144439ce2c3..34e97d9404641 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/subnet-v2.ts @@ -455,7 +455,7 @@ function storeSubnetToVpcByType(vpc: IVpcV2, subnet: SubnetV2, type: SubnetType) function validateSupportIpv6(vpc: IVpcV2) { if (vpc.secondaryCidrBlock) { if (vpc.secondaryCidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true || - secondaryAddress.ipv6IpamPoolId != undefined)) { + secondaryAddress.ipv6IpamPoolId != undefined || secondaryAddress.ipv6Pool != undefined)) { return true; } else { throw new Error('To use IPv6, the VPC must enable IPv6 support.'); diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts index 7566ce711e0c4..86158297624cb 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts @@ -323,13 +323,65 @@ export class CidrBlockIpv6 { } /** - * @returns Maximum IPv6 address for a provided CIDR + * Calculates the maximum IPv6 address in the CIDR block + * + * For example, with CIDR 2001:db8::8/56: + * + * 1. networkPartLength = Math.ceil(56/16) = 4 parts needed for network portion + * 2. remainingBits = 56 % 16 = 8 bits remaining in last network part + * 3. endIP starts as [2001, db8, 0, 0] (network part) + * 4. For remaining bits (8), creates mask: (1 << (16-8)) - 1 = 0x00FF + * 5. Applies mask to last network part: 0x0000 | 0x00FF = 0x00FF + * 6. Fills remaining parts with 0xFFFF: [2001, db8, 0, ff, ffff, ffff, ffff, ffff] + * 7. Final address: 2001:db8:0:ff:ffff:ffff:ffff:ffff + * + * @returns The maximum IPv6 address as a string */ public maxIp(): string { + /** + * Calculate how many 16-bit blocks are needed for the network portion + * e.g. for /56, networkPartLength = ceil(56/16) = 4 blocks + */ + const networkPartLength = Math.ceil(this.cidrPrefix / 16); + /** + * Calculate remaining bits in last network block + * e.g. for /56, remainingBits = 56 % 16 = 8 bits + */ + const remainingBits = this.cidrPrefix % 16; + /** + * Create copy of network portion of address + * e.g. [2001, db8, 0, 0] for 2001:db8::/56 + */ const endIP = [...this.networkPart]; - const hostPart = Array(8 - this.networkPart.length).fill(BigInt(0xffff)); - endIP.push(...hostPart); + /** + * If there are remaining bits in last network block, + * create appropriate bitmask and apply to last network block + * e.g. for /56: mask = (1 << (16-8)) - 1 = 0x00FF + */ + if (remainingBits > 0) { + const lastNetworkIndex = networkPartLength - 1; + const mask = (BigInt(1) << BigInt(16 - remainingBits)) - BigInt(1); + /** + * Apply bitmask to last network block using bitwise OR + * e.g. if lastNetworkIndex=3 and mask=0x00FF: + * networkPart[3]=0x0000 | 0x00FF = 0x00FF + */ + endIP[lastNetworkIndex] = this.networkPart[lastNetworkIndex] | mask; + } + + /** + * Fill remaining blocks with maximum value 0xFFFF + * e.g. [2001, db8, 0, ff, ffff, ffff, ffff, ffff] + */ + for (let i = networkPartLength; i < 8; i++) { + endIP.push(BigInt('0xffff')); + } + + /** + * Convert blocks to hex strings and join with colons + * e.g. 2001:db8:0:ff:ffff:ffff:ffff:ffff + */ return endIP.map(this.formatIPv6Part).join(':'); } @@ -342,26 +394,18 @@ export class CidrBlockIpv6 { * @returns true if two ranges overlap, false otherwise */ public rangesOverlap(range1: string, range2: string): boolean { - const [start1, end1] = this.getIPv6Range(range1); - const [start2, end2] = this.getIPv6Range(range2); + // Create new CidrBlockIpv6 instances for both ranges + const cidr1 = new CidrBlockIpv6(range1); + const cidr2 = new CidrBlockIpv6(range2); - return (start1 <= end2) && (start2 <= end1); - } + // Convert min and max IPs to numeric values for comparison + const start1 = this.ipv6ToNumber(cidr1.minIp()); + const end1 = this.ipv6ToNumber(cidr1.maxIp()); + const start2 = this.ipv6ToNumber(cidr2.minIp()); + const end2 = this.ipv6ToNumber(cidr2.maxIp()); - /** - * - * @param cidr - * @returns Range in the from of big int number [start, end] - */ - private getIPv6Range(cidr: string): [bigint, bigint] { - const [ipv6Address, prefixLength] = cidr.split('/'); - const ipv6Number = this.ipv6ToNumber(ipv6Address); - const mask = (BigInt(1) << BigInt(128 - Number(prefixLength))) - BigInt(1); - const networkPrefix = ipv6Number & ~mask; - const start = networkPrefix; - const end = networkPrefix | mask; - - return [start, end]; + // Check for overlap + return (start1 <= end2) && (start2 <= end1); } /** diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts index 32b2bceec2720..5c33bd3f81792 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts @@ -430,7 +430,7 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { let useIpv6; if (this.secondaryCidrBlock) { useIpv6 = (this.secondaryCidrBlock.some((secondaryAddress) => secondaryAddress.amazonProvidedIpv6CidrBlock === true || - secondaryAddress.ipv6IpamPoolId != undefined)); + secondaryAddress.ipv6IpamPoolId != undefined || secondaryAddress.ipv6CidrBlock !== undefined)); } if (!useIpv6) { diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts index ea493343f8802..e2b0427ae5717 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts @@ -17,6 +17,27 @@ export interface SecondaryAddressProps { readonly cidrBlockName: string; } +/** + * Additional props needed for BYOIP IPv6 address props + */ +export interface Ipv6PoolSecondaryAddressProps extends SecondaryAddressProps { + /** + * ID of the IPv6 address pool from which to allocate the IPv6 CIDR block + * Note: BYOIP Pool ID is different than the pool ID of IPAM. + * To onboard your IPv6 address range to AWS account please refer to below documentation + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/byoip-onboard.html + */ + readonly ipv6Pool: string; + + /** + * An valid IPv6 CIDR block from the IPv6 address pool onboarded to AWS using BYOIP. + * The most specific IPv6 address range that you can bring is /48 for CIDRs that are publicly advertisable + * and /56 for CIDRs that are not publicly advertisable. + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-byoip.html#byoip-definitions + */ + readonly ipv6CidrBlock: string; +} + /** * IpAddress options to define VPC V2 */ @@ -49,6 +70,13 @@ export class IpAddresses { public static amazonProvidedIpv6(props: SecondaryAddressProps) : IIpAddresses { return new AmazonProvided(props); } + + /** + * A BYOIP IPv6 address pool + */ + public static ipv6Pool(props: Ipv6PoolSecondaryAddressProps): IIpAddresses { + return new Ipv6Pool(props); + } } /** @@ -121,6 +149,20 @@ export interface VpcCidrOptions { * @default - no IPAM IPv4 CIDR range is provisioned using IPAM */ readonly ipv4IpamProvisionedCidrs?: string[]; + + /** + * IPv6 CIDR block from the IPv6 address pool. + * + * @default - None + */ + readonly ipv6CidrBlock?: string; + + /** + * ID of the IPv6 address pool from which to allocate the IPv6 CIDR block + * + * @default - None + */ + readonly ipv6Pool?: string; } /** @@ -499,7 +541,7 @@ export class VpcV2 extends VpcV2Base { throw new Error('Cidr Block Name is required to create secondary IP address'); } - if (secondaryVpcOptions.amazonProvided || secondaryVpcOptions.ipv6IpamPool) { + if (secondaryVpcOptions.amazonProvided || secondaryVpcOptions.ipv6IpamPool || secondaryVpcOptions.ipv6Pool) { this.useIpv6 = true; } //validate CIDR ranges per RFC 1918 @@ -520,6 +562,10 @@ export class VpcV2 extends VpcV2Base { ipv6NetmaskLength: secondaryVpcOptions.ipv6NetmaskLength, ipv6IpamPoolId: secondaryVpcOptions.ipv6IpamPool?.ipamPoolId, amazonProvidedIpv6CidrBlock: secondaryVpcOptions.amazonProvided, + //BYOIP IPv6 Address + ipv6CidrBlock: secondaryVpcOptions?.ipv6CidrBlock, + //BYOIP Pool for IPv6 address + ipv6Pool: secondaryVpcOptions?.ipv6Pool, }); if (secondaryVpcOptions.dependencies) { for (const dep of secondaryVpcOptions.dependencies) { @@ -633,6 +679,23 @@ class IpamIpv4 implements IIpAddresses { } } +/** + * Supports assigning IPv6 address to VPC in an address pool + */ +class Ipv6Pool implements IIpAddresses { + + constructor(private readonly props: Ipv6PoolSecondaryAddressProps) { + } + + allocateVpcCidr(): VpcCidrOptions { + return { + ipv6CidrBlock: this.props.ipv6CidrBlock, + ipv6Pool: this.props.ipv6Pool, + cidrBlockName: this.props?.cidrBlockName, + }; + } +} + /** * Interface to create L2 for VPC Cidr Block */ @@ -658,6 +721,16 @@ export interface IVPCCidrBlock { * IPAM pool for IPv4 address type */ readonly ipv4IpamPoolId ?: string; + + /** + * The IPv6 CIDR block from the specified IPv6 address pool. + */ + readonly ipv6CidrBlock?: string; + + /** + * The ID of the IPv6 address pool from which to allocate the IPv6 CIDR block. + */ + readonly ipv6Pool?: string; } /** @@ -721,6 +794,20 @@ export interface VPCCidrBlockattributes { * @default - no IPAM IPv4 CIDR range is provisioned using IPAM */ readonly ipv4IpamProvisionedCidrs?: string[]; + + /** + * The IPv6 CIDR block from the specified IPv6 address pool. + * + * @default - No IPv6 CIDR block associated with VPC. + */ + readonly ipv6CidrBlock?: string; + + /** + * The ID of the IPv6 address pool from which to allocate the IPv6 CIDR block. + * Note: BYOIP Pool ID is different than IPAM Pool ID. + * @default - No BYOIP pool associated with VPC. + */ + readonly ipv6Pool?: string; } /** @@ -748,6 +835,9 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock { public readonly amazonProvidedIpv6CidrBlock ?: boolean = props.amazonProvidedIpv6CidrBlock; public readonly ipv6IpamPoolId ?: string = props.ipv6IpamPoolId; public readonly ipv4IpamPoolId ?: string = props.ipv4IpamPoolId; + //BYOIP Pool Attributes + public readonly ipv6Pool?: string = props.ipv6Pool; + public readonly ipv6CidrBlock?: string = props.ipv6CidrBlock; } return new Import(scope, id); } @@ -762,6 +852,10 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock { public readonly ipv4IpamPoolId?: string; + public readonly ipv6CidrBlock?: string; + + public readonly ipv6Pool?: string; + constructor(scope: Construct, id: string, props: VPCCidrBlockProps) { super(scope, id); this.resource = new CfnVPCCidrBlock(this, id, props); @@ -770,6 +864,9 @@ class VPCCidrBlock extends Resource implements IVPCCidrBlock { this.ipv6IpamPoolId = props.ipv6IpamPoolId; this.ipv4IpamPoolId = props.ipv4IpamPoolId; this.amazonProvidedIpv6CidrBlock = props.amazonProvidedIpv6CidrBlock; + //BYOIP Pool and CIDR Block + this.ipv6CidrBlock = props.ipv6CidrBlock; + this.ipv6Pool = props.ipv6Pool; } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out new file mode 100644 index 0000000000000..91e1a8b9901d5 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"39.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json new file mode 100644 index 0000000000000..1953228ca8e7a --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "39.0.0", + "testCases": { + "integtest-model/DefaultTest": { + "stacks": [ + "vpc-byoip-ipv6" + ], + "assertionStack": "integtest-model/DefaultTest/DeployAssert", + "assertionStackName": "integtestmodelDefaultTestDeployAssertCF40BD53" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json new file mode 100644 index 0000000000000..928ed61bb88d0 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "integtestmodelDefaultTestDeployAssertCF40BD53.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/integtestmodelDefaultTestDeployAssertCF40BD53.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "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." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json new file mode 100644 index 0000000000000..cbc7813bcae89 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/manifest.json @@ -0,0 +1,155 @@ +{ + "version": "39.0.0", + "artifacts": { + "vpc-byoip-ipv6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "vpc-byoip-ipv6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "vpc-byoip-ipv6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "vpc-byoip-ipv6.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/2ac973cd3fa2ed1a3934fbe78ee77203b26df0366d34997e96d1570af96f2614.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "vpc-byoip-ipv6.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "vpc-byoip-ipv6.assets" + ], + "metadata": { + "/vpc-byoip-ipv6/VPC-integ-test-1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCintegtest1EBA1CB75" + } + ], + "/vpc-byoip-ipv6/VPC-integ-test-1/MyByoipIpv6Block/MyByoipIpv6Block": [ + { + "type": "aws:cdk:logicalId", + "data": "VPCintegtest1MyByoipIpv6BlockC474849A" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-1/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest1Subnet33DEE36B" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-1/RouteTable/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest1RouteTableD7246BB7" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-1/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest1RouteTableAssociation25E3C72D" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-2/Subnet": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest2Subnet5A56D422" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-2/RouteTable/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest2RouteTable2528D856" + } + ], + "/vpc-byoip-ipv6/Subnet-integ-test-2/RouteTableAssociation": [ + { + "type": "aws:cdk:logicalId", + "data": "Subnetintegtest2RouteTableAssociation9569A2C6" + } + ], + "/vpc-byoip-ipv6/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/vpc-byoip-ipv6/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "vpc-byoip-ipv6" + }, + "integtestmodelDefaultTestDeployAssertCF40BD53.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integtestmodelDefaultTestDeployAssertCF40BD53.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integtestmodelDefaultTestDeployAssertCF40BD53": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integtestmodelDefaultTestDeployAssertCF40BD53.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integtestmodelDefaultTestDeployAssertCF40BD53.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integtestmodelDefaultTestDeployAssertCF40BD53.assets" + ], + "metadata": { + "/integtest-model/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integtest-model/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "integtest-model/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json new file mode 100644 index 0000000000000..47b5abdeda2b3 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/tree.json @@ -0,0 +1,361 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "vpc-byoip-ipv6": { + "id": "vpc-byoip-ipv6", + "path": "vpc-byoip-ipv6", + "children": { + "VPC-integ-test-1": { + "id": "VPC-integ-test-1", + "path": "vpc-byoip-ipv6/VPC-integ-test-1", + "children": { + "Resource": { + "id": "Resource", + "path": "vpc-byoip-ipv6/VPC-integ-test-1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.1.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default", + "tags": [ + { + "key": "Name", + "value": "vpc-byoip-ipv6/VPC-integ-test-1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "MyByoipIpv6Block": { + "id": "MyByoipIpv6Block", + "path": "vpc-byoip-ipv6/VPC-integ-test-1/MyByoipIpv6Block", + "children": { + "MyByoipIpv6Block": { + "id": "MyByoipIpv6Block", + "path": "vpc-byoip-ipv6/VPC-integ-test-1/MyByoipIpv6Block/MyByoipIpv6Block", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCCidrBlock", + "aws:cdk:cloudformation:props": { + "ipv6CidrBlock": "2600:f0f0:8::/56", + "ipv6Pool": "ipv6pool-ec2-0a95217e154b65493", + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCCidrBlock", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2", + "version": "0.0.0" + } + }, + "Subnet-integ-test-1": { + "id": "Subnet-integ-test-1", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1", + "children": { + "Subnet": { + "id": "Subnet", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "assignIpv6AddressOnCreation": false, + "availabilityZone": "us-west-2a", + "cidrBlock": "10.1.1.0/24", + "ipv6CidrBlock": "2600:f0f0:8:1::/64", + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/RouteTable", + "children": { + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/RouteTable/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "DefaultCDKRouteTable" + } + ], + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.RouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "vpc-byoip-ipv6/Subnet-integ-test-1/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Fn::GetAtt": [ + "Subnetintegtest1RouteTableD7246BB7", + "RouteTableId" + ] + }, + "subnetId": { + "Ref": "Subnetintegtest1Subnet33DEE36B" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.SubnetV2", + "version": "0.0.0" + } + }, + "Subnet-integ-test-2": { + "id": "Subnet-integ-test-2", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2", + "children": { + "Subnet": { + "id": "Subnet", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/Subnet", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Subnet", + "aws:cdk:cloudformation:props": { + "assignIpv6AddressOnCreation": false, + "availabilityZone": "us-west-2a", + "cidrBlock": "10.1.0.0/24", + "ipv6CidrBlock": "2600:f0f0:8:0::/64", + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnet", + "version": "0.0.0" + } + }, + "Acl": { + "id": "Acl", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/Acl", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/RouteTable", + "children": { + "RouteTable": { + "id": "RouteTable", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/RouteTable/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "tags": [ + { + "key": "Name", + "value": "DefaultCDKRouteTable" + } + ], + "vpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.RouteTable", + "version": "0.0.0" + } + }, + "RouteTableAssociation": { + "id": "RouteTableAssociation", + "path": "vpc-byoip-ipv6/Subnet-integ-test-2/RouteTableAssociation", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::SubnetRouteTableAssociation", + "aws:cdk:cloudformation:props": { + "routeTableId": { + "Fn::GetAtt": [ + "Subnetintegtest2RouteTable2528D856", + "RouteTableId" + ] + }, + "subnetId": { + "Ref": "Subnetintegtest2Subnet5A56D422" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnSubnetRouteTableAssociation", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.SubnetV2", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "vpc-byoip-ipv6/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "vpc-byoip-ipv6/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "integtest-model": { + "id": "integtest-model", + "path": "integtest-model", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "integtest-model/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "integtest-model/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "integtest-model/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integtest-model/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integtest-model/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json new file mode 100644 index 0000000000000..b1aceb6a1dfef --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "39.0.0", + "files": { + "2ac973cd3fa2ed1a3934fbe78ee77203b26df0366d34997e96d1570af96f2614": { + "source": { + "path": "vpc-byoip-ipv6.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "2ac973cd3fa2ed1a3934fbe78ee77203b26df0366d34997e96d1570af96f2614.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json new file mode 100644 index 0000000000000..83856929ba1d8 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.js.snapshot/vpc-byoip-ipv6.template.json @@ -0,0 +1,176 @@ +{ + "Resources": { + "VPCintegtest1EBA1CB75": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.1.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "vpc-byoip-ipv6/VPC-integ-test-1" + } + ] + } + }, + "VPCintegtest1MyByoipIpv6BlockC474849A": { + "Type": "AWS::EC2::VPCCidrBlock", + "Properties": { + "Ipv6CidrBlock": "2600:f0f0:8::/56", + "Ipv6Pool": "ipv6pool-ec2-0a95217e154b65493", + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + } + }, + "Subnetintegtest1Subnet33DEE36B": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AssignIpv6AddressOnCreation": false, + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.1.1.0/24", + "Ipv6CidrBlock": "2600:f0f0:8:1::/64", + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest1RouteTableD7246BB7": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "DefaultCDKRouteTable" + } + ], + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest1RouteTableAssociation25E3C72D": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Fn::GetAtt": [ + "Subnetintegtest1RouteTableD7246BB7", + "RouteTableId" + ] + }, + "SubnetId": { + "Ref": "Subnetintegtest1Subnet33DEE36B" + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest2Subnet5A56D422": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "AssignIpv6AddressOnCreation": false, + "AvailabilityZone": "us-west-2a", + "CidrBlock": "10.1.0.0/24", + "Ipv6CidrBlock": "2600:f0f0:8:0::/64", + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest2RouteTable2528D856": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "DefaultCDKRouteTable" + } + ], + "VpcId": { + "Fn::GetAtt": [ + "VPCintegtest1EBA1CB75", + "VpcId" + ] + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + }, + "Subnetintegtest2RouteTableAssociation9569A2C6": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Fn::GetAtt": [ + "Subnetintegtest2RouteTable2528D856", + "RouteTableId" + ] + }, + "SubnetId": { + "Ref": "Subnetintegtest2Subnet5A56D422" + } + }, + "DependsOn": [ + "VPCintegtest1MyByoipIpv6BlockC474849A" + ] + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "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." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.ts b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.ts new file mode 100644 index 0000000000000..f3e8d90e247e5 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.byoip-ipv6.ts @@ -0,0 +1,52 @@ +/* + * This integration test deploys a VPC that contains a BYOIP IPv6 address. + * The address is owned by the CDK maintainers, who are able to run and + * update the test if need be for future changes. + */ + +import * as vpc_v2 from '../lib/vpc-v2'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import { IpCidr, SubnetV2 } from '../lib'; +import { SubnetType } from 'aws-cdk-lib/aws-ec2'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'vpc-byoip-ipv6'); + +const myVpc = new vpc_v2.VpcV2(stack, 'VPC-integ-test-1', { + primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [ + vpc_v2.IpAddresses.ipv6Pool({ + ipv6Pool: 'ipv6pool-ec2-0a95217e154b65493', + cidrBlockName: 'MyByoipIpv6Block', + ipv6CidrBlock: '2600:f0f0:8::/56', + }), + ], + enableDnsHostnames: true, + enableDnsSupport: true, +}); + +new SubnetV2(stack, 'Subnet-integ-test-1', { + vpc: myVpc, + ipv4CidrBlock: new IpCidr('10.1.1.0/24'), + ipv6CidrBlock: new IpCidr('2600:f0f0:8:1::/64'), + availabilityZone: 'us-west-2a', + subnetType: SubnetType.PRIVATE_ISOLATED, +}); + +/** + * Check for non-ovelapping subnet range + */ +new SubnetV2(stack, 'Subnet-integ-test-2', { + vpc: myVpc, + ipv4CidrBlock: new IpCidr('10.1.0.0/24'), + ipv6CidrBlock: new IpCidr('2600:f0f0:8:0::/64'), + availabilityZone: 'us-west-2a', + subnetType: SubnetType.PRIVATE_ISOLATED, +}); + +new IntegTest(app, 'integtest-model', { + testCases: [stack], +}); + diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts index 6c3087ce42a77..12376739aa831 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/vpc-v2.test.ts @@ -256,5 +256,53 @@ describe('Vpc V2 with full control', () => { }, }); }); -}); + test('VPC with secondary IPv6 Pool address', () => { + new vpc.VpcV2(stack, 'TestVpc', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv6Pool({ + cidrBlockName: 'SecondaryIPv6', + ipv6Pool: 'SecondaryIPv6Pool', + ipv6CidrBlock: '2001:db8::/32', + })], + enableDnsHostnames: true, + enableDnsSupport: true, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPCCidrBlock', { + VpcId: { + 'Fn::GetAtt': [ + 'TestVpcE77CE678', + 'VpcId', + ], + }, + Ipv6CidrBlock: '2001:db8::/32', + Ipv6Pool: 'SecondaryIPv6Pool', + }); + }); + + test('VPC with multiple IPv6 Pool addresses', () => { + // WHEN + new vpc.VpcV2(stack, 'TestVpc', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), + secondaryAddressBlocks: [ + vpc.IpAddresses.ipv6Pool({ + cidrBlockName: 'SecondaryIPv6One', + ipv6Pool: 'Pool1', + ipv6CidrBlock: '2001:db8:1::/48', + }), + vpc.IpAddresses.ipv6Pool({ + cidrBlockName: 'SecondaryIPv6Two', + ipv6Pool: 'Pool2', + ipv6CidrBlock: '2001:db8:2::/48', + }), + ], + enableDnsHostnames: true, + enableDnsSupport: true, + }); + + // THEN + const template = Template.fromStack(stack); + template.resourceCountIs('AWS::EC2::VPCCidrBlock', 2); + }); +});