-
Notifications
You must be signed in to change notification settings - Fork 4k
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
(BREAKING) Enhancement - Custom VPC Subnets #250
Changes from 3 commits
28aea13
2beb4a7
2a172ed
9da7a2f
55e3666
a2b527d
c9d4f39
af75c41
88a6d94
9ac38f4
60ef32d
0f0079a
abd5086
6dabebe
7be9cc5
2d1bb07
1ef7db6
06350b4
455d9ef
701858c
5ecb64a
a5ce5d9
2fbb213
1777874
9d8067f
d639de4
f2d4571
5c1bc5d
0864bb8
776f1e0
82246ae
864a341
9722cdc
bba5e67
c2baa77
31057b2
f3f5428
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,28 +75,32 @@ export interface VpcNetworkProps { | |
* | ||
* For example if you want 1 public subnet, 1 private subnet, and 1 internal | ||
* subnet in each AZ provide the following: | ||
* subnets: [ | ||
* { | ||
* cidrMask: 24, | ||
* name: application, | ||
* subnetType: SubnetType.Private, | ||
* }, | ||
* { | ||
* cidrMask: 26, | ||
* name: ingress, | ||
* subnetType: SubnetType.Public, | ||
* natGateway: true, | ||
* }, | ||
* { | ||
* cidrMask: 28, | ||
* name: rds, | ||
* subnetType: SubnetType.Internal, | ||
* } | ||
* subnetConfiguration: [ | ||
* { | ||
* cidrMask: 24, | ||
* name: 'ingress', | ||
* subnetType: SubnetType.Public, | ||
* natGateway: true, | ||
* }, | ||
* { | ||
* cidrMask: 24, | ||
* name: 'application', | ||
* subnetType: SubnetType.Private, | ||
* }, | ||
* { | ||
* cidrMask: 28, | ||
* name: 'rds', | ||
* subnetType: SubnetType.Internal, | ||
* } | ||
* ] | ||
* | ||
* `cidrMask` is optional and if not provided the IP space in the VPC will be | ||
* evenly divided between the requested subnets. | ||
* | ||
* @default the VPC CIDR will be evenly divided between 1 public and 1 | ||
* private subnet per AZ | ||
*/ | ||
subnetConfigurations?: SubnetConfiguration[]; | ||
subnetConfiguration?: SubnetConfiguration[]; | ||
} | ||
|
||
/** | ||
|
@@ -128,20 +132,25 @@ export enum SubnetType { | |
Internal = 1, | ||
|
||
/** | ||
* Private subnets route outbound traffic via a NAT Gateway | ||
* Subnet that routes to the internet, but not vice versa. | ||
* | ||
* Instances in a private subnet can connect to the Internet, but will not | ||
* allow connections to be initiated from the Internet. | ||
* | ||
* Outbound traffic will be routed via a NAT Gateways preference being in | ||
* Outbound traffic will be routed via a NAT Gateway. Preference being in | ||
* the same AZ, but if not available will use another AZ. This is common for | ||
* experimental cost conscious accounts or accounts where HA outbound | ||
* traffic is not needed. | ||
*/ | ||
Private = 2, | ||
|
||
/** | ||
* Public subnets route outbound traffic via an Internet Gateway | ||
* Subnet connected to the Internet | ||
* | ||
* If this is set and OutboundTrafficMode.None is configure an error | ||
* will be thrown. | ||
* Instances in a Public subnet can connect to the Internet and can be | ||
* connected to from the Internet as long as they are launched with public IPs. | ||
* | ||
* Public subnets route outbound traffic via an Internet Gateway. | ||
*/ | ||
Public = 3 | ||
|
||
|
@@ -151,15 +160,44 @@ export enum SubnetType { | |
* Specify configuration parameters for a VPC to be built | ||
*/ | ||
export interface SubnetConfiguration { | ||
// the cidr mask value from 16-28 | ||
/** | ||
* The CIDR Mask or the number of leading 1 bits in the routing mask | ||
* | ||
* Valid values are 16 - 28 | ||
*/ | ||
cidrMask?: number; | ||
// Public (IGW), Private (Nat GW), Internal (no outbound) | ||
|
||
/** | ||
* The type of Subnet to configure. | ||
* | ||
* The Subnet type will control the ability to route and connect to the | ||
* Internet. | ||
*/ | ||
subnetType: SubnetType; | ||
// name that will be used to generate an AZ specific name e.g. name-2a | ||
|
||
/** | ||
* The common Logical Name for the `VpcSubnet` | ||
* | ||
* Thi name will be suffixed with an integer correlating to a specific | ||
* availability zone. | ||
*/ | ||
name: string; | ||
// if true will place a NAT Gateway in this subnet, subnetType must be Public | ||
|
||
/** | ||
* Controls the creation a NAT Gateway in this subnet | ||
* | ||
* If true will place a NAT Gateway in this subnet, subnetType must be Public. | ||
* Defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Internal. | ||
* NAT Gateways in non-public subnets will functionally not work and are | ||
* therefore not possible to create. | ||
*/ | ||
natGateway?: boolean; | ||
// defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Internal | ||
|
||
/** | ||
* Controls if a public IP is associated to an instance at launch | ||
* | ||
* Defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Internal. | ||
*/ | ||
mapPublicIpOnLaunch?: boolean; | ||
} | ||
|
||
|
@@ -193,17 +231,7 @@ export class VpcNetwork extends VpcNetworkRef { | |
public static readonly DEFAULT_CIDR_RANGE: string = '10.0.0.0/16'; | ||
|
||
/** | ||
* The default maximum number of NAT Gateways | ||
* | ||
* @link https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Appendix_Limits.html | ||
* defaulting 256 is an arbitrary max that should be impracticel to reach. | ||
* This can be overriden using VpcNetworkProps when creating a VPCNetwork resource. | ||
* e.g. new VpcResource(this, { maxNatGateways: 1 }) | ||
*/ | ||
public static readonly MAX_NAT_GATEWAYS: number = 256; | ||
|
||
/** | ||
* The deafult subnet configuration | ||
* The default subnet configuration | ||
* | ||
* 1 Public and 1 Private subnet per AZ evenly split | ||
*/ | ||
|
@@ -243,7 +271,7 @@ export class VpcNetwork extends VpcNetworkRef { | |
/** | ||
* Maximum Number of NAT Gateways used to control cost | ||
* | ||
* @default {VpcNetwork.MAX_NAT_GATEWAYS} | ||
* @default {VpcNetworkProps.maxAZs} | ||
*/ | ||
private readonly maxNatGateways: number; | ||
|
||
|
@@ -265,7 +293,7 @@ export class VpcNetwork extends VpcNetworkRef { | |
/** | ||
* Subnet configurations for this VPC | ||
*/ | ||
private subnetConfigurations: SubnetConfiguration[] = []; | ||
private subnetConfiguration: SubnetConfiguration[] = []; | ||
|
||
/** | ||
* Maximum AZs to Uses for this VPC | ||
|
@@ -288,7 +316,7 @@ export class VpcNetwork extends VpcNetworkRef { | |
throw new Error('To use DNS Hostnames, DNS Support must be enabled, however, it was explicitly disabled.'); | ||
} | ||
|
||
const cidrBlock = props.cidr || VpcNetwork.DEFAULT_CIDR_RANGE; | ||
const cidrBlock = ifUndefined(props.cidr, VpcNetwork.DEFAULT_CIDR_RANGE); | ||
this.networkBuilder = new NetworkBuilder(cidrBlock); | ||
|
||
const enableDnsHostnames = props.enableDnsHostnames == null ? true : props.enableDnsHostnames; | ||
|
@@ -314,15 +342,15 @@ export class VpcNetwork extends VpcNetworkRef { | |
this.vpcId = this.resource.ref; | ||
this.dependencyElements.push(this.resource); | ||
|
||
this.maxNatGateways = props.maxNatGateways || VpcNetwork.MAX_NAT_GATEWAYS; | ||
this.maxNatGateways = ifUndefined(props.maxNatGateways, this.availabilityZones.length); | ||
|
||
this.subnetConfigurations = props.subnetConfigurations || VpcNetwork.DEFAULT_SUBNETS; | ||
this.subnetConfiguration = ifUndefined(props.subnetConfiguration, VpcNetwork.DEFAULT_SUBNETS); | ||
this.createSubnets(); | ||
|
||
const allowOutbound = this.subnetConfigurations.filter( | ||
(subnet) => (subnet.subnetType !== SubnetType.Internal)).length > 0; | ||
const allowOutbound = this.subnetConfiguration.filter( | ||
subnet => (subnet.subnetType !== SubnetType.Internal)).length > 0; | ||
|
||
// Create an Internet Gateway and attach it (if the outbound traffic mode != None) | ||
// Create an Internet Gateway and attach it if necessary | ||
if (allowOutbound) { | ||
const igw = new cloudformation.InternetGatewayResource(this, 'IGW'); | ||
const att = new cloudformation.VPCGatewayAttachmentResource(this, 'VPCGW', { | ||
|
@@ -363,10 +391,7 @@ export class VpcNetwork extends VpcNetworkRef { | |
|
||
// Calculate number of public/private subnets based on number of AZs | ||
|
||
for (const subnet of this.subnetConfigurations) { | ||
subnet.mapPublicIpOnLaunch = subnet.mapPublicIpOnLaunch || | ||
(subnet.subnetType === SubnetType.Public); | ||
|
||
for (const subnet of this.subnetConfiguration) { | ||
if (subnet.cidrMask === undefined) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With less variables and state munging:
But we still don't sort our CIDRs in the same order as in the argument list, do we? Because the automatically allocated ones will always go at the end now. So we should actually do the mask calculation before starting to add any subnets at all. I see how this will be some hard math to do properly, especially as soon as there are multiple unmasked blocks. At the same time, I DO like to have the guarantee that the subnets are laid out in the space in the order mentioned in the config. So if it's too hard to do this math, then we add a restriction that unassigned There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have discussed this in more detail on the call. I think we still have a gap here. First the Re: do the mask calculation before starting -> I agree and considered this, but I don't think it buys us much. Even if we do it first and then create the subnetConfiguration and do it for real, the user can still almost never update the subnet configurations because a change will likely modify the "remaining space" and cloudformation will try to destroy/create. What I think currently is updating a VPC is already a low probability event once you get to prod (even today with CFN it rarely happens). So if you plan to make modifications to your VPC, then you really should not use any remaining space subnets. Does that mean we should prevent the mixing of these types or leave it similar to how it functions currently? I think I lean towards separating the two because it's an easier mental model. However, I already have docs that discuss the likely use case of mixing them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
And I agree that automatic space allocation combined with extending the nets is probably always a bad idea. In the best case, the automatic allocation left some wasted space that you now have to explicitly specify, and you get to create some nets in the previously wasted space. Even though it should have been, this wasn't immediately obvious to me :). I guess it might be worthwhile to add a comment on the All that is to say: you're right, no need to change this. |
||
remainingSpaceSubnets.push(subnet); | ||
continue; | ||
|
@@ -388,7 +413,8 @@ export class VpcNetwork extends VpcNetworkRef { | |
availabilityZone: zone, | ||
vpcId: this.vpcId, | ||
cidrBlock: this.networkBuilder.addSubnet(cidrMask), | ||
mapPublicIpOnLaunch: subnetConfig.mapPublicIpOnLaunch | ||
mapPublicIpOnLaunch: ifUndefined(subnetConfig.mapPublicIpOnLaunch, | ||
(subnetConfig.subnetType === SubnetType.Public)), | ||
}; | ||
|
||
switch (subnetConfig.subnetType) { | ||
|
@@ -419,9 +445,27 @@ export class VpcNetwork extends VpcNetworkRef { | |
* Specify configuration parameters for a VPC subnet | ||
*/ | ||
export interface VpcSubnetProps { | ||
|
||
/** | ||
* The availability zone for the subnet | ||
*/ | ||
availabilityZone: string; | ||
|
||
/** | ||
* The VPC which this subnet is part of | ||
*/ | ||
vpcId: Token; | ||
|
||
/** | ||
* The CIDR notation for this subnet | ||
*/ | ||
cidrBlock: string; | ||
|
||
/** | ||
* Controls if a public IP is associated to an instance at launch | ||
* | ||
* Defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Internal. | ||
*/ | ||
mapPublicIpOnLaunch?: boolean; | ||
} | ||
|
||
|
@@ -532,3 +576,7 @@ export class VpcPrivateSubnet extends VpcSubnet { | |
this.addDefaultRouteToNAT(natGatewayId); | ||
} | ||
} | ||
|
||
function ifUndefined<T>(value: T | undefined, defaultValue: T): T { | ||
return value !== undefined ? value : defaultValue; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@default Available space is automatically divided over maskless subnets