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(ec2): Vpc supports reserving space for future AZs #22705

Merged
merged 2 commits into from Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Expand Up @@ -273,6 +273,31 @@ new ec2.Vpc(stack, 'TheVPC', {

With this method of IP address management, no attempt is made to guess at subnet group sizes or to exhaustively allocate the IP range. All subnet groups must have an explicit `cidrMask` set as part of their subnet configuration, or `defaultSubnetIpv4NetmaskLength` must be set for a default size. If not, synthesis will fail and you must provide one or the other.

### Reserving availability zones

There are situations where the IP space for availability zones will
need to be reserved. This is useful in situations where availability
zones would need to be added after the vpc is originally deployed,
without causing IP renumbering for availability zones subnets. The IP
space for reserving `n` availability zones can be done by setting the
`reservedAzs` to `n` in vpc props, as shown below:

```ts
const vpc = new ec2.Vpc(this, 'TheVPC', {
cidr: '10.0.0.0/21',
maxAzs: 3,
reservedAzs: 1,
});
```

In the example above, the subnets for reserved availability zones is not
actually provisioned but its IP space is still reserved. If, in the future,
new availability zones needs to be provisioned, then we would decrement
the value of `reservedAzs` and increment the `maxAzs` or `availabilityZones`
accordingly. This action would not cause the IP address of subnets to get
renumbered, but rather the IP space that was previously reserved will be
used for the new availability zones subnets.

### Advanced Subnet Configuration

If the default VPC configuration (public and private subnets spanning the
Expand Down
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-ec2/lib/vpc.ts
Expand Up @@ -21,6 +21,7 @@ import { VpcLookupOptions } from './vpc-lookup';
import { EnableVpnGatewayOptions, VpnConnection, VpnConnectionOptions, VpnConnectionType, VpnGateway } from './vpn';

const VPC_SUBNET_SYMBOL = Symbol.for('@aws-cdk/aws-ec2.VpcSubnet');
const FAKE_AZ_NAME = 'fake-az';

export interface ISubnet extends IResource {
/**
Expand Down Expand Up @@ -888,6 +889,16 @@ export interface VpcProps {
*/
readonly maxAzs?: number;

/**
* Define the number of AZs to reserve.
*
* When specified, the IP space is reserved for the azs but no actual
* resources are provisioned.
*
* @default 0
*/
readonly reservedAzs?: number;

/**
* Availability zones this VPC spans.
*
Expand Down Expand Up @@ -1396,6 +1407,9 @@ export class Vpc extends VpcBase {
const maxAZs = props.maxAzs ?? 3;
this.availabilityZones = stack.availabilityZones.slice(0, maxAZs);
}
for (let i = 0; props.reservedAzs && i < props.reservedAzs; i++) {
this.availabilityZones.push(FAKE_AZ_NAME);
}


this.vpcId = this.resource.ref;
Expand Down Expand Up @@ -1561,6 +1575,10 @@ export class Vpc extends VpcBase {
// For reserved subnets, do not create any resources
return;
}
if (availabilityZone === FAKE_AZ_NAME) {
// For reserved azs, do not create any resources
return;
}

// mapPublicIpOnLaunch true in Subnet.Public, false in Subnet.Private or Subnet.Isolated.
let mapPublicIpOnLaunch = false;
Expand Down
15 changes: 15 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/integ.vpc-reserved-azs.ts
@@ -0,0 +1,15 @@
import * as cdk from '@aws-cdk/core';
import { IntegTest } from '@aws-cdk/integ-tests';
import * as ec2 from '../lib';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'integtest-vpc-reserved-azs');

new ec2.Vpc(stack, 'MyVpc', {
reservedAzs: 2,
maxAzs: 3,
});

new IntegTest(app, 'vpc-reserved-azs', {
testCases: [stack],
});
@@ -0,0 +1 @@
{"version":"21.0.0"}
@@ -0,0 +1,12 @@
{
"version": "21.0.0",
"testCases": {
"vpc-reserved-azs/DefaultTest": {
"stacks": [
"integtest-vpc-reserved-azs"
],
"assertionStack": "vpc-reserved-azs/DefaultTest/DeployAssert",
"assertionStackName": "vpcreservedazsDefaultTestDeployAssertE48D2C6D"
}
}
}
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"2316c1d0bd29529a3dc0b6ffcefa3aa88c8d79ea1b90aff8056d49f0de23e53b": {
"source": {
"path": "integtest-vpc-reserved-azs.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "2316c1d0bd29529a3dc0b6ffcefa3aa88c8d79ea1b90aff8056d49f0de23e53b.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}