Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(VpcNetwork): support reserved subnets in subnetConfiguration #2090

Merged
merged 8 commits into from
Apr 1, 2019
46 changes: 46 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,52 @@ The `VpcNetwork` above will have the exact same subnet definitions as listed
above. However, this time the VPC will have only 1 NAT Gateway and all
Application subnets will route to the NAT Gateway.

#### Reserving subnet IP space
There are situations where the IP space for a subnet or number of subnets
will need to be reserved. This is useful in situations where subnets
would need to be added after the vpc is originally deployed, without causing
IP renumbering for existing subnets. The IP space for a subnet may be reserved
by setting the `reserved` subnetConfiguration property to true, as shown below:

```ts
import ec2 = require('@aws-cdk/aws-ec2');
const vpc = new ec2.VpcNetwork(this, 'TheVPC', {
cidr: '10.0.0.0/16',
natGateways: 1,
subnetConfiguration: [
{
cidrMask: 26,
name: 'Public',
subnetType: SubnetType.Public,
},
{
cidrMask: 26,
name: 'Application1',
subnetType: SubnetType.Private,
},
{
cidrMask: 26,
name: 'Application2',
subnetType: SubnetType.Private,
reserved: true,
},
{
cidrMask: 27,
name: 'Database',
subnetType: SubnetType.Isolated,
}
],
});
```

In the example above, the subnet for Application2 is not actually provisioned
but its IP space is still reserved. If in the future this subnet needs to be
provisioned, then the `reserved: true` property should be removed. Most
importantly, this action would not cause the Database subnet to get renumbered,
but rather the IP space that was previously reserved will be used for the
subnet provisioned for Application2. The `reserved` property also takes into
consideration the number of availability zones when reserving IP space.

#### Sharing VPCs between stacks

If you are creating multiple `Stack`s inside the same CDK application, you
Expand Down
20 changes: 19 additions & 1 deletion packages/@aws-cdk/aws-ec2/lib/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,22 @@ export interface SubnetConfiguration {
/**
* The common Logical Name for the `VpcSubnet`
*
* Thi name will be suffixed with an integer correlating to a specific
* This name will be suffixed with an integer correlating to a specific
* availability zone.
*/
readonly name: string;

/**
* Controls if subnet IP space needs to be reserved.
*
* When true, the IP space for the subnet is reserved but no actual
* resources are provisioned. This space is only dependent on the
* number of availibility zones and on `cidrMask` - all other subnet
* properties are ignored.
*
* @default false
*/
readonly reserved?: boolean;
}

/**
Expand Down Expand Up @@ -501,6 +513,12 @@ export class VpcNetwork extends VpcNetworkBase {

private createSubnetResources(subnetConfig: SubnetConfiguration, cidrMask: number) {
this.availabilityZones.forEach((zone, index) => {
if (subnetConfig.reserved === true) {
// For reserved subnets, just allocate ip space but do not create any resources
this.networkBuilder.addSubnet(cidrMask);
return;
}

const name = subnetId(subnetConfig.name, index);
const subnetProps: VpcSubnetProps = {
availabilityZone: zone,
Expand Down
68 changes: 68 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/test.vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,74 @@ export = {
test.done();
},

"with subnets and reserved subnets defined, VPC subnet count should not contain reserved subnets "(test: Test) {
const stack = getTestStack();
new VpcNetwork(stack, 'TheVPC', {
cidr: '10.0.0.0/16',
subnetConfiguration: [
{
cidrMask: 24,
subnetType: SubnetType.Private,
name: 'Private',
},
{
cidrMask: 24,
name: 'reserved',
subnetType: SubnetType.Private,
reserved: true,
},
{
cidrMask: 28,
name: 'rds',
subnetType: SubnetType.Isolated,
}
],
maxAZs: 3
});
expect(stack).to(countResources("AWS::EC2::Subnet", 6));
test.done();
},
"with reserved subents, any other subnets should not have cidrBlock from within reserved space"(test: Test) {
const stack = getTestStack();
new VpcNetwork(stack, 'TheVPC', {
cidr: '10.0.0.0/16',
subnetConfiguration: [
{
cidrMask: 24,
name: 'ingress',
subnetType: SubnetType.Private,
},
{
cidrMask: 24,
name: 'reserved',
subnetType: SubnetType.Private,
reserved: true,
},
{
cidrMask: 24,
name: 'rds',
subnetType: SubnetType.Private,
}
],
maxAZs: 3
});
for (let i = 0; i < 3; i++) {
expect(stack).to(haveResource("AWS::EC2::Subnet", {
CidrBlock: `10.0.${i}.0/24`
}));
}
for (let i = 3; i < 6; i++) {
expect(stack).notTo(haveResource("AWS::EC2::Subnet", {
CidrBlock: `10.0.${i}.0/24`
}));
}
for (let i = 6; i < 9; i++) {
expect(stack).to(haveResource("AWS::EC2::Subnet", {
CidrBlock: `10.0.${i}.0/24`
}));
}
test.done();
},
"with custom subents, the VPC should have the right number of subnets, an IGW, and a NAT Gateway per AZ"(test: Test) {
const stack = getTestStack();
const zones = new AvailabilityZoneProvider(stack).availabilityZones.length;
Expand Down