From e1195f93f9ee12b86d38bc7a64b2118ba204ef9e Mon Sep 17 00:00:00 2001 From: 1kaileychen Date: Thu, 21 Nov 2024 14:11:16 -0800 Subject: [PATCH] feat(vpcv2): vpc peering connection construct (#31645) ### Issue # (if applicable) Contributes to closing [RFC#507](https://github.com/aws/aws-cdk-rfcs/blob/57fd92a7f20e242b96885264c12567493f5e867f/text/0507-subnets.md?plain=1#L252) Tracking: https://github.com/aws/aws-cdk/issues/30762 ### Reason for this change This PR implements a new L2 construct to setup VPC Peering Connection. ### Description of changes - L2 class(VPCPeeringConnection) - Function `validateVpcCidrOverlap` to ensure IPv4 CIDR blocks don't overlap for subnets in the VPCs ### Description of how you validated changes - Unit tests to test combination of accounts (cross account & cross region, default same account & same region) - Unit tests to see simulate when CIDR blocks overlap - Primary CIDR block overlaps - Secondary CIDR block overlaps - Primary and secondary CIDR block overlaps - Integration test for peering connection ### Checklist - [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* --- packages/@aws-cdk/aws-ec2-alpha/README.md | 151 ++++++ packages/@aws-cdk/aws-ec2-alpha/lib/route.ts | 139 ++++- packages/@aws-cdk/aws-ec2-alpha/lib/util.ts | 3 +- .../@aws-cdk/aws-ec2-alpha/lib/vpc-v2-base.ts | 58 ++- packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts | 2 +- .../aws-ec2-alpha/rosetta/default.ts-fixture | 2 +- ...efaultTestDeployAssertB5B8DCA8.assets.json | 19 + ...aultTestDeployAssertB5B8DCA8.template.json | 36 ++ .../acceptor-stack.assets.json | 20 + .../acceptor-stack.template.json | 170 ++++++ .../cdk.out | 1 + .../integ.json | 13 + .../manifest.json | 218 ++++++++ .../requestor-stack.assets.json | 20 + .../requestor-stack.template.json | 94 ++++ .../tree.json | 490 ++++++++++++++++++ .../test/integ.peering-cross-account.ts | 114 ++++ ...dk-routev2-egressonlyigw-alpha.assets.json | 4 +- .../aws-cdk-routev2-igw-alpha.assets.json | 4 +- ...cdk-routev2-privatenatgw-alpha.assets.json | 4 +- ...-cdk-routev2-publicnatgw-alpha.assets.json | 4 +- ...routev2-virtualprivategw-alpha.assets.json | 4 +- .../integ.route-v2.js.snapshot/manifest.json | 16 +- .../@aws-cdk/aws-ec2-alpha/test/route.test.ts | 128 ++++- .../aws-ec2-alpha/test/vpc-add-method.test.ts | 40 ++ 25 files changed, 1728 insertions(+), 26 deletions(-) create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.assets.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.template.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.ts diff --git a/packages/@aws-cdk/aws-ec2-alpha/README.md b/packages/@aws-cdk/aws-ec2-alpha/README.md index ecedb42ae1189..21d5ff793016b 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/README.md +++ b/packages/@aws-cdk/aws-ec2-alpha/README.md @@ -225,8 +225,159 @@ new Route(this, 'DynamoDBRoute', { destination: '0.0.0.0/0', target: { endpoint: dynamoEndpoint }, }); + +``` + +## VPC Peering Connection + +VPC peering connection allows you to connect two VPCs and route traffic between them using private IP addresses. The VpcV2 construct supports creating VPC peering connections through the `VPCPeeringConnection` construct from the `route` module. + +Peering Connection cannot be established between two VPCs with overlapping CIDR ranges. Please make sure the two VPC CIDRs do not overlap with each other else it will throw an error. + +For more information, see [What is VPC peering?](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html). + +The following show examples of how to create a peering connection between two VPCs for all possible combinations of same-account or cross-account, and same-region or cross-region configurations. + +Note: You cannot create a VPC peering connection between VPCs that have matching or overlapping CIDR blocks + +**Case 1: Same Account and Same Region Peering Connection** + +```ts +const stack = new Stack(); + +const vpcA = new VpcV2(this, 'VpcA', { + primaryAddressBlock: IpAddresses.ipv4('10.0.0.0/16'), +}); + +const vpcB = new VpcV2(this, 'VpcB', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +const peeringConnection = vpcA.createPeeringConnection('sameAccountSameRegionPeering', { + acceptorVpc: vpcB, +}); +``` + +**Case 2: Same Account and Cross Region Peering Connection** + +There is no difference from Case 1 when calling `createPeeringConnection`. The only change is that one of the VPCs are created in another stack with a different region. To establish cross region VPC peering connection, acceptorVpc needs to be imported to the requestor VPC stack using `fromVpcV2Attributes` method. + +```ts +const app = new App(); + +const stackA = new Stack(app, 'VpcStackA', { env: { account: '000000000000', region: 'us-east-1' } }); +const stackB = new Stack(app, 'VpcStackB', { env: { account: '000000000000', region: 'us-west-2' } }); + +const vpcA = new VpcV2(stackA, 'VpcA', { + primaryAddressBlock: IpAddresses.ipv4('10.0.0.0/16'), +}); + +new VpcV2(stackB, 'VpcB', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +const vpcB = VpcV2.fromVpcV2Attributes(stackA, 'ImportedVpcB', { + vpcId: 'MockVpcBid', + vpcCidrBlock: '10.1.0.0/16', + region: 'us-west-2', + ownerAccountId: '000000000000', + }); + + +const peeringConnection = vpcA.createPeeringConnection('sameAccountCrossRegionPeering', { + acceptorVpc: vpcB, +}); ``` +**Case 3: Cross Account Peering Connection** + +For cross-account connections, the acceptor account needs an IAM role that grants the requestor account permission to initiate the connection. Create a new IAM role in the acceptor account using method `createAcceptorVpcRole` to provide the necessary permissions. + +Once role is created in account, provide role arn for field `peerRoleArn` under method `createPeeringConnection` + +```ts +const stack = new Stack(); + +const acceptorVpc = new VpcV2(this, 'VpcA', { + primaryAddressBlock: IpAddresses.ipv4('10.0.0.0/16'), +}); + +const acceptorRoleArn = acceptorVpc.createAcceptorVpcRole('000000000000'); // Requestor account ID +``` + +After creating an IAM role in the acceptor account, we can initiate the peering connection request from the requestor VPC. Import accpeptorVpc to the stack using `fromVpcV2Attributes` method, it is recommended to specify owner account id of the acceptor VPC in case of cross account peering connection, if acceptor VPC is hosted in different region provide region value for import as well. +The following code snippet demonstrates how to set up VPC peering between two VPCs in different AWS accounts using CDK: + +```ts +const stack = new Stack(); + +const acceptorVpc = VpcV2.fromVpcV2Attributes(this, 'acceptorVpc', { + vpcId: 'vpc-XXXX', + vpcCidrBlock: '10.0.0.0/16', + region: 'us-east-2', + ownerAccountId: '111111111111', + }); + +const acceptorRoleArn = 'arn:aws:iam::111111111111:role/VpcPeeringRole'; + +const requestorVpc = new VpcV2(this, 'VpcB', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +const peeringConnection = requestorVpc.createPeeringConnection('crossAccountCrossRegionPeering', { + acceptorVpc: acceptorVpc, + peerRoleArn: acceptorRoleArn, +}); +``` + +### Route Table Configuration + +After establishing the VPC peering connection, routes must be added to the respective route tables in the VPCs to enable traffic flow. If a route is added to the requestor stack, information will be able to flow from the requestor VPC to the acceptor VPC, but not in the reverse direction. For bi-directional communication, routes need to be added in both VPCs from their respective stacks. + +For more information, see [Update your route tables for a VPC peering connection](https://docs.aws.amazon.com/vpc/latest/peering/vpc-peering-routing.html). + +```ts +const stack = new Stack(); + +const acceptorVpc = new VpcV2(this, 'VpcA', { + primaryAddressBlock: IpAddresses.ipv4('10.0.0.0/16'), +}); + +const requestorVpc = new VpcV2(this, 'VpcB', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +const peeringConnection = requestorVpc.createPeeringConnection('peeringConnection', { + acceptorVpc: acceptorVpc, +}); + +const routeTable = new RouteTable(this, 'RouteTable', { + vpc: requestorVpc, +}); + +routeTable.addRoute('vpcPeeringRoute', '10.0.0.0/16', { gateway: peeringConnection }); +``` + +This can also be done using AWS CLI. For more information, see [create-route](https://docs.aws.amazon.com/cli/latest/reference/ec2/create-route.html). + +```bash +# Add a route to the requestor VPC route table +aws ec2 create-route --route-table-id rtb-requestor --destination-cidr-block 10.0.0.0/16 --vpc-peering-connection-id pcx-xxxxxxxx + +# For bi-directional add a route in the acceptor vpc account as well +aws ec2 create-route --route-table-id rtb-acceptor --destination-cidr-block 10.1.0.0/16 --vpc-peering-connection-id pcx-xxxxxxxx +``` + +### Deleting the Peering Connection + +To delete a VPC peering connection, use the following command: + +```bash +aws ec2 delete-vpc-peering-connection --vpc-peering-connection-id pcx-xxxxxxxx +``` + +For more information, see [Delete a VPC peering connection](https://docs.aws.amazon.com/vpc/latest/peering/create-vpc-peering-connection.html#delete-vpc-peering-connection). + ## Adding Egress-Only Internet Gateway to VPC An egress-only internet gateway is a horizontally scaled, redundant, and highly available VPC component that allows outbound communication over IPv6 from instances in your VPC to the internet, and prevents the internet from initiating an IPv6 connection with your instances. diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts index ad098d68cf950..80776a0f29eee 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts @@ -1,8 +1,8 @@ -import { CfnEIP, CfnEgressOnlyInternetGateway, CfnInternetGateway, CfnNatGateway, CfnRoute, CfnRouteTable, CfnVPCGatewayAttachment, CfnVPNGateway, CfnVPNGatewayRoutePropagation, GatewayVpcEndpoint, IRouteTable, IVpcEndpoint, RouterType } from 'aws-cdk-lib/aws-ec2'; +import { CfnEIP, CfnEgressOnlyInternetGateway, CfnInternetGateway, CfnNatGateway, CfnVPCPeeringConnection, CfnRoute, CfnRouteTable, CfnVPCGatewayAttachment, CfnVPNGateway, CfnVPNGatewayRoutePropagation, GatewayVpcEndpoint, IRouteTable, IVpcEndpoint, RouterType } from 'aws-cdk-lib/aws-ec2'; import { Construct, IDependable } from 'constructs'; import { Annotations, Duration, IResource, Resource } from 'aws-cdk-lib/core'; import { IVpcV2, VPNGatewayV2Options } from './vpc-v2-base'; -import { NetworkUtils, allRouteTableIds } from './util'; +import { NetworkUtils, allRouteTableIds, CidrBlock } from './util'; import { ISubnetV2 } from './subnet-v2'; /** @@ -175,6 +175,40 @@ export interface NatGatewayProps extends NatGatewayOptions { readonly vpc?: IVpcV2; } +/** + * Options to define a VPC peering connection. + */ +export interface VPCPeeringConnectionOptions { + /** + * The VPC that is accepting the peering connection. + */ + readonly acceptorVpc: IVpcV2; + + /** + * The role arn created in the acceptor account. + * + * @default - no peerRoleArn needed if not cross account connection + */ + readonly peerRoleArn?: string; + + /** + * The resource name of the peering connection. + * + * @default - peering connection provisioned without any name + */ + readonly vpcPeeringConnectionName?: string; +} + +/** + * Properties to define a VPC peering connection. + */ +export interface VPCPeeringConnectionProps extends VPCPeeringConnectionOptions { + /** + * The VPC that is requesting the peering connection. + */ + readonly requestorVpc: IVpcV2; +} + /** * Creates an egress-only internet gateway * @resource AWS::EC2::EgressOnlyInternetGateway @@ -312,7 +346,7 @@ export class VPNGatewayV2 extends Resource implements IRouteTarget { const routeTableIds = allRouteTableIds(subnets); if (routeTableIds.length === 0) { - Annotations.of(this).addWarningV2('@aws-cdk:aws-ec2-elpha:enableVpnGatewayV2', `No subnets matching selection: '${JSON.stringify(vpnRoutePropagation)}'. Select other subnets to add routes to.`); + Annotations.of(scope).addWarningV2('@aws-cdk:aws-ec2-elpha:enableVpnGatewayV2', `No subnets matching selection: '${JSON.stringify(vpnRoutePropagation)}'. Select other subnets to add routes to.`); } this._routePropagation = new CfnVPNGatewayRoutePropagation(this, 'RoutePropagation', { @@ -402,6 +436,103 @@ export class NatGateway extends Resource implements IRouteTarget { } } +/** + * Creates a peering connection between two VPCs + * @resource AWS::EC2::VPCPeeringConnection + */ +export class VPCPeeringConnection extends Resource implements IRouteTarget { + + /** + * The type of router used in the route. + */ + readonly routerType: RouterType; + + /** + * The ID of the route target. + */ + readonly routerTargetId: string; + + /** + * The VPC peering connection CFN resource. + */ + public readonly resource: CfnVPCPeeringConnection; + + constructor(scope: Construct, id: string, props: VPCPeeringConnectionProps) { + super(scope, id); + + this.routerType = RouterType.VPC_PEERING_CONNECTION; + + const isCrossAccount = props.requestorVpc.ownerAccountId !== props.acceptorVpc.ownerAccountId; + + if (!isCrossAccount && props.peerRoleArn) { + throw new Error('peerRoleArn is not needed for same account peering'); + } + + if (isCrossAccount && !props.peerRoleArn) { + throw new Error('Cross account VPC peering requires peerRoleArn'); + } + + const overlap = this.validateVpcCidrOverlap(props.requestorVpc, props.acceptorVpc); + if (overlap) { + throw new Error('CIDR block should not overlap with each other for establishing a peering connection'); + } + + this.resource = new CfnVPCPeeringConnection(this, 'VPCPeeringConnection', { + vpcId: props.requestorVpc.vpcId, + peerVpcId: props.acceptorVpc.vpcId, + peerOwnerId: props.acceptorVpc.ownerAccountId, + peerRegion: props.acceptorVpc.region, + peerRoleArn: isCrossAccount ? props.peerRoleArn : undefined, + }); + + this.routerTargetId = this.resource.attrId; + this.node.defaultChild = this.resource; + } + + /** + * Validates if the provided IPv4 CIDR block overlaps with existing subnet CIDR blocks within the given VPC. + * + * @param requestorVpc The VPC of the requestor. + * @param acceptorVpc The VPC of the acceptor. + * @returns True if the IPv4 CIDR block overlaps with each other for two VPCs, false otherwise. + * @internal + */ + private validateVpcCidrOverlap(requestorVpc: IVpcV2, acceptorVpc: IVpcV2): boolean { + + const requestorCidrs = [requestorVpc.ipv4CidrBlock]; + const acceptorCidrs = [acceptorVpc.ipv4CidrBlock]; + + if (requestorVpc.secondaryCidrBlock) { + requestorCidrs.push(...requestorVpc.secondaryCidrBlock + .map(block => block.cidrBlock) + .filter((cidr): cidr is string => cidr !== undefined)); + } + + if (acceptorVpc.secondaryCidrBlock) { + acceptorCidrs.push(...acceptorVpc.secondaryCidrBlock + .map(block => block.cidrBlock) + .filter((cidr): cidr is string => cidr !== undefined)); + } + + for (const requestorCidr of requestorCidrs) { + const requestorRange = new CidrBlock(requestorCidr); + const requestorIpRange: [string, string] = [requestorRange.minIp(), requestorRange.maxIp()]; + + for (const acceptorCidr of acceptorCidrs) { + const acceptorRange = new CidrBlock(acceptorCidr); + const acceptorIpRange: [string, string] = [acceptorRange.minIp(), acceptorRange.maxIp()]; + + if (requestorRange.rangesOverlap(acceptorIpRange, requestorIpRange)) { + return true; + } + } + } + + return false; + } + +} + /** * The type of endpoint or gateway being targeted by the route. */ @@ -534,7 +665,7 @@ export class Route extends Resource implements IRouteV2 { /** * The type of router the route is targetting */ - public readonly targetRouterType: RouterType + public readonly targetRouterType: RouterType; /** * The route CFN resource. diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts index ed8ae62342ded..8cbed53f9b919 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/util.ts @@ -378,5 +378,4 @@ export class CidrBlockIpv6 { } return ipv6Number; } -} - +} \ No newline at end of file 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 458b4e8757340..26bde2cb903c8 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 @@ -1,9 +1,10 @@ -import { Resource, Annotations } from 'aws-cdk-lib'; +import { Aws, Resource, Annotations } from 'aws-cdk-lib'; import { IVpc, ISubnet, SubnetSelection, SelectedSubnets, EnableVpnGatewayOptions, VpnGateway, VpnConnectionType, CfnVPCGatewayAttachment, CfnVPNGatewayRoutePropagation, VpnConnectionOptions, VpnConnection, ClientVpnEndpointOptions, ClientVpnEndpoint, InterfaceVpcEndpointOptions, InterfaceVpcEndpoint, GatewayVpcEndpointOptions, GatewayVpcEndpoint, FlowLogOptions, FlowLog, FlowLogResourceType, SubnetType, SubnetFilter } from 'aws-cdk-lib/aws-ec2'; import { allRouteTableIds, flatten, subnetGroupNameFromConstructId } from './util'; import { IDependable, Dependable, IConstruct, DependencyGroup } from 'constructs'; -import { EgressOnlyInternetGateway, InternetGateway, NatConnectivityType, NatGateway, NatGatewayOptions, Route, VPNGatewayV2 } from './route'; +import { EgressOnlyInternetGateway, InternetGateway, NatConnectivityType, NatGateway, NatGatewayOptions, Route, VPCPeeringConnection, VPCPeeringConnectionOptions, VPNGatewayV2 } from './route'; import { ISubnetV2 } from './subnet-v2'; +import { AccountPrincipal, Effect, PolicyStatement, Role } from 'aws-cdk-lib/aws-iam'; import { IVPCCidrBlock } from './vpc-v2'; /** @@ -150,6 +151,19 @@ export interface IVpcV2 extends IVpc { */ addNatGateway(options: NatGatewayOptions): NatGateway; + /** + * Adds a new role to acceptor VPC account + * A cross account role is required for the VPC to peer with another account. + * For more information, see the {@link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/peer-with-vpc-in-another-account.html}. + */ + createAcceptorVpcRole(requestorAccountId: string): Role; + + /** + * Creates a new peering connection + * A peering connection is a private virtual network established between two VPCs. + * For more information, see the {@link https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html}. + */ + createPeeringConnection(id: string, options: VPCPeeringConnectionOptions): VPCPeeringConnection; } /** @@ -487,6 +501,46 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { }); } + /** + * Creates peering connection role for acceptor VPC + */ + public createAcceptorVpcRole(requestorAccountId: string): Role { + const peeringRole = new Role(this, 'VpcPeeringRole', { + assumedBy: new AccountPrincipal(requestorAccountId), + roleName: 'VpcPeeringRole', + description: 'Restrictive role for VPC peering', + }); + + peeringRole.addToPolicy(new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['ec2:AcceptVpcPeeringConnection'], + resources: [`arn:${Aws.PARTITION}:ec2:${this.region}:${this.ownerAccountId}:vpc/${this.vpcId}`], + })); + + peeringRole.addToPolicy(new PolicyStatement({ + actions: ['ec2:AcceptVpcPeeringConnection'], + effect: Effect.ALLOW, + resources: [`arn:${Aws.PARTITION}:ec2:${this.region}:${this.ownerAccountId}:vpc-peering-connection/*`], + conditions: { + StringEquals: { + 'ec2:AccepterVpc': `arn:${Aws.PARTITION}:ec2:${this.region}:${this.ownerAccountId}:vpc/${this.vpcId}`, + }, + }, + })); + + return peeringRole; + } + + /** + * Creates a peering connection + */ + public createPeeringConnection(id: string, options: VPCPeeringConnectionOptions): VPCPeeringConnection { + return new VPCPeeringConnection(this, id, { + requestorVpc: this, + ...options, + }); + } + /** * Returns the id of the VPN Gateway (if enabled) */ 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 e004593c70bdb..841bdcb6d8a63 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/vpc-v2.ts @@ -397,7 +397,7 @@ export class VpcV2 extends VpcV2Base { public readonly publicSubnets: ISubnet[]; /** - * Pbulic Subnets that are part of this VPC. + * Public Subnets that are part of this VPC. */ public readonly privateSubnets: ISubnet[]; diff --git a/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture index 9643806d2973b..d48ffb9f1330b 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-ec2-alpha/rosetta/default.ts-fixture @@ -1,7 +1,7 @@ // Fixture with packages imported, but nothing else import { Construct } from 'constructs'; import { Stack, App, Fn } from 'aws-cdk-lib'; -import { VpcV2, SubnetV2, IpAddresses, IpamPoolPublicIpSource, RouteTable, InternetGateway, Route, NatGateway, EgressOnlyInternetGateway } from '@aws-cdk/aws-ec2-alpha'; +import { VpcV2, SubnetV2, IpAddresses, IpamPoolPublicIpSource, RouteTable, InternetGateway, Route, NatGateway, EgressOnlyInternetGateway, VPCPeeringConnection } from '@aws-cdk/aws-ec2-alpha'; import { Ipam, AwsServiceName, IpCidr, AddressFamily } from '@aws-cdk/aws-ec2-alpha'; import { NatConnectivityType } from '@aws-cdk/aws-ec2-alpha'; import { SubnetType, VpnConnectionType } from 'aws-cdk-lib/aws-ec2'; diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets.json new file mode 100644 index 0000000000000..2e0c890253c27 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets.json @@ -0,0 +1,19 @@ +{ + "version": "38.0.1", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.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.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.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.peering-cross-account.js.snapshot/acceptor-stack.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.assets.json new file mode 100644 index 0000000000000..a1d772336ff2e --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.assets.json @@ -0,0 +1,20 @@ +{ + "version": "38.0.1", + "files": { + "34c1052a8eb4080f81a8ce579f7e552334e70d6019ef73deaf914358f9d679d1": { + "source": { + "path": "acceptor-stack.template.json", + "packaging": "file" + }, + "destinations": { + "234567890123-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-234567890123-us-east-1", + "objectKey": "34c1052a8eb4080f81a8ce579f7e552334e70d6019ef73deaf914358f9d679d1.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::234567890123:role/cdk-hnb659fds-file-publishing-role-234567890123-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.template.json new file mode 100644 index 0000000000000..72217fdffef18 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/acceptor-stack.template.json @@ -0,0 +1,170 @@ +{ + "Resources": { + "acceptorVpc5B7D1670": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default" + } + }, + "acceptorVpcVpcPeeringRoleF389E47A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::12345678:root" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Restrictive role for VPC peering", + "RoleName": "VpcPeeringRole" + } + }, + "acceptorVpcVpcPeeringRoleDefaultPolicyE79C72D0": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:AcceptVpcPeeringConnection", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:234567890123:vpc/", + { + "Fn::GetAtt": [ + "acceptorVpc5B7D1670", + "VpcId" + ] + } + ] + ] + } + }, + { + "Action": "ec2:AcceptVpcPeeringConnection", + "Condition": { + "StringEquals": { + "ec2:AccepterVpc": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:234567890123:vpc/", + { + "Fn::GetAtt": [ + "acceptorVpc5B7D1670", + "VpcId" + ] + } + ] + ] + } + } + }, + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:234567890123:vpc-peering-connection/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "acceptorVpcVpcPeeringRoleDefaultPolicyE79C72D0", + "Roles": [ + { + "Ref": "acceptorVpcVpcPeeringRoleF389E47A" + } + ] + } + }, + "requestorVpcSameAccountF27E91F7": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.1.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default" + } + }, + "requestorVpcSameAccountsameAccountPeeringVPCPeeringConnection4E07C8CD": { + "Type": "AWS::EC2::VPCPeeringConnection", + "Properties": { + "PeerOwnerId": "234567890123", + "PeerRegion": "us-east-1", + "PeerVpcId": { + "Fn::GetAtt": [ + "acceptorVpc5B7D1670", + "VpcId" + ] + }, + "VpcId": { + "Fn::GetAtt": [ + "requestorVpcSameAccountF27E91F7", + "VpcId" + ] + } + } + } + }, + "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.peering-cross-account.js.snapshot/cdk.out b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/cdk.out new file mode 100644 index 0000000000000..c6e612584e352 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/integ.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/integ.json new file mode 100644 index 0000000000000..e513fb54e9323 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/integ.json @@ -0,0 +1,13 @@ +{ + "version": "38.0.1", + "testCases": { + "VpcpcCrossAccountInteg/DefaultTest": { + "stacks": [ + "acceptor-stack", + "requestor-stack" + ], + "assertionStack": "VpcpcCrossAccountInteg/DefaultTest/DeployAssert", + "assertionStackName": "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/manifest.json new file mode 100644 index 0000000000000..67e4520ec29e0 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/manifest.json @@ -0,0 +1,218 @@ +{ + "version": "38.0.1", + "artifacts": { + "acceptor-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "acceptor-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "acceptor-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://234567890123/us-east-1", + "properties": { + "templateFile": "acceptor-stack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::234567890123:role/cdk-hnb659fds-deploy-role-234567890123-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::234567890123:role/cdk-hnb659fds-cfn-exec-role-234567890123-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-234567890123-us-east-1/34c1052a8eb4080f81a8ce579f7e552334e70d6019ef73deaf914358f9d679d1.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "acceptor-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::234567890123:role/cdk-hnb659fds-lookup-role-234567890123-us-east-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "acceptor-stack.assets" + ], + "metadata": { + "/acceptor-stack/acceptorVpc/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "acceptorVpc5B7D1670" + } + ], + "/acceptor-stack/acceptorVpc/VpcPeeringRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "acceptorVpcVpcPeeringRoleF389E47A" + } + ], + "/acceptor-stack/acceptorVpc/VpcPeeringRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "acceptorVpcVpcPeeringRoleDefaultPolicyE79C72D0" + } + ], + "/acceptor-stack/requestorVpcSameAccount/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "requestorVpcSameAccountF27E91F7" + } + ], + "/acceptor-stack/requestorVpcSameAccount/sameAccountPeering/VPCPeeringConnection": [ + { + "type": "aws:cdk:logicalId", + "data": "requestorVpcSameAccountsameAccountPeeringVPCPeeringConnection4E07C8CD", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" + ] + } + ], + "/acceptor-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/acceptor-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "acceptor-stack" + }, + "requestor-stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "requestor-stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "requestor-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://12345678/us-east-1", + "properties": { + "templateFile": "requestor-stack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-deploy-role-12345678-us-east-1", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-cfn-exec-role-12345678-us-east-1", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/a3dc1b1e845e0078e800b7fcd60d3634a2ab75ac0074794c0344338afc77b16f.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "requestor-stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-lookup-role-12345678-us-east-1", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "requestor-stack.assets" + ], + "metadata": { + "/requestor-stack/requestorVpcCrossAccount/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "requestorVpcCrossAccount6372A252" + } + ], + "/requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering/VPCPeeringConnection": [ + { + "type": "aws:cdk:logicalId", + "data": "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" + ] + } + ], + "/requestor-stack/RouteTable/RouteTable": [ + { + "type": "aws:cdk:logicalId", + "data": "RouteTableE1378006" + } + ], + "/requestor-stack/RouteTable/vpcPeeringRoute/Route": [ + { + "type": "aws:cdk:logicalId", + "data": "RouteTablevpcPeeringRoute2C93DAB3" + } + ], + "/requestor-stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/requestor-stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "requestor-stack" + }, + "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "notificationArns": [], + "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": [ + "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.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": [ + "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8.assets" + ], + "metadata": { + "/VpcpcCrossAccountInteg/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/VpcpcCrossAccountInteg/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "VpcpcCrossAccountInteg/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.peering-cross-account.js.snapshot/requestor-stack.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.assets.json new file mode 100644 index 0000000000000..1aa5990e5adb9 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.assets.json @@ -0,0 +1,20 @@ +{ + "version": "38.0.1", + "files": { + "a3dc1b1e845e0078e800b7fcd60d3634a2ab75ac0074794c0344338afc77b16f": { + "source": { + "path": "requestor-stack.template.json", + "packaging": "file" + }, + "destinations": { + "12345678-us-east-1": { + "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", + "objectKey": "a3dc1b1e845e0078e800b7fcd60d3634a2ab75ac0074794c0344338afc77b16f.json", + "region": "us-east-1", + "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.template.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.template.json new file mode 100644 index 0000000000000..b975fceab7bb4 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/requestor-stack.template.json @@ -0,0 +1,94 @@ +{ + "Resources": { + "requestorVpcCrossAccount6372A252": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.2.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default" + } + }, + "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0": { + "Type": "AWS::EC2::VPCPeeringConnection", + "Properties": { + "PeerOwnerId": "234567890123", + "PeerRegion": "us-east-1", + "PeerRoleArn": "arn:aws:iam::916743627080:role/VpcPeeringRole", + "PeerVpcId": "vpc-09b9235d8a3195ba3", + "VpcId": { + "Fn::GetAtt": [ + "requestorVpcCrossAccount6372A252", + "VpcId" + ] + } + } + }, + "RouteTableE1378006": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Fn::GetAtt": [ + "requestorVpcCrossAccount6372A252", + "VpcId" + ] + } + } + }, + "RouteTablevpcPeeringRoute2C93DAB3": { + "Type": "AWS::EC2::Route", + "Properties": { + "DestinationCidrBlock": "10.0.0.0/16", + "RouteTableId": { + "Fn::GetAtt": [ + "RouteTableE1378006", + "RouteTableId" + ] + }, + "VpcPeeringConnectionId": { + "Fn::GetAtt": [ + "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0", + "Id" + ] + } + }, + "DependsOn": [ + "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0" + ] + } + }, + "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.peering-cross-account.js.snapshot/tree.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/tree.json new file mode 100644 index 0000000000000..cfd1493b0ca60 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.js.snapshot/tree.json @@ -0,0 +1,490 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "acceptor-stack": { + "id": "acceptor-stack", + "path": "acceptor-stack", + "children": { + "acceptorVpc": { + "id": "acceptorVpc", + "path": "acceptor-stack/acceptorVpc", + "children": { + "Resource": { + "id": "Resource", + "path": "acceptor-stack/acceptorVpc/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.0.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "VpcPeeringRole": { + "id": "VpcPeeringRole", + "path": "acceptor-stack/acceptorVpc/VpcPeeringRole", + "children": { + "ImportVpcPeeringRole": { + "id": "ImportVpcPeeringRole", + "path": "acceptor-stack/acceptorVpc/VpcPeeringRole/ImportVpcPeeringRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "acceptor-stack/acceptorVpc/VpcPeeringRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::12345678:root" + } + } + ], + "Version": "2012-10-17" + }, + "description": "Restrictive role for VPC peering", + "roleName": "VpcPeeringRole" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "acceptor-stack/acceptorVpc/VpcPeeringRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "acceptor-stack/acceptorVpc/VpcPeeringRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "ec2:AcceptVpcPeeringConnection", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:234567890123:vpc/", + { + "Fn::GetAtt": [ + "acceptorVpc5B7D1670", + "VpcId" + ] + } + ] + ] + } + }, + { + "Action": "ec2:AcceptVpcPeeringConnection", + "Condition": { + "StringEquals": { + "ec2:AccepterVpc": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:234567890123:vpc/", + { + "Fn::GetAtt": [ + "acceptorVpc5B7D1670", + "VpcId" + ] + } + ] + ] + } + } + }, + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:234567890123:vpc-peering-connection/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "acceptorVpcVpcPeeringRoleDefaultPolicyE79C72D0", + "roles": [ + { + "Ref": "acceptorVpcVpcPeeringRoleF389E47A" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2", + "version": "0.0.0" + } + }, + "requestorVpcSameAccount": { + "id": "requestorVpcSameAccount", + "path": "acceptor-stack/requestorVpcSameAccount", + "children": { + "Resource": { + "id": "Resource", + "path": "acceptor-stack/requestorVpcSameAccount/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" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "sameAccountPeering": { + "id": "sameAccountPeering", + "path": "acceptor-stack/requestorVpcSameAccount/sameAccountPeering", + "children": { + "VPCPeeringConnection": { + "id": "VPCPeeringConnection", + "path": "acceptor-stack/requestorVpcSameAccount/sameAccountPeering/VPCPeeringConnection", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCPeeringConnection", + "aws:cdk:cloudformation:props": { + "peerOwnerId": "234567890123", + "peerRegion": "us-east-1", + "peerVpcId": { + "Fn::GetAtt": [ + "acceptorVpc5B7D1670", + "VpcId" + ] + }, + "vpcId": { + "Fn::GetAtt": [ + "requestorVpcSameAccountF27E91F7", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCPeeringConnection", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VPCPeeringConnection", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "acceptor-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "acceptor-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "requestor-stack": { + "id": "requestor-stack", + "path": "requestor-stack", + "children": { + "acceptorVpc": { + "id": "acceptorVpc", + "path": "requestor-stack/acceptorVpc", + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2Base", + "version": "0.0.0" + } + }, + "requestorVpcCrossAccount": { + "id": "requestorVpcCrossAccount", + "path": "requestor-stack/requestorVpcCrossAccount", + "children": { + "Resource": { + "id": "Resource", + "path": "requestor-stack/requestorVpcCrossAccount/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPC", + "aws:cdk:cloudformation:props": { + "cidrBlock": "10.2.0.0/16", + "enableDnsHostnames": true, + "enableDnsSupport": true, + "instanceTenancy": "default" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPC", + "version": "0.0.0" + } + }, + "acceptorAccountCrossRegionPeering": { + "id": "acceptorAccountCrossRegionPeering", + "path": "requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering", + "children": { + "VPCPeeringConnection": { + "id": "VPCPeeringConnection", + "path": "requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering/VPCPeeringConnection", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::VPCPeeringConnection", + "aws:cdk:cloudformation:props": { + "peerOwnerId": "234567890123", + "peerRegion": "us-east-1", + "peerRoleArn": "arn:aws:iam::916743627080:role/VpcPeeringRole", + "peerVpcId": "vpc-09b9235d8a3195ba3", + "vpcId": { + "Fn::GetAtt": [ + "requestorVpcCrossAccount6372A252", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnVPCPeeringConnection", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VPCPeeringConnection", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.VpcV2", + "version": "0.0.0" + } + }, + "RouteTable": { + "id": "RouteTable", + "path": "requestor-stack/RouteTable", + "children": { + "RouteTable": { + "id": "RouteTable", + "path": "requestor-stack/RouteTable/RouteTable", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::RouteTable", + "aws:cdk:cloudformation:props": { + "vpcId": { + "Fn::GetAtt": [ + "requestorVpcCrossAccount6372A252", + "VpcId" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRouteTable", + "version": "0.0.0" + } + }, + "vpcPeeringRoute": { + "id": "vpcPeeringRoute", + "path": "requestor-stack/RouteTable/vpcPeeringRoute", + "children": { + "Route": { + "id": "Route", + "path": "requestor-stack/RouteTable/vpcPeeringRoute/Route", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::EC2::Route", + "aws:cdk:cloudformation:props": { + "destinationCidrBlock": "10.0.0.0/16", + "routeTableId": { + "Fn::GetAtt": [ + "RouteTableE1378006", + "RouteTableId" + ] + }, + "vpcPeeringConnectionId": { + "Fn::GetAtt": [ + "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0", + "Id" + ] + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_ec2.CfnRoute", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.Route", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-ec2-alpha.RouteTable", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "requestor-stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "requestor-stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "VpcpcCrossAccountInteg": { + "id": "VpcpcCrossAccountInteg", + "path": "VpcpcCrossAccountInteg", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "VpcpcCrossAccountInteg/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "VpcpcCrossAccountInteg/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.4.2" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "VpcpcCrossAccountInteg/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "VpcpcCrossAccountInteg/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "VpcpcCrossAccountInteg/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.peering-cross-account.ts b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.ts new file mode 100644 index 0000000000000..413c327a366c8 --- /dev/null +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.peering-cross-account.ts @@ -0,0 +1,114 @@ +/* + * Our integration tests act as snapshot tests to make sure the rendered template is stable. + * If any changes to the result are required, + * you need to perform an actual CloudFormation deployment of this application, + * and, if it is successful, a new snapshot will be written out. + * + * For more information on CDK integ tests, + * see the main CONTRIBUTING.md file. + * + * Notes on how to run this integ test + * Replace 123456789012 and 234567890123 with your own account numbers + * + * 1. Configure Accounts + * a. Requestor Account (123456789012) should be bootstrapped for us-east-1 + * b. Acceptor Account (234567890123) should be bootstrapped for us-east-1 + * and needs to set trust permissions for requestor account (123456789012) + * - `cdk bootstrap --trust 123456789012 --cloudformation-execution-policies 'arn:aws:iam::aws:policy/AdministratorAccess' 'aws://234567890123/us-east-1'` + * - assuming this is the default profile for aws credentials + * + * 2. Set environment variables + * a. `export CDK_INTEG_ACCOUNT=123456789012` //Requestor Account + * b. `export CDK_INTEG_CROSS_ACCOUNT=234567890123` //Acceptor Account + * + * 3. Run the integ test (from the @aws-cdk/aws-ec2-alpha/test directory)with no clean flag + * a. Get temporary console access credentials for Requestor Account + * - `yarn integ test/integ.vpcpc.js --no-clean` + * b. Fall back if temp credentials do not work (account info may be in snapshot) + * - `yarn integ test/integ.vpcpc.js --profiles cross-account` + * Note: Integration test will fail since vpcId of acceptor stack is a dummy value + * + * 4. Modify acceptorVpcId to actual physical Id and rerun the integration test to + * test cross account peering + * - `yarn integ test/integ.vpcpc.js` +*/ + +import * as vpc_v2 from '../lib/vpc-v2'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { RouteTable } from '../lib/route'; + +const app = new cdk.App(); +const account = process.env.CDK_INTEG_ACCOUNT || '123456789012'; +const acceptorAccount = process.env.CDK_INTEG_CROSS_ACCOUNT || '234567890123'; + +class AcceptorStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const acceptorVpc = new vpc_v2.VpcV2(this, 'acceptorVpc', { + primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.0.0.0/16'), + }); + + //Same account VPC peering + const requestorVpc = new vpc_v2.VpcV2(this, 'requestorVpcSameAccount', { + primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.1.0.0/16'), + }); + + requestorVpc.createPeeringConnection('sameAccountPeering', { + acceptorVpc: acceptorVpc, + }); + + //For cross-account peering connection + acceptorVpc.createAcceptorVpcRole(account); + } +} + +class RequestorStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + //Import acceptorVpc into the requestor stack, change vpcId after vpc is created using acceptorStack definition + const acceptorVpc = vpc_v2.VpcV2.fromVpcV2Attributes(this, 'acceptorVpc', { + //Replace VPC Id before running integ test again + vpcId: 'vpc-09b9235d8a3195ba3', + vpcCidrBlock: '10.0.0.0/16', + region: 'us-east-1', + ownerAccountId: acceptorAccount, + }); + + const requestorVpc = new vpc_v2.VpcV2(this, 'requestorVpcCrossAccount', { + primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.2.0.0/16'), + }); + + const peeringConnection = requestorVpc.createPeeringConnection('acceptorAccountCrossRegionPeering', { + acceptorVpc: acceptorVpc, + peerRoleArn: 'arn:aws:iam::916743627080:role/VpcPeeringRole', + }); + + const routeTable = new RouteTable(this, 'RouteTable', { + vpc: requestorVpc, + }); + + routeTable.addRoute('vpcPeeringRoute', '10.0.0.0/16', { gateway: peeringConnection }); + } +} + +const acceptorStack = new AcceptorStack(app, 'acceptor-stack', { + env: { + account: acceptorAccount, + region: 'us-east-1', + }, +}); + +const requestorStack = new RequestorStack(app, 'requestor-stack', { + env: { + account: account, + region: 'us-east-1', + }, +}); + +new IntegTest(app, 'VpcpcCrossAccountInteg', { + testCases: [acceptorStack, requestorStack], +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-egressonlyigw-alpha.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-egressonlyigw-alpha.assets.json index e9f134adee307..c1c2915c2db8f 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-egressonlyigw-alpha.assets.json +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-egressonlyigw-alpha.assets.json @@ -1,7 +1,7 @@ { "version": "38.0.1", "files": { - "31af8a4b3ed5c39cae0d40f9fa6de2f21f6b3f857e16ba4dfabf0e32bdbb0b22": { + "59eea98c04eb81e61ad96cf12d5ad30981ba8827c0a6f9329042aaf3b6ab25ae": { "source": { "path": "aws-cdk-routev2-egressonlyigw-alpha.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "31af8a4b3ed5c39cae0d40f9fa6de2f21f6b3f857e16ba4dfabf0e32bdbb0b22.json", + "objectKey": "59eea98c04eb81e61ad96cf12d5ad30981ba8827c0a6f9329042aaf3b6ab25ae.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-igw-alpha.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-igw-alpha.assets.json index fa6d8fb7bcca2..a48f00526bfc3 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-igw-alpha.assets.json +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-igw-alpha.assets.json @@ -1,7 +1,7 @@ { "version": "38.0.1", "files": { - "768f53ce08170ccd9ae866aaa8526583c1a031cd5a89c8b6cf6cc0719801a995": { + "26ace3dfd222102fc0437541ab4b4c0eb03fcd95da132842d7546d8bb568cdd5": { "source": { "path": "aws-cdk-routev2-igw-alpha.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "768f53ce08170ccd9ae866aaa8526583c1a031cd5a89c8b6cf6cc0719801a995.json", + "objectKey": "26ace3dfd222102fc0437541ab4b4c0eb03fcd95da132842d7546d8bb568cdd5.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-privatenatgw-alpha.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-privatenatgw-alpha.assets.json index 4fc8d880ad0a8..22f5a39f5640f 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-privatenatgw-alpha.assets.json +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-privatenatgw-alpha.assets.json @@ -1,7 +1,7 @@ { "version": "38.0.1", "files": { - "f59a6c688cc139a40850f502b2626fce81567794eb14b38691220b8ee189b3cb": { + "39e23e50b17229259428c78d506b45da6f2681804d01ba216b26facaf35f69d6": { "source": { "path": "aws-cdk-routev2-privatenatgw-alpha.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "f59a6c688cc139a40850f502b2626fce81567794eb14b38691220b8ee189b3cb.json", + "objectKey": "39e23e50b17229259428c78d506b45da6f2681804d01ba216b26facaf35f69d6.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-publicnatgw-alpha.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-publicnatgw-alpha.assets.json index a39748fdb79c1..0a7794b4fb281 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-publicnatgw-alpha.assets.json +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-publicnatgw-alpha.assets.json @@ -1,7 +1,7 @@ { "version": "38.0.1", "files": { - "3cd2d2327728a3b0d353ffb3df39f530a2c61b89027ace480938237617938cc3": { + "895d9a465fd31edd1eaadb22929fc8cf042ad912d272ea411632a6c9f820aba9": { "source": { "path": "aws-cdk-routev2-publicnatgw-alpha.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "3cd2d2327728a3b0d353ffb3df39f530a2c61b89027ace480938237617938cc3.json", + "objectKey": "895d9a465fd31edd1eaadb22929fc8cf042ad912d272ea411632a6c9f820aba9.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-virtualprivategw-alpha.assets.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-virtualprivategw-alpha.assets.json index c9fa66db80d1b..345f3924a3e83 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-virtualprivategw-alpha.assets.json +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/aws-cdk-routev2-virtualprivategw-alpha.assets.json @@ -1,7 +1,7 @@ { "version": "38.0.1", "files": { - "81a032b4432db12bf0035622e7573c9546888d62dc9d5b5f380d5ecb10a7aeca": { + "6d2e4cfbc5e7bfd23a115ae098e231aa3f8f68fc66cf9834642b374c23457ca7": { "source": { "path": "aws-cdk-routev2-virtualprivategw-alpha.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "81a032b4432db12bf0035622e7573c9546888d62dc9d5b5f380d5ecb10a7aeca.json", + "objectKey": "6d2e4cfbc5e7bfd23a115ae098e231aa3f8f68fc66cf9834642b374c23457ca7.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/manifest.json b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/manifest.json index 636378bebe6ef..6cc8b46bf443b 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/manifest.json +++ b/packages/@aws-cdk/aws-ec2-alpha/test/integ.route-v2.js.snapshot/manifest.json @@ -99,7 +99,7 @@ "notificationArns": [], "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}/31af8a4b3ed5c39cae0d40f9fa6de2f21f6b3f857e16ba4dfabf0e32bdbb0b22.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/59eea98c04eb81e61ad96cf12d5ad30981ba8827c0a6f9329042aaf3b6ab25ae.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -191,7 +191,7 @@ "notificationArns": [], "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}/768f53ce08170ccd9ae866aaa8526583c1a031cd5a89c8b6cf6cc0719801a995.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/26ace3dfd222102fc0437541ab4b4c0eb03fcd95da132842d7546d8bb568cdd5.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -289,7 +289,7 @@ "notificationArns": [], "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}/81a032b4432db12bf0035622e7573c9546888d62dc9d5b5f380d5ecb10a7aeca.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6d2e4cfbc5e7bfd23a115ae098e231aa3f8f68fc66cf9834642b374c23457ca7.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -306,6 +306,12 @@ "aws-cdk-routev2-virtualprivategw-alpha.assets" ], "metadata": { + "/aws-cdk-routev2-virtualprivategw-alpha": [ + { + "type": "aws:cdk:warning", + "data": "No subnets matching selection: '[]'. Select other subnets to add routes to. [ack: @aws-cdk:aws-ec2-elpha:enableVpnGatewayV2]" + } + ], "/aws-cdk-routev2-virtualprivategw-alpha/vpgw/Resource": [ { "type": "aws:cdk:logicalId", @@ -399,7 +405,7 @@ "notificationArns": [], "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}/3cd2d2327728a3b0d353ffb3df39f530a2c61b89027ace480938237617938cc3.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/895d9a465fd31edd1eaadb22929fc8cf042ad912d272ea411632a6c9f820aba9.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -515,7 +521,7 @@ "notificationArns": [], "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}/f59a6c688cc139a40850f502b2626fce81567794eb14b38691220b8ee189b3cb.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/39e23e50b17229259428c78d506b45da6f2681804d01ba216b26facaf35f69d6.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/route.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/route.test.ts index 6504622ae4611..dfaa1f68caa8d 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/route.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/route.test.ts @@ -512,5 +512,131 @@ describe('EC2 Routing', () => { }, }); }); - }); + +describe('VPCPeeringConnection', () => { + + let stackA: cdk.Stack; + let stackB: cdk.Stack; + let stackC: cdk.Stack; + + let vpcA: vpc.VpcV2; + let vpcB: vpc.VpcV2; + let vpcC: vpc.VpcV2; + + beforeEach(() => { + const app = new cdk.App({ + context: { + '@aws-cdk/core:newStyleStackSynthesis': false, + }, + }); + + stackA = new cdk.Stack(app, 'VpcStackA', { env: { account: '234567890123', region: 'us-east-1' } }); + stackB = new cdk.Stack(app, 'VpcStackB', { env: { account: '123456789012', region: 'us-east-1' } }); + stackC = new cdk.Stack(app, 'VpcStackC', { env: { account: '123456789012', region: 'us-west-2' }, crossRegionReferences: true }); + + vpcA = new vpc.VpcV2(stackA, 'VpcA', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + secondaryAddressBlocks: [vpc.IpAddresses.ipv4('10.1.0.0/16', { cidrBlockName: 'TempSecondaryBlock' })], + }); + vpcB = new vpc.VpcV2(stackB, 'VpcB', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.2.0.0/16'), + }); + vpcC = new vpc.VpcV2(stackC, 'VpcC', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), + }); + + }); + + test('Creates a cross account VPC peering connection', () => { + + const importedVpcB = vpc.VpcV2.fromVpcV2Attributes(stackA, 'VpcB', { + vpcId: 'mockVpcBId', //cross account stack references are not supported + vpcCidrBlock: '10.2.0.0/16', + region: vpcB.env.region, + ownerAccountId: '123456789012', + }); + + new route.VPCPeeringConnection(stackA, 'TestPeeringConnection', { + requestorVpc: vpcA, + acceptorVpc: importedVpcB, + peerRoleArn: 'arn:aws:iam::012345678910:role/VpcPeeringRole', + }); + const template = Template.fromStack(stackA); + template.hasResourceProperties('AWS::EC2::VPCPeeringConnection', { + PeerRoleArn: 'arn:aws:iam::012345678910:role/VpcPeeringRole', + VpcId: { + 'Fn::GetAtt': ['VpcAAD85CA4C', 'VpcId'], + }, + PeerVpcId: 'mockVpcBId', + PeerOwnerId: '123456789012', + PeerRegion: 'us-east-1', + }); + }); + + test('Creates a cross region VPC peering connection', () => { + + const importedVpcC = vpc.VpcV2.fromVpcV2Attributes(stackA, 'VpcB', { + vpcId: 'mockVpcCId', //cross account stack references are not supported + vpcCidrBlock: '10.3.0.0/16', + region: vpcC.env.region, + ownerAccountId: '123456789012', + }); + + new route.VPCPeeringConnection(stackB, 'TestCrossRegionPeeringConnection', { + requestorVpc: vpcB, + acceptorVpc: importedVpcC, + }); + + Template.fromStack(stackB).hasResourceProperties('AWS::EC2::VPCPeeringConnection', { + VpcId: { + 'Fn::GetAtt': ['VpcB98A08B07', 'VpcId'], + }, + PeerVpcId: 'mockVpcCId', + PeerOwnerId: '123456789012', + PeerRegion: 'us-west-2', + }); + }); + + test('Throws error when peerRoleArn is not provided for cross-account peering', () => { + expect(() => { + new route.VPCPeeringConnection(stackA, 'TestCrossAccountPeeringConnection', { + requestorVpc: vpcA, + acceptorVpc: vpcB, + }); + }).toThrow(/Cross account VPC peering requires peerRoleArn/); + }); + + test('Throws error when peerRoleArn is provided for same account peering', () => { + expect(() => { + new route.VPCPeeringConnection(stackB, 'TestPeeringConnection', { + requestorVpc: vpcB, + acceptorVpc: vpcC, + peerRoleArn: 'arn:aws:iam::123456789012:role/unnecessary-role', + }); + }).toThrow(/peerRoleArn is not needed for same account peering/); + }); + + test('CIDR block overlap with secondary CIDR block should throw error', () => { + expect(() => { + new route.VPCPeeringConnection(stackA, 'TestPeering', { + requestorVpc: vpcA, + acceptorVpc: vpcC, + peerRoleArn: 'arn:aws:iam::012345678910:role/VpcPeeringRole', + }); + }).toThrow(/CIDR block should not overlap with each other for establishing a peering connection/); + }); + + test('CIDR block overlap with primary CIDR block should throw error', () => { + const vpcD = new vpc.VpcV2(stackA, 'VpcD', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + }); + + expect(() => { + new route.VPCPeeringConnection(stackA, 'TestPeering', { + requestorVpc: vpcA, + acceptorVpc: vpcD, + }); + }).toThrow(/CIDR block should not overlap with each other for establishing a peering connection/); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2-alpha/test/vpc-add-method.test.ts b/packages/@aws-cdk/aws-ec2-alpha/test/vpc-add-method.test.ts index 9ac84c7646237..26400da6bc87b 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/vpc-add-method.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/vpc-add-method.test.ts @@ -398,4 +398,44 @@ describe('Vpc V2 with full control', () => { }).toThrow('The VPN Gateway has already been enabled.'); }); + test('createAcceptorVpcRole creates a restricted role', () => { + myVpc.createAcceptorVpcRole('123456789012'); + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + AWS: { 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':iam::123456789012:root']] }, + }, + }, + ], + Version: '2012-10-17', + }, + }); + }); + + test('createPeeringConnection establishes connection between 2 VPCs', () => { + const acceptorVpc = new vpc.VpcV2(stack, 'TestAcceptorVpc', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), + enableDnsHostnames: true, + enableDnsSupport: true, + }); + + myVpc.createPeeringConnection('testPeeringConnection', { + acceptorVpc: acceptorVpc, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::EC2::VPCPeeringConnection', { + VpcId: { + 'Fn::GetAtt': ['TestVpcE77CE678', 'VpcId'], + }, + PeerVpcId: { + 'Fn::GetAtt': ['TestAcceptorVpc4AE3E611', 'VpcId'], + }, + PeerOwnerId: { Ref: 'AWS::AccountId' }, + PeerRegion: { Ref: 'AWS::Region' }, + }); + }); }); \ No newline at end of file