in packages/aws-cdk-lib/aws-ec2/lib/vpc.ts [1505:1705]
constructor(scope: Construct, id: string, props: VpcProps = {}) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
const stack = Stack.of(this);
// Can't have enabledDnsHostnames without enableDnsSupport
if (props.enableDnsHostnames && !props.enableDnsSupport) {
throw new ValidationError('To use DNS Hostnames, DNS Support must be enabled, however, it was explicitly disabled.', this);
}
if (props.availabilityZones && props.maxAzs) {
throw new ValidationError('Vpc supports \'availabilityZones\' or \'maxAzs\', but not both.', this);
}
const cidrBlock = ifUndefined(props.cidr, Vpc.DEFAULT_CIDR_RANGE);
if (Token.isUnresolved(cidrBlock)) {
throw new ValidationError('\'cidr\' property must be a concrete CIDR string, got a Token (we need to parse it for automatic subdivision)', this);
}
if (props.ipAddresses && props.cidr) {
throw new ValidationError('supply at most one of ipAddresses or cidr', this);
}
const ipProtocol = props.ipProtocol ?? IpProtocol.IPV4_ONLY;
// this property can be set to false if an IPv6_ONLY VPC is implemented in the future
this.useIpv4 = ipProtocol === IpProtocol.IPV4_ONLY || ipProtocol === IpProtocol.DUAL_STACK;
this.useIpv6 = ipProtocol === IpProtocol.DUAL_STACK;
const ipv6OnlyProps: Array<keyof VpcProps> = ['ipv6Addresses'];
if (!this.useIpv6) {
for (const prop of ipv6OnlyProps) {
if (props[prop] !== undefined) {
throw new ValidationError(`${prop} can only be set if IPv6 is enabled. Set ipProtocol to DUAL_STACK`, this);
}
}
}
this.ipAddresses = props.ipAddresses ?? IpAddresses.cidr(cidrBlock);
this.dnsHostnamesEnabled = props.enableDnsHostnames == null ? true : props.enableDnsHostnames;
this.dnsSupportEnabled = props.enableDnsSupport == null ? true : props.enableDnsSupport;
const instanceTenancy = props.defaultInstanceTenancy || 'default';
this.internetConnectivityEstablished = this._internetConnectivityEstablished;
const vpcIpAddressOptions = this.ipAddresses.allocateVpcCidr();
// Define a VPC using the provided CIDR range
this.resource = new CfnVPC(this, 'Resource', {
cidrBlock: vpcIpAddressOptions.cidrBlock,
ipv4IpamPoolId: vpcIpAddressOptions.ipv4IpamPoolId,
ipv4NetmaskLength: vpcIpAddressOptions.ipv4NetmaskLength,
enableDnsHostnames: this.dnsHostnamesEnabled,
enableDnsSupport: this.dnsSupportEnabled,
instanceTenancy,
});
this.vpcDefaultNetworkAcl = this.resource.attrDefaultNetworkAcl;
this.vpcCidrBlockAssociations = this.resource.attrCidrBlockAssociations;
this.vpcCidrBlock = this.resource.attrCidrBlock;
this.vpcDefaultSecurityGroup = this.resource.attrDefaultSecurityGroup;
this.vpcIpv6CidrBlocks = this.resource.attrIpv6CidrBlocks;
Tags.of(this).add(NAME_TAG, props.vpcName || this.node.path);
if (props.availabilityZones) {
// If given AZs and stack AZs are both resolved, then validate their compatibility.
const resolvedStackAzs = this.resolveStackAvailabilityZones(stack.availabilityZones);
const areGivenAzsSubsetOfStack = resolvedStackAzs.length === 0 ||
props.availabilityZones.every(az => Token.isUnresolved(az) ||resolvedStackAzs.includes(az));
if (!areGivenAzsSubsetOfStack) {
throw new ValidationError(`Given VPC 'availabilityZones' ${props.availabilityZones} must be a subset of the stack's availability zones ${resolvedStackAzs}`, this);
}
this.availabilityZones = props.availabilityZones;
} else {
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;
this.vpcArn = Arn.format({
service: 'ec2',
resource: 'vpc',
resourceName: this.vpcId,
}, stack);
const defaultSubnet = props.natGateways === 0 ? Vpc.DEFAULT_SUBNETS_NO_NAT : Vpc.DEFAULT_SUBNETS;
this.subnetConfiguration = ifUndefined(props.subnetConfiguration, defaultSubnet);
const natGatewayPlacement = props.natGatewaySubnets || { subnetType: SubnetType.PUBLIC };
const natGatewayCount = determineNatGatewayCount(props.natGateways, this.subnetConfiguration, this.availabilityZones.length);
if (this.useIpv6) {
this.ipv6Addresses = props.ipv6Addresses ?? Ipv6Addresses.amazonProvided();
this.ipv6CidrBlock = this.ipv6Addresses.allocateVpcIpv6Cidr({
scope: this,
vpcId: this.vpcId,
});
this.ipv6SelectedCidr = Fn.select(0, this.resource.attrIpv6CidrBlocks);
}
// subnetConfiguration must be set before calling createSubnets
this.createSubnets();
const createInternetGateway = props.createInternetGateway ?? true;
const allowOutbound = this.subnetConfiguration.filter(
subnet => (subnet.subnetType !== SubnetType.PRIVATE_ISOLATED && subnet.subnetType !== SubnetType.ISOLATED && !subnet.reserved)).length > 0;
// Create an Internet Gateway and attach it if necessary
if (allowOutbound && createInternetGateway) {
const igw = new CfnInternetGateway(this, 'IGW', {
});
this.internetGatewayId = igw.ref;
this._internetConnectivityEstablished.add(igw);
const att = new CfnVPCGatewayAttachment(this, 'VPCGW', {
internetGatewayId: igw.ref,
vpcId: this.resource.ref,
});
(this.publicSubnets as PublicSubnet[]).forEach(publicSubnet => {
// configure IPv4 route
if (this.useIpv4) {
publicSubnet.addDefaultInternetRoute(igw.ref, att);
}
// configure IPv6 route if VPC is dual stack
if (this.useIpv6) {
publicSubnet.addIpv6DefaultInternetRoute(igw.ref);
}
});
// if gateways are needed create them
if (natGatewayCount > 0) {
const provider = props.natGatewayProvider || NatProvider.gateway();
this.createNatGateways(provider, natGatewayCount, natGatewayPlacement);
}
}
// Create an Egress Only Internet Gateway and attach it if necessary
if (this.useIpv6 && this.privateSubnets) {
const eigw = new CfnEgressOnlyInternetGateway(this, 'EIGW6', {
vpcId: this.vpcId,
});
(this.privateSubnets as PrivateSubnet[]).forEach(privateSubnet => {
privateSubnet.addIpv6DefaultEgressOnlyInternetRoute(eigw.ref);
});
}
if (props.vpnGateway && this.publicSubnets.length === 0 && this.privateSubnets.length === 0 && this.isolatedSubnets.length === 0) {
throw new ValidationError('Can not enable the VPN gateway while the VPC has no subnets at all', this);
}
if ((props.vpnConnections || props.vpnGatewayAsn) && props.vpnGateway === false) {
throw new ValidationError('Cannot specify `vpnConnections` or `vpnGatewayAsn` when `vpnGateway` is set to false.', this);
}
if (props.vpnGateway || props.vpnConnections || props.vpnGatewayAsn) {
this.enableVpnGateway({
amazonSideAsn: props.vpnGatewayAsn,
type: VpnConnectionType.IPSEC_1,
vpnRoutePropagation: props.vpnRoutePropagation,
});
const vpnConnections = props.vpnConnections || {};
for (const [connectionId, connection] of Object.entries(vpnConnections)) {
this.addVpnConnection(connectionId, connection);
}
}
// Allow creation of gateway endpoints on VPC instantiation as those can be
// immediately functional without further configuration. This is not the case
// for interface endpoints where the security group must be configured.
if (props.gatewayEndpoints) {
const gatewayEndpoints = props.gatewayEndpoints || {};
for (const [endpointId, endpoint] of Object.entries(gatewayEndpoints)) {
this.addGatewayEndpoint(endpointId, endpoint);
}
}
// Add flow logs to the VPC
if (props.flowLogs) {
const flowLogs = props.flowLogs || {};
for (const [flowLogId, flowLog] of Object.entries(flowLogs)) {
this.addFlowLog(flowLogId, flowLog);
}
}
const restrictFlag = FeatureFlags.of(this).isEnabled(EC2_RESTRICT_DEFAULT_SECURITY_GROUP);
if ((restrictFlag && props.restrictDefaultSecurityGroup !== false) || props.restrictDefaultSecurityGroup) {
this.restrictDefaultSecurityGroup();
}
}