Skip to content

Commit 7b51ea9

Browse files
authored
feat(ec2): Vpc supports reserving space for future AZs (#22705)
---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 228c865 commit 7b51ea9

12 files changed

+1556
-0
lines changed

packages/@aws-cdk/aws-ec2/README.md

+25
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,31 @@ new ec2.Vpc(stack, 'TheVPC', {
273273

274274
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.
275275

276+
### Reserving availability zones
277+
278+
There are situations where the IP space for availability zones will
279+
need to be reserved. This is useful in situations where availability
280+
zones would need to be added after the vpc is originally deployed,
281+
without causing IP renumbering for availability zones subnets. The IP
282+
space for reserving `n` availability zones can be done by setting the
283+
`reservedAzs` to `n` in vpc props, as shown below:
284+
285+
```ts
286+
const vpc = new ec2.Vpc(this, 'TheVPC', {
287+
cidr: '10.0.0.0/21',
288+
maxAzs: 3,
289+
reservedAzs: 1,
290+
});
291+
```
292+
293+
In the example above, the subnets for reserved availability zones is not
294+
actually provisioned but its IP space is still reserved. If, in the future,
295+
new availability zones needs to be provisioned, then we would decrement
296+
the value of `reservedAzs` and increment the `maxAzs` or `availabilityZones`
297+
accordingly. This action would not cause the IP address of subnets to get
298+
renumbered, but rather the IP space that was previously reserved will be
299+
used for the new availability zones subnets.
300+
276301
### Advanced Subnet Configuration
277302

278303
If the default VPC configuration (public and private subnets spanning the

packages/@aws-cdk/aws-ec2/lib/vpc.ts

+18
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { VpcLookupOptions } from './vpc-lookup';
2121
import { EnableVpnGatewayOptions, VpnConnection, VpnConnectionOptions, VpnConnectionType, VpnGateway } from './vpn';
2222

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

2526
export interface ISubnet extends IResource {
2627
/**
@@ -888,6 +889,16 @@ export interface VpcProps {
888889
*/
889890
readonly maxAzs?: number;
890891

892+
/**
893+
* Define the number of AZs to reserve.
894+
*
895+
* When specified, the IP space is reserved for the azs but no actual
896+
* resources are provisioned.
897+
*
898+
* @default 0
899+
*/
900+
readonly reservedAzs?: number;
901+
891902
/**
892903
* Availability zones this VPC spans.
893904
*
@@ -1396,6 +1407,9 @@ export class Vpc extends VpcBase {
13961407
const maxAZs = props.maxAzs ?? 3;
13971408
this.availabilityZones = stack.availabilityZones.slice(0, maxAZs);
13981409
}
1410+
for (let i = 0; props.reservedAzs && i < props.reservedAzs; i++) {
1411+
this.availabilityZones.push(FAKE_AZ_NAME);
1412+
}
13991413

14001414

14011415
this.vpcId = this.resource.ref;
@@ -1561,6 +1575,10 @@ export class Vpc extends VpcBase {
15611575
// For reserved subnets, do not create any resources
15621576
return;
15631577
}
1578+
if (availabilityZone === FAKE_AZ_NAME) {
1579+
// For reserved azs, do not create any resources
1580+
return;
1581+
}
15641582

15651583
// mapPublicIpOnLaunch true in Subnet.Public, false in Subnet.Private or Subnet.Isolated.
15661584
let mapPublicIpOnLaunch = false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as cdk from '@aws-cdk/core';
2+
import { IntegTest } from '@aws-cdk/integ-tests';
3+
import * as ec2 from '../lib';
4+
5+
const app = new cdk.App();
6+
const stack = new cdk.Stack(app, 'integtest-vpc-reserved-azs');
7+
8+
new ec2.Vpc(stack, 'MyVpc', {
9+
reservedAzs: 2,
10+
maxAzs: 3,
11+
});
12+
13+
new IntegTest(app, 'vpc-reserved-azs', {
14+
testCases: [stack],
15+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"21.0.0"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "21.0.0",
3+
"testCases": {
4+
"vpc-reserved-azs/DefaultTest": {
5+
"stacks": [
6+
"integtest-vpc-reserved-azs"
7+
],
8+
"assertionStack": "vpc-reserved-azs/DefaultTest/DeployAssert",
9+
"assertionStackName": "vpcreservedazsDefaultTestDeployAssertE48D2C6D"
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "21.0.0",
3+
"files": {
4+
"2316c1d0bd29529a3dc0b6ffcefa3aa88c8d79ea1b90aff8056d49f0de23e53b": {
5+
"source": {
6+
"path": "integtest-vpc-reserved-azs.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "2316c1d0bd29529a3dc0b6ffcefa3aa88c8d79ea1b90aff8056d49f0de23e53b.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}

0 commit comments

Comments
 (0)