Skip to content

Commit

Permalink
feat(ec2): L2 Construct - VpcPeeringConnection
Browse files Browse the repository at this point in the history
fixes #9338
  • Loading branch information
civilizeddev committed Jul 29, 2020
1 parent 3cad6a3 commit c9c3808
Show file tree
Hide file tree
Showing 4 changed files with 259 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -828,4 +828,16 @@ const subnet = Subnet.fromSubnetAttributes(this, 'SubnetFromAttributes', {

// Supply only subnet id
const subnet = Subnet.fromSubnetId(this, 'SubnetFromId', 's-1234');
```
```

## VPC Peering Connections

Requests a VPC peering connection between two VPCs: a requester VPC that you own and an accepter VPC with which to create the connection. The accepter VPC can belong to another AWS account and can be in a different Region to the requester VPC. The requester VPC and accepter VPC cannot have overlapping CIDR blocks.

To import an existing VPC peering connection:

```ts
VpcPeeringConnection.fromVpcPeeringConnectionAttributes(this, 'vpcPeeringConnection', {
vpcPeeringConnectionId: 'pcx-019847b3359e77fc6',
});
```
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-ec2/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export * from './vpc-lookup';
export * from './vpn';
export * from './vpc-endpoint';
export * from './vpc-endpoint-service';
export * from './vpc-peering-connection';
export * from './user-data';
export * from './windows-versions';
export * from './vpc-flow-logs';
Expand Down
132 changes: 132 additions & 0 deletions packages/@aws-cdk/aws-ec2/lib/vpc-peering-connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import * as iam from '@aws-cdk/aws-iam';
import * as cdk from '@aws-cdk/core';
import { CfnVPCPeeringConnection } from './ec2.generated';
import { IVpc } from './vpc';

/**
* VPC with which you are creating the VPC peering connection.
*/
export class PeerVpc {
constructor(
/**
* The ID of the VPC with which you are creating the VPC peering connection
*/
public readonly vpcId: string,

/**
* The Region code for the accepter VPC
*
* @default - The Region in which you make the request
*/
public readonly region?: string,

/**
* The AWS account ID of the owner of the accepter VPC
*
* @default - Your AWS account ID
*/
public readonly account?: string,
) {}
}

/**
* Options for constructing VpcPeeringConnection
*/
export interface VpcPeeringConnectionOptions {
/**
* The name of VPC peering connections
*
* @default - generated by CloudFormation
*/
readonly vpcPeeringConnectionName?: string

/**
* VPC with which you are creating the VPC peering connection.
*/
readonly peerVpc: PeerVpc

/**
* The role, which is required when you are peering a VPC in a different AWS account.
*
* @default - conditional
*/
readonly role?: iam.IRole
}

/**
* Props for constructing VpcPeeringConnection
*/
export interface VpcPeeringConnectionProps extends VpcPeeringConnectionOptions {
/**
* The requester VPC that you own
*/
readonly vpc: IVpc
}

/**
* VPC peering connection between two VPC
*/
export interface IVpcPeeringConnection extends cdk.IResource {
/**
* The ID of the VPC peering connection.
*
* @attribute
*/
readonly vpcPeeringConnectionId: string
}

/**
* Attributes of VPC peering connection
*/
export interface VpcPeeringConnectionAttributes {
/**
* The ID of the VPC peering connection.
*/
readonly vpcPeeringConnectionId: string
}

/**
* VPC peering connection between two VPC
*/
export class VpcPeeringConnection extends cdk.Resource implements IVpcPeeringConnection {
/**
* Import a VPC peering connection from attributes
*/
public static fromVpcPeeringConnectionAttributes(scope: cdk.Construct, id: string, attrs: VpcPeeringConnectionAttributes): IVpcPeeringConnection {
class Import extends cdk.Resource implements IVpcPeeringConnection {
public readonly vpcPeeringConnectionId: string = attrs.vpcPeeringConnectionId
}
return new Import(scope, id);
}

/**
* The ID of the VPC peering connection.
*
* @attribute
*/
public readonly vpcPeeringConnectionId: string

constructor(scope: cdk.Construct, id: string, props: VpcPeeringConnectionProps) {
super(scope, id);

if (props.peerVpc?.account && !props.role) {
throw new Error('The role is required when you are peering a VPC in a different AWS account.');
}

const tags: cdk.CfnTag[] = [];
if (props.vpcPeeringConnectionName) {
tags.push({ key: 'Name', value: props.vpcPeeringConnectionName });
}

const resource = new CfnVPCPeeringConnection(this, 'Resource', {
vpcId: props.vpc.vpcId,
peerVpcId: props.peerVpc.vpcId,
peerRegion: props.peerVpc.region || this.stack.region,
peerOwnerId: props.peerVpc.account || this.stack.account,
peerRoleArn: props.role?.roleArn,
tags,
});

this.vpcPeeringConnectionId = resource.ref;
}
}
113 changes: 113 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/vpc-peering-connection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { ABSENT } from '@aws-cdk/assert';
import '@aws-cdk/assert/jest';
import * as iam from '@aws-cdk/aws-iam';
import * as cdk from '@aws-cdk/core';
import * as ec2 from '../lib';

describe('Normal', () => {
describe('Account x Region', () => {
test('Peer vpc in my account & the same region', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
const peerVpc = new ec2.PeerVpc('vpc-09876543210987654');
new ec2.VpcPeeringConnection(stack, 'peering', { vpc, peerVpc });

expect(stack).toHaveResource('AWS::EC2::VPCPeeringConnection', {
VpcId: vpc.vpcId,
PeerOwnerId: {
Ref: 'AWS::AccountId',
},
PeerRegion: {
Ref: 'AWS::Region',
},
PeerRoleArn: ABSENT,
});
});

test('Peer vpc in my account & the different region', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
const peerVpc = new ec2.PeerVpc('vpc-09876543210987654', 'ap-southeast-1');
new ec2.VpcPeeringConnection(stack, 'peering', { vpc, peerVpc });

expect(stack).toHaveResource('AWS::EC2::VPCPeeringConnection', {
VpcId: vpc.vpcId,
PeerOwnerId: {
Ref: 'AWS::AccountId',
},
PeerRegion: peerVpc.region,
PeerRoleArn: ABSENT,
});
});

test('Peer vpc in another account & the different region', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
const peerVpc = new ec2.PeerVpc('vpc-09876543210987654', 'ap-southeast-1', '123456789012');
const role = iam.Role.fromRoleArn(stack, 'role', `arn:aws:iam:${peerVpc.account}:${peerVpc.region}:role/role-for-vpc-peering-connection`);
new ec2.VpcPeeringConnection(stack, 'peering', { vpc, peerVpc, role });

expect(stack).toHaveResource('AWS::EC2::VPCPeeringConnection', {
VpcId: vpc.vpcId,
PeerOwnerId: peerVpc.account,
PeerRegion: peerVpc.region,
PeerRoleArn: role.roleArn,
});
});

test('Peer vpc in another account & the same region', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
const peerVpc = new ec2.PeerVpc('vpc-09876543210987654', undefined, '123456789012');
const role = iam.Role.fromRoleArn(stack, 'role', `arn:aws:iam:${peerVpc.account}:${peerVpc.region}:role/role-for-vpc-peering-connection`);
new ec2.VpcPeeringConnection(stack, 'peering', { vpc, peerVpc, role });

expect(stack).toHaveResource('AWS::EC2::VPCPeeringConnection', {
VpcId: vpc.vpcId,
PeerOwnerId: peerVpc.account,
PeerRegion: {
Ref: 'AWS::Region',
},
PeerRoleArn: role.roleArn,
});
});
});

describe('Name', () => {
test('With name', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
const peerVpc = new ec2.PeerVpc('vpc-09876543210987654');
const vpcPeeringConnectionName = 'VpcPeeringConnection';
new ec2.VpcPeeringConnection(stack, 'peering', { vpc, peerVpc, vpcPeeringConnectionName });

expect(stack).toHaveResource('AWS::EC2::VPCPeeringConnection', {
Tags: [{ Key: 'Name', Value: vpcPeeringConnectionName }],
});
});

test('Without name', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
const peerVpc = new ec2.PeerVpc('vpc-09876543210987654');
new ec2.VpcPeeringConnection(stack, 'peering', { vpc, peerVpc });

expect(stack).toHaveResource('AWS::EC2::VPCPeeringConnection', {
Tags: ABSENT,
});
});
});
});

describe('Error', () => {
test('The role is required when you are peering a VPC in a different AWS account.', () => {
const stack = new cdk.Stack();
const vpc = ec2.Vpc.fromVpcAttributes(stack, 'vpc', { vpcId: 'vpc-01234567890123456', availabilityZones: ['ap-northeast-1a'] });
expect(() => {
new ec2.VpcPeeringConnection(stack, 'peering', {
vpc,
peerVpc: new ec2.PeerVpc('vpc-09876543210987654', 'ap-northeast-2', '012345678901'),
});
}).toThrowError('The role is required when you are peering a VPC in a different AWS account.');
});
});

0 comments on commit c9c3808

Please sign in to comment.