diff --git a/packages/@aws-cdk/aws-ec2-alpha/README.md b/packages/@aws-cdk/aws-ec2-alpha/README.md index ab7423f5cc055..f71a2d9439bc4 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/README.md +++ b/packages/@aws-cdk/aws-ec2-alpha/README.md @@ -330,7 +330,7 @@ const peeringConnection = vpcA.createPeeringConnection('sameAccountCrossRegionPe 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` +Once the role is created in the acceptor account, provide the role object for the `peerRole` field under method `createPeeringConnection`: ```ts const stack = new Stack(); @@ -339,13 +339,15 @@ const acceptorVpc = new VpcV2(this, 'VpcA', { primaryAddressBlock: IpAddresses.ipv4('10.0.0.0/16'), }); -const acceptorRoleArn = acceptorVpc.createAcceptorVpcRole('000000000000'); // Requestor account ID +const acceptorRole = 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 acceptorVpc 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 +import * as iam from 'aws-cdk-lib/aws-iam'; + const stack = new Stack(); const acceptorVpc = VpcV2.fromVpcV2Attributes(this, 'acceptorVpc', { @@ -355,7 +357,10 @@ const acceptorVpc = VpcV2.fromVpcV2Attributes(this, 'acceptorVpc', { ownerAccountId: '111111111111', }); -const acceptorRoleArn = 'arn:aws:iam::111111111111:role/VpcPeeringRole'; +// Import the role created in the acceptor account +// Note: The actual role name will be auto-generated by CDK +const acceptorRole = iam.Role.fromRoleArn(this, 'AcceptorRole', + 'arn:aws:iam::111111111111:role/acceptor-stack-acceptorVpcVpcPeeringRole12345678-ABCDEFGHIJKL'); const requestorVpc = new VpcV2(this, 'VpcB', { primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), @@ -363,8 +368,80 @@ const requestorVpc = new VpcV2(this, 'VpcB', { const peeringConnection = requestorVpc.createPeeringConnection('crossAccountCrossRegionPeering', { acceptorVpc: acceptorVpc, - peerRoleArn: acceptorRoleArn, + peerRole: acceptorRole, +}); +``` + +### Importing Existing VPC Peering Connections + +You can import an existing VPC peering connection using the `fromAttributes` static method. This is useful when you need to reference a peering connection that was created outside of your CDK stack for routing purposes: + +```ts +const myVpc = new VpcV2(this, 'MyVpc', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +const existingPeeringConnection = VPCPeeringConnection.fromAttributes(this, 'ExistingPeering', { + vpcPeeringConnectionId: 'pcx-12345678', +}); + +// Use the imported peering connection as a route target +const routeTable = new RouteTable(this, 'RouteTable', { + vpc: myVpc, +}); + +routeTable.addRoute('PeeringRoute', '10.0.0.0/16', { gateway: existingPeeringConnection }); +``` + +### Creating Requestor-Side Peering Roles + +For cross-account peering scenarios, you can also create a role on the requestor side using the `createRequestorPeerRole` method. This is useful when you need to set up the role infrastructure from the requestor account: + +```ts +const requestorVpc = new VpcV2(this, 'RequestorVpc', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +// Create a role that can be assumed by the acceptor account +const requestorRole = requestorVpc.createRequestorPeerRole('111111111111'); // Acceptor account ID +``` + +### Complete Cross-Account Peering Setup + +Here's a complete example showing both sides of a cross-account peering connection: + +```ts +// 1. In acceptor account - create acceptor role and VPC +const acceptorVpc = new VpcV2(this, 'AcceptorVpc', { + primaryAddressBlock: IpAddresses.ipv4('10.0.0.0/16'), }); + +const acceptorRole = acceptorVpc.createAcceptorVpcRole('123456789012'); // Requestor account ID + +// 2. In requestor account - create requestor role and peering connection +const requestorVpc = new VpcV2(this, 'RequestorVpc', { + primaryAddressBlock: IpAddresses.ipv4('10.1.0.0/16'), +}); + +// Import the acceptor VPC and role +const importedAcceptorVpc = VpcV2.fromVpcV2Attributes(this, 'ImportedAcceptorVpc', { + vpcId: 'vpc-acceptor123', + vpcCidrBlock: '10.0.0.0/16', + region: 'us-east-1', + ownerAccountId: '111111111111', +}); + +const importedAcceptorRole = iam.Role.fromRoleArn(this, 'ImportedAcceptorRole', + 'arn:aws:iam::111111111111:role/VpcPeeringRole'); + +// Create the peering connection +const peeringConnection = requestorVpc.createPeeringConnection('CrossAccountPeering', { + acceptorVpc: importedAcceptorVpc, + peerRole: importedAcceptorRole, +}); + +// 3. The acceptor account must then accept the peering connection +// This is done outside of CDK using AWS CLI or console ``` ### Route Table Configuration diff --git a/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts b/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts index 5385411baf0c5..763d8c025abba 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/lib/route.ts @@ -1,4 +1,5 @@ import { CfnEIP, CfnEgressOnlyInternetGateway, CfnInternetGateway, CfnNatGateway, CfnVPCPeeringConnection, CfnRoute, CfnRouteTable, CfnVPCGatewayAttachment, CfnVPNGateway, CfnVPNGatewayRoutePropagation, GatewayVpcEndpoint, IRouteTable, IVpcEndpoint, RouterType } from 'aws-cdk-lib/aws-ec2'; +import { IRole } from 'aws-cdk-lib/aws-iam'; import { Construct, IDependable } from 'constructs'; import { Annotations, Duration, FeatureFlags, IResource, Resource, Tags, ValidationError } from 'aws-cdk-lib/core'; import { IVpcV2, VPNGatewayV2Options } from './vpc-v2-base'; @@ -188,11 +189,11 @@ export interface VPCPeeringConnectionOptions { readonly acceptorVpc: IVpcV2; /** - * The role arn created in the acceptor account. + * The role created in the acceptor account for cross-account peering. * - * @default - no peerRoleArn needed if not cross account connection + * @default - no peerRole needed if not cross account connection */ - readonly peerRoleArn?: string; + readonly peerRole?: IRole; /** * The resource name of the peering connection. @@ -202,6 +203,20 @@ export interface VPCPeeringConnectionOptions { readonly vpcPeeringConnectionName?: string; } +/** + * Attributes to import an existing VPC peering connection. + */ +export interface VpcPeeringConnectionAttributes { + /** + * The ID of the VPC peering connection. + * + * Must be a valid VPC peering connection ID starting with 'pcx-'. + * + * @example 'pcx-12345678' + */ + readonly vpcPeeringConnectionId: string; +} + /** * Properties to define a VPC peering connection. */ @@ -509,6 +524,39 @@ export class NatGateway extends Resource implements IRouteTarget { export class VPCPeeringConnection extends Resource implements IRouteTarget { /** Uniquely identifies this class. */ public static readonly PROPERTY_INJECTION_ID: string = '@aws-cdk.aws-ec2-alpha.VPCPeeringConnection'; + + /** + * Import an existing VPC peering connection. + * + * @param scope The scope in which to define this construct + * @param id The scoped construct ID + * @param attrs The attributes of the VPC peering connection to import + * @returns An imported VPC peering connection that can be used as a route target + */ + public static fromAttributes(scope: Construct, id: string, attrs: VpcPeeringConnectionAttributes): IRouteTarget { + /** + * Internal class to allow users to import VPC peering connection + * @internal + */ + class ImportedVPCPeeringConnection extends Resource implements IRouteTarget { + public readonly routerType: RouterType = RouterType.VPC_PEERING_CONNECTION; + public readonly routerTargetId: string; + + constructor(construct: Construct, constructId: string, vpcPeeringConnectionId: string) { + super(construct, constructId); + + // Add validation + if (!vpcPeeringConnectionId || !vpcPeeringConnectionId.startsWith('pcx-')) { + throw new Error('VPC Peering Connection ID must be a valid ID starting with "pcx-"'); + } + + this.routerTargetId = vpcPeeringConnectionId; + } + } + + return new ImportedVPCPeeringConnection(scope, id, attrs.vpcPeeringConnectionId); + } + /** * The type of router used in the route. */ @@ -533,12 +581,12 @@ export class VPCPeeringConnection extends Resource implements IRouteTarget { const isCrossAccount = props.requestorVpc.ownerAccountId !== props.acceptorVpc.ownerAccountId; - if (!isCrossAccount && props.peerRoleArn) { - throw new ValidationError('peerRoleArn is not needed for same account peering', this); + if (!isCrossAccount && props.peerRole) { + throw new ValidationError('peerRole is not needed for same account peering', this); } - if (isCrossAccount && !props.peerRoleArn) { - throw new ValidationError('Cross account VPC peering requires peerRoleArn', this); + if (isCrossAccount && !props.peerRole) { + throw new ValidationError('Cross account VPC peering requires peerRole. Use createAcceptorVpcRole() or createRequestorPeerRole() to create the required role.', this); } const overlap = this.validateVpcCidrOverlap(props.requestorVpc, props.acceptorVpc); @@ -554,7 +602,7 @@ export class VPCPeeringConnection extends Resource implements IRouteTarget { peerVpcId: props.acceptorVpc.vpcId, peerOwnerId: props.acceptorVpc.ownerAccountId, peerRegion: props.acceptorVpc.region, - peerRoleArn: isCrossAccount ? props.peerRoleArn : undefined, + peerRoleArn: isCrossAccount ? props.peerRole?.roleArn : undefined, }); this.routerTargetId = this.resource.attrId; 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 cd361285acff3..741cd7c6c65f2 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 @@ -71,7 +71,7 @@ export interface EgressOnlyInternetGatewayOptions { /** * Options to define InternetGateway for VPC */ -export interface InternetGatewayOptions{ +export interface InternetGatewayOptions { /** * Destination Ipv6 address for EGW route @@ -400,9 +400,9 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { routeTableIds, vpnGatewayId: this._vpnGatewayId, }); - // The AWS::EC2::VPNGatewayRoutePropagation resource cannot use the VPN gateway - // until it has successfully attached to the VPC. - // See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpn-gatewayrouteprop.html + // The AWS::EC2::VPNGatewayRoutePropagation resource cannot use the VPN gateway + // until it has successfully attached to the VPC. + // See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpn-gatewayrouteprop.html routePropagation.node.addDependency(attachment); } @@ -481,7 +481,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.ipv6CidrBlock !== undefined)); + secondaryAddress.ipv6IpamPoolId !== undefined || secondaryAddress.ipv6CidrBlock !== undefined)); } if (!useIpv6) { @@ -618,12 +618,16 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { } /** - * Creates peering connection role for acceptor VPC + * Creates peering connection role for acceptor VPC. + * + * The role name will be auto-generated by CDK to ensure uniqueness. + * + * @param requestorAccountId The AWS account ID that will assume this role + * @returns The created IAM role that can be used for cross-account VPC peering */ public createAcceptorVpcRole(requestorAccountId: string): Role { const peeringRole = new Role(this, 'VpcPeeringRole', { assumedBy: new AccountPrincipal(requestorAccountId), - roleName: 'VpcPeeringRole', description: 'Restrictive role for VPC peering', }); @@ -647,6 +651,50 @@ export abstract class VpcV2Base extends Resource implements IVpcV2 { return peeringRole; } + /** + * Creates peering connection role for requestor VPC. + * + * This role allows the acceptor account to create a VPC peering connection + * with this VPC. The role should be assumed by the acceptor account when + * initiating the peering connection. + * + * The role name will be auto-generated by CDK to ensure uniqueness. + * + * @param acceptorAccountId The AWS account ID that will assume this role + * @returns The created IAM role that can be used for cross-account VPC peering + * + * @example + * ```typescript + * const requestorRole = vpc.createRequestorPeerRole('123456789012'); + * // Share this role ARN with the acceptor account + * ``` + */ + public createRequestorPeerRole(acceptorAccountId: string): Role { + const peeringRole = new Role(this, 'RequestorVpcPeeringRole', { + assumedBy: new AccountPrincipal(acceptorAccountId), + description: 'Restrictive role for VPC peering from requestor side', + }); + + peeringRole.addToPolicy(new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['ec2:CreateVpcPeeringConnection'], + resources: [`arn:${Aws.PARTITION}:ec2:${this.region}:${this.ownerAccountId}:vpc/${this.vpcId}`], + })); + + peeringRole.addToPolicy(new PolicyStatement({ + actions: ['ec2:CreateVpcPeeringConnection'], + effect: Effect.ALLOW, + resources: [`arn:${Aws.PARTITION}:ec2:${this.region}:${this.ownerAccountId}:vpc-peering-connection/*`], + conditions: { + StringEquals: { + 'ec2:RequesterVpc': `arn:${Aws.PARTITION}:ec2:${this.region}:${this.ownerAccountId}:vpc/${this.vpcId}`, + }, + }, + })); + + return peeringRole; + } + /** * Creates a peering connection */ 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 index e5ab34ae6aed6..465ccba008652 100644 --- 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 @@ -1,5 +1,5 @@ { - "version": "41.0.0", + "version": "48.0.0", "files": { "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { "displayName": "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8 Template", @@ -8,7 +8,7 @@ "packaging": "file" }, "destinations": { - "current_account-current_region": { + "current_account-current_region-d8d86b35": { "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}" 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 index c1b666bb8d418..bd70b004363af 100644 --- 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 @@ -1,5 +1,5 @@ { - "version": "41.0.0", + "version": "48.0.0", "files": { "3120b656649ccd47924e75d65f0319b99b945a27aaec53946f0ac80a8e796ff3": { "displayName": "acceptor-stack Template", @@ -8,7 +8,7 @@ "packaging": "file" }, "destinations": { - "234567890123-us-east-1": { + "234567890123-us-east-1-6db216a2": { "bucketName": "cdk-hnb659fds-assets-234567890123-us-east-1", "objectKey": "3120b656649ccd47924e75d65f0319b99b945a27aaec53946f0ac80a8e796ff3.json", "region": "us-east-1", 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 index 188478b55560e..523a9aac37cbf 100644 --- 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 @@ -1 +1 @@ -{"version":"41.0.0"} \ No newline at end of file +{"version":"48.0.0"} \ 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 index f59a8c0f1a9d0..794a9384458a3 100644 --- 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 @@ -1,5 +1,5 @@ { - "version": "41.0.0", + "version": "48.0.0", "testCases": { "VpcpcCrossAccountInteg/DefaultTest": { "stacks": [ @@ -9,5 +9,6 @@ "assertionStack": "VpcpcCrossAccountInteg/DefaultTest/DeployAssert", "assertionStackName": "VpcpcCrossAccountIntegDefaultTestDeployAssertB5B8DCA8" } - } + }, + "minimumCliVersion": "2.1027.0" } \ 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 index ac6ac10f88163..6b35e1125a8c2 100644 --- 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 @@ -1,5 +1,5 @@ { - "version": "42.0.0", + "version": "48.0.0", "artifacts": { "acceptor-stack.assets": { "type": "cdk:asset-manifest", @@ -219,7 +219,7 @@ "validateOnSynth": false, "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/ecaf71f3cdd265ef0c9958f0b5776bc2cb8c30ad708f7ef2ec24a3809686ed13.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-12345678-us-east-1/791ddec7ad7fa3c50b2ed6c24b115177a866ad1c417975f345411c64160b1487.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -247,6 +247,123 @@ "data": "requestorVpcCrossAccount6372A252" } ], + "/requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": { + "assumedBy": { + "principalAccount": "*", + "assumeRoleAction": "*" + }, + "roleName": "*", + "description": "*" + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachInlinePolicy": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPolicy": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addToPrincipalPolicy": [ + {} + ] + } + } + ], + "/requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/ImportRequestorVpcPeeringRole": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + } + ], + "/requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "requestorVpcCrossAccountRequestorVpcPeeringRoleED646990" + } + ], + "/requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/DefaultPolicy": [ + { + "type": "aws:cdk:analytics:construct", + "data": "*" + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "attachToRole": [ + "*" + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + }, + { + "type": "aws:cdk:analytics:method", + "data": { + "addStatements": [ + {} + ] + } + } + ], + "/requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "requestorVpcCrossAccountRequestorVpcPeeringRoleDefaultPolicy1E57A1DA" + } + ], "/requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering": [ { "type": "aws:cdk:analytics:construct", @@ -256,7 +373,10 @@ "/requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering/VPCPeeringConnection": [ { "type": "aws:cdk:logicalId", - "data": "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0" + "data": "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_REPLACE" + ] } ], "/requestor-stack/RouteTable": [ @@ -355,7 +475,485 @@ "properties": { "file": "tree.json" } + }, + "aws-cdk-lib/feature-flag-report": { + "type": "cdk:feature-flag-report", + "properties": { + "module": "aws-cdk-lib", + "flags": { + "@aws-cdk/aws-signer:signingProfileNamePassedToCfn": { + "recommendedValue": true, + "explanation": "Pass signingProfileName to CfnSigningProfile" + }, + "@aws-cdk/core:newStyleStackSynthesis": { + "recommendedValue": true, + "explanation": "Switch to new stack synthesis method which enables CI/CD", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:stackRelativeExports": { + "recommendedValue": true, + "explanation": "Name exports based on the construct paths relative to the stack, rather than the global construct path", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": { + "recommendedValue": true, + "explanation": "Disable implicit openListener when custom security groups are provided" + }, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": { + "recommendedValue": true, + "explanation": "Force lowercasing of RDS Cluster names in CDK", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": { + "recommendedValue": true, + "explanation": "Allow adding/removing multiple UsagePlanKeys independently", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeVersionProps": { + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-lambda:recognizeLayerVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to opt in to the updated logical id calculation for Lambda Version created using the `fn.currentVersion`." + }, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": { + "recommendedValue": true, + "explanation": "Enable this feature flag to have cloudfront distributions use the security policy TLSv1.2_2021 by default.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:checkSecretUsage": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this flag to make it impossible to accidentally use SecretValues in unsafe locations" + }, + "@aws-cdk/core:target-partitions": { + "recommendedValue": [ + "aws", + "aws-cn" + ], + "explanation": "What regions to include in lookup tables of environment agnostic stacks" + }, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": { + "userValue": true, + "recommendedValue": true, + "explanation": "ECS extensions will automatically add an `awslogs` driver if no logging is specified" + }, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names." + }, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": { + "userValue": true, + "recommendedValue": true, + "explanation": "ARN format used by ECS. In the new ARN format, the cluster name is part of the resource ID." + }, + "@aws-cdk/aws-iam:minimizePolicies": { + "userValue": true, + "recommendedValue": true, + "explanation": "Minimize IAM policies by combining Statements" + }, + "@aws-cdk/core:validateSnapshotRemovalPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Error on snapshot removal policies on resources that do not support it." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate key aliases that include the stack name" + }, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature flag to create an S3 bucket policy by default in cases where an AWS service would automatically create the Policy if one does not exist." + }, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict KMS key policy for encrypted Queues a bit more" + }, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make default CloudWatch Role behavior safe for multiple API Gateways in one environment" + }, + "@aws-cdk/core:enablePartitionLiterals": { + "userValue": true, + "recommendedValue": true, + "explanation": "Make ARNs concrete if AWS partition is known" + }, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": { + "userValue": true, + "recommendedValue": true, + "explanation": "Event Rules may only push to encrypted SQS queues in the same account" + }, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": { + "userValue": true, + "recommendedValue": true, + "explanation": "Avoid setting the \"ECS\" deployment controller when adding a circuit breaker" + }, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable this feature to by default create default policy names for imported roles that depend on the stack the role is in." + }, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use S3 Bucket Policy instead of ACLs for Server Access Logging" + }, + "@aws-cdk/aws-route53-patters:useCertificate": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use the official `Certificate` resource instead of `DnsValidatedCertificate`" + }, + "@aws-cdk/customresources:installLatestAwsSdkDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "Whether to install the latest SDK by default in AwsCustomResource" + }, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": { + "userValue": true, + "recommendedValue": true, + "explanation": "Use unique resource name for Database Proxy" + }, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Remove CloudWatch alarms from deployment group" + }, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include authorizer configuration in the calculation of the API deployment logical ID." + }, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": { + "userValue": true, + "recommendedValue": true, + "explanation": "Define user data for a launch template by default when a machine image is provided." + }, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": { + "userValue": true, + "recommendedValue": true, + "explanation": "SecretTargetAttachments uses the ResourcePolicy of the attached Secret." + }, + "@aws-cdk/aws-redshift:columnId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Whether to use an ID to track Redshift column changes" + }, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable AmazonEMRServicePolicy_v2 managed policies" + }, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "Restrict access to the VPC default security group" + }, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a unique id for each RequestValidator added to a method" + }, + "@aws-cdk/aws-kms:aliasNameRef": { + "userValue": true, + "recommendedValue": true, + "explanation": "KMS Alias name and keyArn will have implicit reference to KMS Key" + }, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enable grant methods on Aliases imported by name to use kms:ResourceAliases condition" + }, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": { + "userValue": true, + "recommendedValue": true, + "explanation": "Generate a launch template when creating an AutoScalingGroup" + }, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": { + "userValue": true, + "recommendedValue": true, + "explanation": "Include the stack prefix in the stack name generation process" + }, + "@aws-cdk/aws-efs:denyAnonymousAccess": { + "userValue": true, + "recommendedValue": true, + "explanation": "EFS denies anonymous clients accesses" + }, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables support for Multi-AZ with Standby deployment for opensearch domains" + }, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables aws-lambda-nodejs.Function to use the latest available NodeJs runtime as the default" + }, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, mount targets will have a stable logicalId that is linked to the associated subnet." + }, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a scope of InstanceParameterGroup for AuroraClusterInstance with each parameters will change." + }, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, will always use the arn for identifiers for CfnSourceApiAssociation in the GraphqlApi construct rather than id." + }, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, creating an RDS database cluster from a snapshot will only render credentials for snapshot credentials." + }, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the CodeCommit source action is using the default branch name 'main'." + }, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the logical ID of a Lambda permission for a Lambda action includes an alarm ID." + }, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default value for crossAccountKeys to false." + }, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "Enables Pipeline to set the default pipeline type to V2." + }, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, IAM Policy created from KMS key grant will reduce the resource scope to this key only." + }, + "@aws-cdk/pipelines:reduceAssetRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from PipelineAssetsFileRole trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-eks:nodegroupNameAttribute": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, nodegroupName attribute of the provisioned EKS NodeGroup will not have the cluster name prefix." + }, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default volume type of the EBS volume will be GP3" + }, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, remove default deployment alarm settings" + }, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, the custom resource used for `AwsCustomResource` will configure the `logApiResponseData` property as true by default" + }, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": { + "userValue": false, + "recommendedValue": false, + "explanation": "When enabled, Adding notifications to a bucket in the current stack will not remove notification from imported stack." + }, + "@aws-cdk/aws-stepfunctions-tasks:useNewS3UriParametersForBedrockInvokeModelTask": { + "recommendedValue": true, + "explanation": "When enabled, use new props for S3 URI field in task definition of state machine for bedrock invoke model.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/core:explicitStackTags": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, stack tags need to be assigned explicitly on a Stack." + }, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": { + "userValue": false, + "recommendedValue": false, + "explanation": "When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)**" + }, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)**" + }, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, we will only grant the necessary permissions when users specify cloudwatch log group through logConfiguration" + }, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled will allow you to specify a resource policy per replica, and not copy the source table policy to all replicas" + }, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, initOptions.timeout and resourceSignalTimeout values will be summed together." + }, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, a Lambda authorizer Permission created when using GraphqlApi will be properly scoped with a SourceArn." + }, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the value of property `instanceResourceId` in construct `DatabaseInstanceReadReplica` will be set to the correct value which is `DbiResourceId` instead of currently `DbInstanceArn`" + }, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CFN templates added with `cfn-include` will error if the template contains Resource Update or Create policies with CFN Intrinsics that include non-primitive values." + }, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, both `@aws-sdk` and `@smithy` packages will be excluded from the Lambda Node.js 18.x runtime to prevent version mismatches in bundled applications." + }, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resource of IAM Run Ecs policy generated by SFN EcsRunTask will reference the definition, instead of constructing ARN." + }, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the BastionHost construct will use the latest Amazon Linux 2023 AMI, instead of Amazon Linux 2." + }, + "@aws-cdk/core:aspectStabilization": { + "recommendedValue": true, + "explanation": "When enabled, a stabilization loop will be run when invoking Aspects during synthesis.", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource." + }, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere" + }, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the default behaviour of OIDC provider will reject unauthorized connections" + }, + "@aws-cdk/core:enableAdditionalMetadataCollection": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues." + }, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": { + "userValue": false, + "recommendedValue": false, + "explanation": "[Deprecated] When enabled, Lambda will create new inline policies with AddToRolePolicy instead of adding to the Default Policy Statement" + }, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK will automatically generate a unique role name that is used for s3 object replication." + }, + "@aws-cdk/pipelines:reduceStageRoleTrustScope": { + "recommendedValue": true, + "explanation": "Remove the root account principal from Stage addActions trust policy", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-events:requireEventBusPolicySid": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, grantPutEventsTo() will use resource policies with Statement IDs for service principals." + }, + "@aws-cdk/core:aspectPrioritiesMutating": { + "userValue": true, + "recommendedValue": true, + "explanation": "When set to true, Aspects added by the construct library on your behalf will be given a priority of MUTATING." + }, + "@aws-cdk/aws-dynamodb:retainTableReplica": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, table replica will be default to the removal policy of source table unless specified otherwise." + }, + "@aws-cdk/cognito:logUserPoolClientSecretValue": { + "recommendedValue": false, + "explanation": "When disabled, the value of the user pool client secret will not be logged in the custom resource lambda function logs." + }, + "@aws-cdk/pipelines:reduceCrossAccountActionRoleTrustScope": { + "recommendedValue": true, + "explanation": "When enabled, scopes down the trust policy for the cross-account action role", + "unconfiguredBehavesLike": { + "v2": true + } + }, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the resultWriterV2 property of DistributedMap will be used insted of resultWriter" + }, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": { + "userValue": true, + "recommendedValue": true, + "explanation": "Add an S3 trust policy to a KMS key resource policy for SNS subscriptions." + }, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, the EgressOnlyGateway resource is only created if private subnets are defined in the dual-stack VPC." + }, + "@aws-cdk/aws-ec2-alpha:useResourceIdForVpcV2Migration": { + "recommendedValue": false, + "explanation": "When enabled, use resource IDs for VPC V2 migration" + }, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, setting any combination of options for BlockPublicAccess will automatically set true for any options not defined." + }, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": { + "userValue": true, + "recommendedValue": true, + "explanation": "When enabled, CDK creates and manages loggroup for the lambda function" + } + } + } } }, - "minimumCliVersion": "2.1006.0" + "minimumCliVersion": "2.1027.0" } \ 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 index 9f291a97f06d9..fc9077c006c6a 100644 --- 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 @@ -1,16 +1,16 @@ { - "version": "41.0.0", + "version": "48.0.0", "files": { - "ecaf71f3cdd265ef0c9958f0b5776bc2cb8c30ad708f7ef2ec24a3809686ed13": { + "791ddec7ad7fa3c50b2ed6c24b115177a866ad1c417975f345411c64160b1487": { "displayName": "requestor-stack Template", "source": { "path": "requestor-stack.template.json", "packaging": "file" }, "destinations": { - "12345678-us-east-1": { + "12345678-us-east-1-c4824e0d": { "bucketName": "cdk-hnb659fds-assets-12345678-us-east-1", - "objectKey": "ecaf71f3cdd265ef0c9958f0b5776bc2cb8c30ad708f7ef2ec24a3809686ed13.json", + "objectKey": "791ddec7ad7fa3c50b2ed6c24b115177a866ad1c417975f345411c64160b1487.json", "region": "us-east-1", "assumeRoleArn": "arn:${AWS::Partition}:iam::12345678:role/cdk-hnb659fds-file-publishing-role-12345678-us-east-1" } 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 index fea042fc47c29..40ca142c4759b 100644 --- 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 @@ -15,12 +15,94 @@ ] } }, + "requestorVpcCrossAccountRequestorVpcPeeringRoleED646990": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::234567890123:root" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "Restrictive role for VPC peering from requestor side", + "RoleName": "RequestorVpcPeeringRole", + "Tags": [ + { + "Key": "Name", + "Value": "requestor-stack/requestorVpcCrossAccount" + } + ] + } + }, + "requestorVpcCrossAccountRequestorVpcPeeringRoleDefaultPolicy1E57A1DA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "ec2:CreateVpcPeeringConnection", + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:12345678:vpc-peering-connection/*" + ] + ] + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":ec2:us-east-1:12345678:vpc/", + { + "Fn::GetAtt": [ + "requestorVpcCrossAccount6372A252", + "VpcId" + ] + } + ] + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "requestorVpcCrossAccountRequestorVpcPeeringRoleDefaultPolicy1E57A1DA", + "Roles": [ + { + "Ref": "requestorVpcCrossAccountRequestorVpcPeeringRoleED646990" + } + ] + } + }, "requestorVpcCrossAccountacceptorAccountCrossRegionPeeringVPCPeeringConnection3605B6B0": { "Type": "AWS::EC2::VPCPeeringConnection", "Properties": { "PeerOwnerId": "234567890123", "PeerRegion": "us-east-1", - "PeerRoleArn": "arn:aws:iam::916743627080:role/VpcPeeringRole", + "PeerRoleArn": { + "Fn::GetAtt": [ + "requestorVpcCrossAccountRequestorVpcPeeringRoleED646990", + "Arn" + ] + }, "PeerVpcId": "vpc-09b9235d8a3195ba3", "Tags": [ { 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 index cd3a8074642cf..f148cbbb76449 100644 --- 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 @@ -1 +1 @@ -{"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","tags":[{"key":"Name","value":"acceptor-stack/acceptorVpc"}]}},"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","metadata":["*"]}},"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","tags":[{"key":"Name","value":"acceptor-stack/acceptorVpc"}]}},"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","metadata":["*",{"attachToRole":["*"]},{"attachToRole":["*"]},{"addStatements":[{}]},{"addStatements":[{}]}]}}},"constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"},"roleName":"*","description":"*"},{"addToPolicy":[{}]},{"addToPrincipalPolicy":[{}]},{"attachInlinePolicy":["*"]},{"attachInlinePolicy":["*"]},{"addToPolicy":[{}]},{"addToPrincipalPolicy":[{}]}]}}},"constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2","version":"0.0.0","metadata":["*"]}},"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","tags":[{"key":"Name","value":"acceptor-stack/requestorVpcSameAccount"}]}},"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"]},"tags":[{"key":"Name","value":"acceptor-stack/requestorVpcSameAccount"}],"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","metadata":["*"]}}},"constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2","version":"0.0.0","metadata":["*"]}},"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","metadata":[]}},"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","tags":[{"key":"Name","value":"requestor-stack/requestorVpcCrossAccount"}]}},"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","tags":[{"key":"Name","value":"requestor-stack/requestorVpcCrossAccount"}],"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","metadata":["*"]}}},"constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2","version":"0.0.0","metadata":["*"]}},"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","metadata":["*"]}}},"constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.RouteTable","version":"0.0.0","metadata":["*","*"]}},"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 +{"version":"tree-0.1","tree":{"id":"App","path":"","constructInfo":{"fqn":"aws-cdk-lib.App","version":"0.0.0"},"children":{"acceptor-stack":{"id":"acceptor-stack","path":"acceptor-stack","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"acceptorVpc":{"id":"acceptorVpc","path":"acceptor-stack/acceptorVpc","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2","version":"0.0.0","metadata":["*"]},"children":{"Resource":{"id":"Resource","path":"acceptor-stack/acceptorVpc/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnVPC","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::EC2::VPC","aws:cdk:cloudformation:props":{"cidrBlock":"10.0.0.0/16","enableDnsHostnames":true,"enableDnsSupport":true,"instanceTenancy":"default","tags":[{"key":"Name","value":"acceptor-stack/acceptorVpc"}]}}},"VpcPeeringRole":{"id":"VpcPeeringRole","path":"acceptor-stack/acceptorVpc/VpcPeeringRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"},"roleName":"*","description":"*"},{"addToPolicy":[{}]},{"addToPrincipalPolicy":[{}]},{"attachInlinePolicy":["*"]},{"attachInlinePolicy":["*"]},{"addToPolicy":[{}]},{"addToPrincipalPolicy":[{}]}]},"children":{"ImportVpcPeeringRole":{"id":"ImportVpcPeeringRole","path":"acceptor-stack/acceptorVpc/VpcPeeringRole/ImportVpcPeeringRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":["*"]}},"Resource":{"id":"Resource","path":"acceptor-stack/acceptorVpc/VpcPeeringRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"0.0.0"},"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","tags":[{"key":"Name","value":"acceptor-stack/acceptorVpc"}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"acceptor-stack/acceptorVpc/VpcPeeringRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"0.0.0","metadata":["*",{"attachToRole":["*"]},{"attachToRole":["*"]},{"addStatements":[{}]},{"addStatements":[{}]}]},"children":{"Resource":{"id":"Resource","path":"acceptor-stack/acceptorVpc/VpcPeeringRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"0.0.0"},"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"}]}}}}}}}}},"requestorVpcSameAccount":{"id":"requestorVpcSameAccount","path":"acceptor-stack/requestorVpcSameAccount","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2","version":"0.0.0","metadata":["*"]},"children":{"Resource":{"id":"Resource","path":"acceptor-stack/requestorVpcSameAccount/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnVPC","version":"0.0.0"},"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":"acceptor-stack/requestorVpcSameAccount"}]}}},"sameAccountPeering":{"id":"sameAccountPeering","path":"acceptor-stack/requestorVpcSameAccount/sameAccountPeering","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VPCPeeringConnection","version":"0.0.0","metadata":["*"]},"children":{"VPCPeeringConnection":{"id":"VPCPeeringConnection","path":"acceptor-stack/requestorVpcSameAccount/sameAccountPeering/VPCPeeringConnection","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnVPCPeeringConnection","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::EC2::VPCPeeringConnection","aws:cdk:cloudformation:props":{"peerOwnerId":"234567890123","peerRegion":"us-east-1","peerVpcId":{"Fn::GetAtt":["acceptorVpc5B7D1670","VpcId"]},"tags":[{"key":"Name","value":"acceptor-stack/requestorVpcSameAccount"}],"vpcId":{"Fn::GetAtt":["requestorVpcSameAccountF27E91F7","VpcId"]}}}}}}}},"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"}}}},"requestor-stack":{"id":"requestor-stack","path":"requestor-stack","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"children":{"acceptorVpc":{"id":"acceptorVpc","path":"requestor-stack/acceptorVpc","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2Base","version":"0.0.0","metadata":[]}},"requestorVpcCrossAccount":{"id":"requestorVpcCrossAccount","path":"requestor-stack/requestorVpcCrossAccount","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VpcV2","version":"0.0.0","metadata":["*"]},"children":{"Resource":{"id":"Resource","path":"requestor-stack/requestorVpcCrossAccount/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnVPC","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::EC2::VPC","aws:cdk:cloudformation:props":{"cidrBlock":"10.2.0.0/16","enableDnsHostnames":true,"enableDnsSupport":true,"instanceTenancy":"default","tags":[{"key":"Name","value":"requestor-stack/requestorVpcCrossAccount"}]}}},"RequestorVpcPeeringRole":{"id":"RequestorVpcPeeringRole","path":"requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Role","version":"0.0.0","metadata":[{"assumedBy":{"principalAccount":"*","assumeRoleAction":"*"},"roleName":"*","description":"*"},{"addToPolicy":[{}]},{"addToPrincipalPolicy":[{}]},{"attachInlinePolicy":["*"]},{"attachInlinePolicy":["*"]},{"addToPolicy":[{}]},{"addToPrincipalPolicy":[{}]}]},"children":{"ImportRequestorVpcPeeringRole":{"id":"ImportRequestorVpcPeeringRole","path":"requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/ImportRequestorVpcPeeringRole","constructInfo":{"fqn":"aws-cdk-lib.Resource","version":"0.0.0","metadata":["*"]}},"Resource":{"id":"Resource","path":"requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnRole","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Role","aws:cdk:cloudformation:props":{"assumeRolePolicyDocument":{"Statement":[{"Action":"sts:AssumeRole","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::234567890123:root"}}],"Version":"2012-10-17"},"description":"Restrictive role for VPC peering from requestor side","roleName":"RequestorVpcPeeringRole","tags":[{"key":"Name","value":"requestor-stack/requestorVpcCrossAccount"}]}}},"DefaultPolicy":{"id":"DefaultPolicy","path":"requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/DefaultPolicy","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.Policy","version":"0.0.0","metadata":["*",{"attachToRole":["*"]},{"attachToRole":["*"]},{"addStatements":[{}]},{"addStatements":[{}]}]},"children":{"Resource":{"id":"Resource","path":"requestor-stack/requestorVpcCrossAccount/RequestorVpcPeeringRole/DefaultPolicy/Resource","constructInfo":{"fqn":"aws-cdk-lib.aws_iam.CfnPolicy","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::IAM::Policy","aws:cdk:cloudformation:props":{"policyDocument":{"Statement":[{"Action":"ec2:CreateVpcPeeringConnection","Effect":"Allow","Resource":[{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":ec2:us-east-1:12345678:vpc-peering-connection/*"]]},{"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":ec2:us-east-1:12345678:vpc/",{"Fn::GetAtt":["requestorVpcCrossAccount6372A252","VpcId"]}]]}]}],"Version":"2012-10-17"},"policyName":"requestorVpcCrossAccountRequestorVpcPeeringRoleDefaultPolicy1E57A1DA","roles":[{"Ref":"requestorVpcCrossAccountRequestorVpcPeeringRoleED646990"}]}}}}}}},"acceptorAccountCrossRegionPeering":{"id":"acceptorAccountCrossRegionPeering","path":"requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.VPCPeeringConnection","version":"0.0.0","metadata":["*"]},"children":{"VPCPeeringConnection":{"id":"VPCPeeringConnection","path":"requestor-stack/requestorVpcCrossAccount/acceptorAccountCrossRegionPeering/VPCPeeringConnection","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnVPCPeeringConnection","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::EC2::VPCPeeringConnection","aws:cdk:cloudformation:props":{"peerOwnerId":"234567890123","peerRegion":"us-east-1","peerRoleArn":{"Fn::GetAtt":["requestorVpcCrossAccountRequestorVpcPeeringRoleED646990","Arn"]},"peerVpcId":"vpc-09b9235d8a3195ba3","tags":[{"key":"Name","value":"requestor-stack/requestorVpcCrossAccount"}],"vpcId":{"Fn::GetAtt":["requestorVpcCrossAccount6372A252","VpcId"]}}}}}}}},"RouteTable":{"id":"RouteTable","path":"requestor-stack/RouteTable","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.RouteTable","version":"0.0.0","metadata":["*","*"]},"children":{"RouteTable":{"id":"RouteTable","path":"requestor-stack/RouteTable/RouteTable","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnRouteTable","version":"0.0.0"},"attributes":{"aws:cdk:cloudformation:type":"AWS::EC2::RouteTable","aws:cdk:cloudformation:props":{"vpcId":{"Fn::GetAtt":["requestorVpcCrossAccount6372A252","VpcId"]}}}},"vpcPeeringRoute":{"id":"vpcPeeringRoute","path":"requestor-stack/RouteTable/vpcPeeringRoute","constructInfo":{"fqn":"@aws-cdk/aws-ec2-alpha.Route","version":"0.0.0","metadata":["*"]},"children":{"Route":{"id":"Route","path":"requestor-stack/RouteTable/vpcPeeringRoute/Route","constructInfo":{"fqn":"aws-cdk-lib.aws_ec2.CfnRoute","version":"0.0.0"},"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"]}}}}}}}},"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"}}}},"VpcpcCrossAccountInteg":{"id":"VpcpcCrossAccountInteg","path":"VpcpcCrossAccountInteg","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTest","version":"0.0.0"},"children":{"DefaultTest":{"id":"DefaultTest","path":"VpcpcCrossAccountInteg/DefaultTest","constructInfo":{"fqn":"@aws-cdk/integ-tests-alpha.IntegTestCase","version":"0.0.0"},"children":{"Default":{"id":"Default","path":"VpcpcCrossAccountInteg/DefaultTest/Default","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}},"DeployAssert":{"id":"DeployAssert","path":"VpcpcCrossAccountInteg/DefaultTest/DeployAssert","constructInfo":{"fqn":"aws-cdk-lib.Stack","version":"0.0.0"},"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"}}}}}}}},"Tree":{"id":"Tree","path":"Tree","constructInfo":{"fqn":"constructs.Construct","version":"10.4.2"}}}}} \ 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 index f6efb1ff733b0..952c7a3cc7d4c 100644 --- 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 @@ -82,9 +82,12 @@ class RequestorStack extends cdk.Stack { primaryAddressBlock: vpc_v2.IpAddresses.ipv4('10.2.0.0/16'), }); + // Create a role using the new createRequestorPeerRole method + const peerRole = requestorVpc.createRequestorPeerRole(acceptorAccount); + const peeringConnection = requestorVpc.createPeeringConnection('acceptorAccountCrossRegionPeering', { acceptorVpc: acceptorVpc, - peerRoleArn: 'arn:aws:iam::916743627080:role/VpcPeeringRole', + peerRole: peerRole, }); const routeTable = new RouteTable(this, 'RouteTable', { 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 3d1e65fcef868..b60428a68bfe6 100644 --- a/packages/@aws-cdk/aws-ec2-alpha/test/route.test.ts +++ b/packages/@aws-cdk/aws-ec2-alpha/test/route.test.ts @@ -1,9 +1,10 @@ import * as cdk from 'aws-cdk-lib'; import * as vpc from '../lib/vpc-v2'; import * as subnet from '../lib/subnet-v2'; -import { CfnEIP, GatewayVpcEndpoint, GatewayVpcEndpointAwsService, SubnetType, VpnConnectionType } from 'aws-cdk-lib/aws-ec2'; +import { CfnEIP, GatewayVpcEndpoint, GatewayVpcEndpointAwsService, SubnetType, VpnConnectionType, RouterType } from 'aws-cdk-lib/aws-ec2'; import * as route from '../lib/route'; import { Match, Template } from 'aws-cdk-lib/assertions'; +import { Role, AccountPrincipal } from 'aws-cdk-lib/aws-iam'; describe('EC2 Routing', () => { let stack: cdk.Stack; @@ -630,14 +631,21 @@ describe('VPCPeeringConnection', () => { ownerAccountId: '123456789012', }); + const peerRole = new Role(stackA, 'PeerRole', { + assumedBy: new AccountPrincipal('123456789012'), + roleName: 'VpcPeeringRole', + }); + new route.VPCPeeringConnection(stackA, 'TestPeeringConnection', { requestorVpc: vpcA, acceptorVpc: importedVpcB, - peerRoleArn: 'arn:aws:iam::012345678910:role/VpcPeeringRole', + peerRole: peerRole, }); const template = Template.fromStack(stackA); template.hasResourceProperties('AWS::EC2::VPCPeeringConnection', { - PeerRoleArn: 'arn:aws:iam::012345678910:role/VpcPeeringRole', + PeerRoleArn: { + 'Fn::GetAtt': [Match.stringLikeRegexp('PeerRole.*'), 'Arn'], + }, VpcId: { 'Fn::GetAtt': ['VpcAAD85CA4C', 'VpcId'], }, @@ -670,35 +678,66 @@ describe('VPCPeeringConnection', () => { }); }); - test('Throws error when peerRoleArn is not provided for cross-account peering', () => { + test('Throws error when peerRole is not provided for cross-account peering', () => { expect(() => { new route.VPCPeeringConnection(stackA, 'TestCrossAccountPeeringConnection', { requestorVpc: vpcA, acceptorVpc: vpcB, }); - }).toThrow(/Cross account VPC peering requires peerRoleArn/); + }).toThrow(/Cross account VPC peering requires peerRole. Use createAcceptorVpcRole\(\) or createRequestorPeerRole\(\) to create the required role./); }); - test('Throws error when peerRoleArn is provided for same account peering', () => { + test('Throws error when peerRole is provided for same account peering', () => { + const unnecessaryRole = new Role(stackB, 'UnnecessaryRole', { + assumedBy: new AccountPrincipal('123456789012'), + }); + expect(() => { new route.VPCPeeringConnection(stackB, 'TestPeeringConnection', { requestorVpc: vpcB, acceptorVpc: vpcC, - peerRoleArn: 'arn:aws:iam::123456789012:role/unnecessary-role', + peerRole: unnecessaryRole, }); - }).toThrow(/peerRoleArn is not needed for same account peering/); + }).toThrow(/peerRole is not needed for same account peering/); }); test('CIDR block overlap with secondary CIDR block should throw error', () => { + // Create a VPC in the same account as vpcC with overlapping CIDR + const vpcOverlap = new vpc.VpcV2(stackC, 'VpcOverlap', { + primaryAddressBlock: vpc.IpAddresses.ipv4('10.1.0.0/16'), // This overlaps with vpcA's secondary CIDR + }); + expect(() => { - new route.VPCPeeringConnection(stackA, 'TestPeering', { - requestorVpc: vpcA, - acceptorVpc: vpcC, - peerRoleArn: 'arn:aws:iam::012345678910:role/VpcPeeringRole', + new route.VPCPeeringConnection(stackC, 'TestPeering', { + requestorVpc: vpcC, + acceptorVpc: vpcOverlap, }); }).toThrow(/CIDR block should not overlap with each other for establishing a peering connection/); }); + test('fromAttributes creates imported VPC peering connection', () => { + const importedConnection = route.VPCPeeringConnection.fromAttributes(stackA, 'ImportedConnection', { + vpcPeeringConnectionId: 'pcx-12345678', + }); + + expect(importedConnection.routerType).toBe(RouterType.VPC_PEERING_CONNECTION); + expect(importedConnection.routerTargetId).toBe('pcx-12345678'); + }); + + test('fromAttributes validates VPC peering connection ID format', () => { + expect(() => { + route.VPCPeeringConnection.fromAttributes(stackA, 'InvalidConnection', { + vpcPeeringConnectionId: 'invalid-id', + }); + }).toThrow('VPC Peering Connection ID must be a valid ID starting with "pcx-"'); + + expect(() => { + route.VPCPeeringConnection.fromAttributes(stackA, 'EmptyConnection', { + vpcPeeringConnectionId: '', + }); + }).toThrow('VPC Peering Connection ID must be a valid ID starting with "pcx-"'); + }); + test('CIDR block overlap with primary CIDR block should throw error', () => { const testVpc = new vpc.VpcV2(stackA, 'TestVpc', { primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'), 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 7b67511d773de..0090d9cea2ce2 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 @@ -523,6 +523,59 @@ describe('Vpc V2 with full control', () => { }); }); + test('createRequestorPeerRole creates a restricted role for requestor side', () => { + myVpc.createRequestorPeerRole('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', + }, + + }); + + // Verify that no hardcoded role name is set (CDK auto-generates unique names) + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', Match.not({ + RoleName: Match.anyValue(), + })); + + // Verify the policy has proper conditions for security + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'ec2:CreateVpcPeeringConnection', + Effect: 'Allow', + Resource: { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':ec2:', { Ref: 'AWS::Region' }, ':', { Ref: 'AWS::AccountId' }, ':vpc/', { 'Fn::GetAtt': [Match.stringLikeRegexp('TestVpc.*'), 'VpcId'] }]], + }, + }, + { + Action: 'ec2:CreateVpcPeeringConnection', + Effect: 'Allow', + Resource: { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':ec2:', { Ref: 'AWS::Region' }, ':', { Ref: 'AWS::AccountId' }, ':vpc-peering-connection/*']], + }, + Condition: { + StringEquals: { + 'ec2:RequesterVpc': { + 'Fn::Join': ['', ['arn:', { Ref: 'AWS::Partition' }, ':ec2:', { Ref: 'AWS::Region' }, ':', { Ref: 'AWS::AccountId' }, ':vpc/', { 'Fn::GetAtt': [Match.stringLikeRegexp('TestVpc.*'), 'VpcId'] }]], + }, + }, + }, + }, + ], + }, + }); + }); + test('createPeeringConnection establishes connection between 2 VPCs', () => { const acceptorVpc = new vpc.VpcV2(stack, 'TestAcceptorVpc', { primaryAddressBlock: vpc.IpAddresses.ipv4('10.0.0.0/16'),