@aws-cdk/aws-ec2
Version:
The CDK Construct Library for AWS::EC2
1,177 lines • 197 kB
JavaScript
"use strict";
var _a, _b, _c, _d;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrivateSubnet = exports.PublicSubnet = exports.RouterType = exports.Subnet = exports.Vpc = exports.DefaultInstanceTenancy = exports.SubnetType = void 0;
const jsiiDeprecationWarnings = require("../.warnings.jsii.js");
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const cxschema = require("@aws-cdk/cloud-assembly-schema");
const core_1 = require("@aws-cdk/core");
const cxapi = require("@aws-cdk/cx-api");
const constructs_1 = require("constructs");
const client_vpn_endpoint_1 = require("./client-vpn-endpoint");
const ec2_generated_1 = require("./ec2.generated");
const nat_1 = require("./nat");
const network_acl_1 = require("./network-acl");
const network_util_1 = require("./network-util");
const subnet_1 = require("./subnet");
const util_1 = require("./util");
const vpc_endpoint_1 = require("./vpc-endpoint");
const vpc_flow_logs_1 = require("./vpc-flow-logs");
const vpn_1 = require("./vpn");
// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
const core_2 = require("@aws-cdk/core");
const VPC_SUBNET_SYMBOL = Symbol.for('@aws-cdk/aws-ec2.VpcSubnet');
/**
* The type of Subnet
*/
var SubnetType;
(function (SubnetType) {
/**
* Isolated Subnets do not route traffic to the Internet (in this VPC),
* and as such, do not require NAT gateways.
*
* Isolated subnets can only connect to or be connected to from other
* instances in the same VPC. A default VPC configuration will not include
* isolated subnets.
*
* This can be good for subnets with RDS or Elasticache instances,
* or which route Internet traffic through a peer VPC.
*
* @deprecated use `SubnetType.PRIVATE_ISOLATED`
*/
SubnetType["ISOLATED"] = "Deprecated_Isolated";
/**
* Isolated Subnets do not route traffic to the Internet (in this VPC),
* and as such, do not require NAT gateways.
*
* Isolated subnets can only connect to or be connected to from other
* instances in the same VPC. A default VPC configuration will not include
* isolated subnets.
*
* This can be good for subnets with RDS or Elasticache instances,
* or which route Internet traffic through a peer VPC.
*/
SubnetType["PRIVATE_ISOLATED"] = "Isolated";
/**
* 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. NAT Gateway(s) are
* required with this subnet type to route the Internet traffic through.
* If a NAT Gateway is not required or desired, use `SubnetType.PRIVATE_ISOLATED` instead.
*
* By default, a NAT gateway is created in every public subnet for maximum availability.
* Be aware that you will be charged for NAT gateways.
*
* Normally a Private subnet will use a NAT gateway in the same AZ, but
* if `natGateways` is used to reduce the number of NAT gateways, a NAT
* gateway from another AZ will be used instead.
*
* @deprecated use `PRIVATE_WITH_NAT`
*/
SubnetType["PRIVATE"] = "Deprecated_Private";
/**
* Subnet that routes to the internet (via a NAT gateway), 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. NAT Gateway(s) are
* required with this subnet type to route the Internet traffic through.
* If a NAT Gateway is not required or desired, use `SubnetType.PRIVATE_ISOLATED` instead.
*
* By default, a NAT gateway is created in every public subnet for maximum availability.
* Be aware that you will be charged for NAT gateways.
*
* Normally a Private subnet will use a NAT gateway in the same AZ, but
* if `natGateways` is used to reduce the number of NAT gateways, a NAT
* gateway from another AZ will be used instead.
*/
SubnetType["PRIVATE_WITH_NAT"] = "Private";
/**
* Subnet connected to the Internet
*
* 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 (controlled on the AutoScalingGroup or other constructs that launch
* instances).
*
* Public subnets route outbound traffic via an Internet Gateway.
*/
SubnetType["PUBLIC"] = "Public";
})(SubnetType = exports.SubnetType || (exports.SubnetType = {}));
/**
* A new or imported VPC
*/
class VpcBase extends core_1.Resource {
constructor() {
super(...arguments);
/**
* Dependencies for NAT connectivity
*
* @deprecated - This value is no longer used.
*/
this.natDependencies = new Array();
/**
* If this is set to true, don't error out on trying to select subnets
*/
this.incompleteSubnetDefinition = false;
}
/**
* Returns IDs of selected subnets
*/
selectSubnets(selection = {}) {
const subnets = this.selectSubnetObjects(selection);
const pubs = new Set(this.publicSubnets);
return {
subnetIds: subnets.map(s => s.subnetId),
get availabilityZones() { return subnets.map(s => s.availabilityZone); },
internetConnectivityEstablished: tap(new CompositeDependable(), d => subnets.forEach(s => d.add(s.internetConnectivityEstablished))),
subnets,
hasPublic: subnets.some(s => pubs.has(s)),
isPendingLookup: this.incompleteSubnetDefinition,
};
}
/**
* Adds a VPN Gateway to this VPC
*/
enableVpnGateway(options) {
if (this.vpnGatewayId) {
throw new Error('The VPN Gateway has already been enabled.');
}
const vpnGateway = new vpn_1.VpnGateway(this, 'VpnGateway', {
amazonSideAsn: options.amazonSideAsn,
type: vpn_1.VpnConnectionType.IPSEC_1,
});
this._vpnGatewayId = vpnGateway.gatewayId;
const attachment = new ec2_generated_1.CfnVPCGatewayAttachment(this, 'VPCVPNGW', {
vpcId: this.vpcId,
vpnGatewayId: this._vpnGatewayId,
});
// Propagate routes on route tables associated with the right subnets
const vpnRoutePropagation = options.vpnRoutePropagation ?? [{}];
const routeTableIds = util_1.allRouteTableIds(util_1.flatten(vpnRoutePropagation.map(s => this.selectSubnets(s).subnets)));
if (routeTableIds.length === 0) {
core_1.Annotations.of(this).addError(`enableVpnGateway: no subnets matching selection: '${JSON.stringify(vpnRoutePropagation)}'. Select other subnets to add routes to.`);
}
const routePropagation = new ec2_generated_1.CfnVPNGatewayRoutePropagation(this, 'RoutePropagation', {
routeTableIds,
vpnGatewayId: this._vpnGatewayId,
});
// The AWS::EC2::VPNGatewayRoutePropagation resource cannot use the VPN gateway
// until it has successfully attached to the VPC.
// See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpn-gatewayrouteprop.html
routePropagation.node.addDependency(attachment);
}
/**
* Adds a new VPN connection to this VPC
*/
addVpnConnection(id, options) {
return new vpn_1.VpnConnection(this, id, {
vpc: this,
...options,
});
}
/**
* Adds a new client VPN endpoint to this VPC
*/
addClientVpnEndpoint(id, options) {
return new client_vpn_endpoint_1.ClientVpnEndpoint(this, id, {
...options,
vpc: this,
});
}
/**
* Adds a new interface endpoint to this VPC
*/
addInterfaceEndpoint(id, options) {
return new vpc_endpoint_1.InterfaceVpcEndpoint(this, id, {
vpc: this,
...options,
});
}
/**
* Adds a new gateway endpoint to this VPC
*/
addGatewayEndpoint(id, options) {
return new vpc_endpoint_1.GatewayVpcEndpoint(this, id, {
vpc: this,
...options,
});
}
/**
* Adds a new flow log to this VPC
*/
addFlowLog(id, options) {
return new vpc_flow_logs_1.FlowLog(this, id, {
resourceType: vpc_flow_logs_1.FlowLogResourceType.fromVpc(this),
...options,
});
}
/**
* Returns the id of the VPN Gateway (if enabled)
*/
get vpnGatewayId() {
return this._vpnGatewayId;
}
/**
* Return the subnets appropriate for the placement strategy
*/
selectSubnetObjects(selection = {}) {
selection = this.reifySelectionDefaults(selection);
if (selection.subnets !== undefined) {
return selection.subnets;
}
let subnets;
if (selection.subnetGroupName !== undefined) { // Select by name
subnets = this.selectSubnetObjectsByName(selection.subnetGroupName);
}
else { // Or specify by type
const type = selection.subnetType || SubnetType.PRIVATE_WITH_NAT;
subnets = this.selectSubnetObjectsByType(type);
}
// Apply all the filters
subnets = this.applySubnetFilters(subnets, selection.subnetFilters ?? []);
return subnets;
}
applySubnetFilters(subnets, filters) {
let filtered = subnets;
// Apply each filter in sequence
for (const filter of filters) {
filtered = filter.selectSubnets(filtered);
}
return filtered;
}
selectSubnetObjectsByName(groupName) {
const allSubnets = [...this.publicSubnets, ...this.privateSubnets, ...this.isolatedSubnets];
const subnets = allSubnets.filter(s => util_1.subnetGroupNameFromConstructId(s) === groupName);
if (subnets.length === 0 && !this.incompleteSubnetDefinition) {
const names = Array.from(new Set(allSubnets.map(util_1.subnetGroupNameFromConstructId)));
throw new Error(`There are no subnet groups with name '${groupName}' in this VPC. Available names: ${names}`);
}
return subnets;
}
selectSubnetObjectsByType(subnetType) {
const allSubnets = {
[SubnetType.PRIVATE_ISOLATED]: this.isolatedSubnets,
[SubnetType.ISOLATED]: this.isolatedSubnets,
[SubnetType.PRIVATE_WITH_NAT]: this.privateSubnets,
[SubnetType.PRIVATE]: this.privateSubnets,
[SubnetType.PUBLIC]: this.publicSubnets,
};
const subnets = allSubnets[subnetType];
// Force merge conflict here with https://github.com/aws/aws-cdk/pull/4089
// see ImportedVpc
if (subnets.length === 0 && !this.incompleteSubnetDefinition) {
const availableTypes = Object.entries(allSubnets).filter(([_, subs]) => subs.length > 0).map(([typeName, _]) => typeName);
throw new Error(`There are no '${subnetType}' subnet groups in this VPC. Available types: ${availableTypes}`);
}
return subnets;
}
/**
* Validate the fields in a SubnetSelection object, and reify defaults if necessary
*
* In case of default selection, select the first type of PRIVATE, ISOLATED,
* PUBLIC (in that order) that has any subnets.
*/
reifySelectionDefaults(placement) {
if (placement.subnetName !== undefined) {
if (placement.subnetGroupName !== undefined) {
throw new Error('Please use only \'subnetGroupName\' (\'subnetName\' is deprecated and has the same behavior)');
}
else {
core_1.Annotations.of(this).addWarning('Usage of \'subnetName\' in SubnetSelection is deprecated, use \'subnetGroupName\' instead');
}
placement = { ...placement, subnetGroupName: placement.subnetName };
}
const exclusiveSelections = ['subnets', 'subnetType', 'subnetGroupName'];
const providedSelections = exclusiveSelections.filter(key => placement[key] !== undefined);
if (providedSelections.length > 1) {
throw new Error(`Only one of '${providedSelections}' can be supplied to subnet selection.`);
}
if (placement.subnetType === undefined && placement.subnetGroupName === undefined && placement.subnets === undefined) {
// Return default subnet type based on subnets that actually exist
let subnetType = this.privateSubnets.length
? SubnetType.PRIVATE_WITH_NAT : this.isolatedSubnets.length ? SubnetType.PRIVATE_ISOLATED : SubnetType.PUBLIC;
placement = { ...placement, subnetType: subnetType };
}
// Establish which subnet filters are going to be used
let subnetFilters = placement.subnetFilters ?? [];
// Backwards compatibility with existing `availabilityZones` and `onePerAz` functionality
if (placement.availabilityZones !== undefined) { // Filter by AZs, if specified
subnetFilters.push(subnet_1.SubnetFilter.availabilityZones(placement.availabilityZones));
}
if (!!placement.onePerAz) { // Ensure one per AZ if specified
subnetFilters.push(subnet_1.SubnetFilter.onePerAz());
}
// Overwrite the provided placement filters and remove the availabilityZones and onePerAz properties
placement = { ...placement, subnetFilters: subnetFilters, availabilityZones: undefined, onePerAz: undefined };
const { availabilityZones, onePerAz, ...rest } = placement;
return rest;
}
}
/**
* Name tag constant
*/
const NAME_TAG = 'Name';
/**
* The default tenancy of instances launched into the VPC.
*/
var DefaultInstanceTenancy;
(function (DefaultInstanceTenancy) {
/**
* Instances can be launched with any tenancy.
*/
DefaultInstanceTenancy["DEFAULT"] = "default";
/**
* Any instance launched into the VPC automatically has dedicated tenancy, unless you launch it with the default tenancy.
*/
DefaultInstanceTenancy["DEDICATED"] = "dedicated";
})(DefaultInstanceTenancy = exports.DefaultInstanceTenancy || (exports.DefaultInstanceTenancy = {}));
/**
* Define an AWS Virtual Private Cloud
*
* See the package-level documentation of this package for an overview
* of the various dimensions in which you can configure your VPC.
*
* For example:
*
* ```ts
* const vpc = new ec2.Vpc(this, 'TheVPC', {
* cidr: "10.0.0.0/16"
* })
*
* // Iterate the private subnets
* const selection = vpc.selectSubnets({
* subnetType: ec2.SubnetType.PRIVATE_WITH_NAT
* });
*
* for (const subnet of selection.subnets) {
* // ...
* }
* ```
*
* @resource AWS::EC2::VPC
*/
class Vpc extends VpcBase {
/**
* Vpc creates a VPC that spans a whole region.
* It will automatically divide the provided VPC CIDR range, and create public and private subnets per Availability Zone.
* Network routing for the public subnets will be configured to allow outbound access directly via an Internet Gateway.
* Network routing for the private subnets will be configured to allow outbound access via a set of resilient NAT Gateways (one per AZ).
*/
constructor(scope, id, props = {}) {
super(scope, id);
/**
* List of public subnets in this VPC
*/
this.publicSubnets = [];
/**
* List of private subnets in this VPC
*/
this.privateSubnets = [];
/**
* List of isolated subnets in this VPC
*/
this.isolatedSubnets = [];
/**
* Subnet configurations for this VPC
*/
this.subnetConfiguration = [];
this._internetConnectivityEstablished = new core_1.ConcreteDependable();
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_VpcProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, Vpc);
}
throw error;
}
const stack = core_1.Stack.of(this);
// Can't have enabledDnsHostnames without enableDnsSupport
if (props.enableDnsHostnames && !props.enableDnsSupport) {
throw new Error('To use DNS Hostnames, DNS Support must be enabled, however, it was explicitly disabled.');
}
const cidrBlock = ifUndefined(props.cidr, Vpc.DEFAULT_CIDR_RANGE);
if (core_1.Token.isUnresolved(cidrBlock)) {
throw new Error('\'cidr\' property must be a concrete CIDR string, got a Token (we need to parse it for automatic subdivision)');
}
this.networkBuilder = new network_util_1.NetworkBuilder(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;
// Define a VPC using the provided CIDR range
this.resource = new ec2_generated_1.CfnVPC(this, 'Resource', {
cidrBlock,
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;
core_1.Tags.of(this).add(NAME_TAG, props.vpcName || this.node.path);
this.availabilityZones = stack.availabilityZones;
const maxAZs = props.maxAzs ?? 3;
this.availabilityZones = this.availabilityZones.slice(0, maxAZs);
this.vpcId = this.resource.ref;
this.vpcArn = core_1.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);
// subnetConfiguration must be set before calling createSubnets
this.createSubnets();
const allowOutbound = this.subnetConfiguration.filter(subnet => (subnet.subnetType !== SubnetType.PRIVATE_ISOLATED && subnet.subnetType !== SubnetType.ISOLATED)).length > 0;
// Create an Internet Gateway and attach it if necessary
if (allowOutbound) {
const igw = new ec2_generated_1.CfnInternetGateway(this, 'IGW', {});
this.internetGatewayId = igw.ref;
this._internetConnectivityEstablished.add(igw);
const att = new ec2_generated_1.CfnVPCGatewayAttachment(this, 'VPCGW', {
internetGatewayId: igw.ref,
vpcId: this.resource.ref,
});
this.publicSubnets.forEach(publicSubnet => {
publicSubnet.addDefaultInternetRoute(igw.ref, att);
});
// if gateways are needed create them
if (natGatewayCount > 0) {
const provider = props.natGatewayProvider || nat_1.NatProvider.gateway();
this.createNatGateways(provider, natGatewayCount, natGatewayPlacement);
}
}
if (props.vpnGateway && this.publicSubnets.length === 0 && this.privateSubnets.length === 0 && this.isolatedSubnets.length === 0) {
throw new Error('Can not enable the VPN gateway while the VPC has no subnets at all');
}
if ((props.vpnConnections || props.vpnGatewayAsn) && props.vpnGateway === false) {
throw new Error('Cannot specify `vpnConnections` or `vpnGatewayAsn` when `vpnGateway` is set to false.');
}
if (props.vpnGateway || props.vpnConnections || props.vpnGatewayAsn) {
this.enableVpnGateway({
amazonSideAsn: props.vpnGatewayAsn,
type: vpn_1.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);
}
}
}
/**
* Import a VPC by supplying all attributes directly
*
* NOTE: using `fromVpcAttributes()` with deploy-time parameters (like a `Fn.importValue()` or
* `CfnParameter` to represent a list of subnet IDs) sometimes accidentally works. It happens
* to work for constructs that need a list of subnets (like `AutoScalingGroup` and `eks.Cluster`)
* but it does not work for constructs that need individual subnets (like
* `Instance`). See https://github.com/aws/aws-cdk/issues/4118 for more
* information.
*
* Prefer to use `Vpc.fromLookup()` instead.
*/
static fromVpcAttributes(scope, id, attrs) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_VpcAttributes(attrs);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.fromVpcAttributes);
}
throw error;
}
return new ImportedVpc(scope, id, attrs, false);
}
/**
* Import an existing VPC from by querying the AWS environment this stack is deployed to.
*
* This function only needs to be used to use VPCs not defined in your CDK
* application. If you are looking to share a VPC between stacks, you can
* pass the `Vpc` object between stacks and use it as normal.
*
* Calling this method will lead to a lookup when the CDK CLI is executed.
* You can therefore not use any values that will only be available at
* CloudFormation execution time (i.e., Tokens).
*
* The VPC information will be cached in `cdk.context.json` and the same VPC
* will be used on future runs. To refresh the lookup, you will have to
* evict the value from the cache using the `cdk context` command. See
* https://docs.aws.amazon.com/cdk/latest/guide/context.html for more information.
*/
static fromLookup(scope, id, options) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_VpcLookupOptions(options);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.fromLookup);
}
throw error;
}
if (core_1.Token.isUnresolved(options.vpcId)
|| core_1.Token.isUnresolved(options.vpcName)
|| Object.values(options.tags || {}).some(core_1.Token.isUnresolved)
|| Object.keys(options.tags || {}).some(core_1.Token.isUnresolved)) {
throw new Error('All arguments to Vpc.fromLookup() must be concrete (no Tokens)');
}
const filter = makeTagFilter(options.tags);
// We give special treatment to some tags
if (options.vpcId) {
filter['vpc-id'] = options.vpcId;
}
if (options.vpcName) {
filter['tag:Name'] = options.vpcName;
}
if (options.isDefault !== undefined) {
filter.isDefault = options.isDefault ? 'true' : 'false';
}
const overrides = {};
if (options.region) {
overrides.region = options.region;
}
const attributes = core_1.ContextProvider.getValue(scope, {
provider: cxschema.ContextProvider.VPC_PROVIDER,
props: {
...overrides,
filter,
returnAsymmetricSubnets: true,
subnetGroupNameTag: options.subnetGroupNameTag,
},
dummyValue: undefined,
}).value;
return new LookedUpVpc(scope, id, attributes || DUMMY_VPC_PROPS, attributes === undefined);
/**
* Prefixes all keys in the argument with `tag:`.`
*/
function makeTagFilter(tags) {
const result = {};
for (const [name, value] of Object.entries(tags || {})) {
result[`tag:${name}`] = value;
}
return result;
}
}
/**
* Adds a new S3 gateway endpoint to this VPC
*
* @deprecated use `addGatewayEndpoint()` instead
*/
addS3Endpoint(id, subnets) {
try {
jsiiDeprecationWarnings.print("@aws-cdk/aws-ec2.Vpc#addS3Endpoint", "use `addGatewayEndpoint()` instead");
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.addS3Endpoint);
}
throw error;
}
return new vpc_endpoint_1.GatewayVpcEndpoint(this, id, {
service: vpc_endpoint_1.GatewayVpcEndpointAwsService.S3,
vpc: this,
subnets,
});
}
/**
* Adds a new DynamoDB gateway endpoint to this VPC
*
* @deprecated use `addGatewayEndpoint()` instead
*/
addDynamoDbEndpoint(id, subnets) {
try {
jsiiDeprecationWarnings.print("@aws-cdk/aws-ec2.Vpc#addDynamoDbEndpoint", "use `addGatewayEndpoint()` instead");
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.addDynamoDbEndpoint);
}
throw error;
}
return new vpc_endpoint_1.GatewayVpcEndpoint(this, id, {
service: vpc_endpoint_1.GatewayVpcEndpointAwsService.DYNAMODB,
vpc: this,
subnets,
});
}
createNatGateways(provider, natCount, placement) {
const natSubnets = this.selectSubnetObjects(placement);
for (const sub of natSubnets) {
if (this.publicSubnets.indexOf(sub) === -1) {
throw new Error(`natGatewayPlacement ${placement} contains non public subnet ${sub}`);
}
}
provider.configureNat({
vpc: this,
natSubnets: natSubnets.slice(0, natCount),
privateSubnets: this.privateSubnets,
});
}
/**
* createSubnets creates the subnets specified by the subnet configuration
* array or creates the `DEFAULT_SUBNETS` configuration
*/
createSubnets() {
const remainingSpaceSubnets = [];
for (const subnet of this.subnetConfiguration) {
if (subnet.cidrMask === undefined) {
remainingSpaceSubnets.push(subnet);
continue;
}
this.createSubnetResources(subnet, subnet.cidrMask);
}
const totalRemaining = remainingSpaceSubnets.length * this.availabilityZones.length;
const cidrMaskForRemaining = this.networkBuilder.maskForRemainingSubnets(totalRemaining);
for (const subnet of remainingSpaceSubnets) {
this.createSubnetResources(subnet, cidrMaskForRemaining);
}
}
createSubnetResources(subnetConfig, cidrMask) {
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;
}
// mapPublicIpOnLaunch true in Subnet.Public, false in Subnet.Private or Subnet.Isolated.
let mapPublicIpOnLaunch = false;
if (subnetConfig.subnetType !== SubnetType.PUBLIC && subnetConfig.mapPublicIpOnLaunch !== undefined) {
throw new Error(`${subnetConfig.subnetType} subnet cannot include mapPublicIpOnLaunch parameter`);
}
if (subnetConfig.subnetType === SubnetType.PUBLIC) {
mapPublicIpOnLaunch = (subnetConfig.mapPublicIpOnLaunch !== undefined)
? subnetConfig.mapPublicIpOnLaunch
: true;
}
const name = util_1.subnetId(subnetConfig.name, index);
const subnetProps = {
availabilityZone: zone,
vpcId: this.vpcId,
cidrBlock: this.networkBuilder.addSubnet(cidrMask),
mapPublicIpOnLaunch: mapPublicIpOnLaunch,
};
let subnet;
switch (subnetConfig.subnetType) {
case SubnetType.PUBLIC:
const publicSubnet = new PublicSubnet(this, name, subnetProps);
this.publicSubnets.push(publicSubnet);
subnet = publicSubnet;
break;
case SubnetType.PRIVATE_WITH_NAT:
case SubnetType.PRIVATE:
const privateSubnet = new PrivateSubnet(this, name, subnetProps);
this.privateSubnets.push(privateSubnet);
subnet = privateSubnet;
break;
case SubnetType.PRIVATE_ISOLATED:
case SubnetType.ISOLATED:
const isolatedSubnet = new PrivateSubnet(this, name, subnetProps);
this.isolatedSubnets.push(isolatedSubnet);
subnet = isolatedSubnet;
break;
default:
throw new Error(`Unrecognized subnet type: ${subnetConfig.subnetType}`);
}
// These values will be used to recover the config upon provider import
const includeResourceTypes = [ec2_generated_1.CfnSubnet.CFN_RESOURCE_TYPE_NAME];
core_1.Tags.of(subnet).add(SUBNETNAME_TAG, subnetConfig.name, { includeResourceTypes });
core_1.Tags.of(subnet).add(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), { includeResourceTypes });
});
}
}
exports.Vpc = Vpc;
_a = JSII_RTTI_SYMBOL_1;
Vpc[_a] = { fqn: "@aws-cdk/aws-ec2.Vpc", version: "1.204.0" };
/**
* The default CIDR range used when creating VPCs.
* This can be overridden using VpcProps when creating a VPCNetwork resource.
* e.g. new VpcResource(this, { cidr: '192.168.0.0./16' })
*/
Vpc.DEFAULT_CIDR_RANGE = '10.0.0.0/16';
/**
* The default subnet configuration
*
* 1 Public and 1 Private subnet per AZ evenly split
*/
Vpc.DEFAULT_SUBNETS = [
{
subnetType: SubnetType.PUBLIC,
name: util_1.defaultSubnetName(SubnetType.PUBLIC),
},
{
subnetType: SubnetType.PRIVATE_WITH_NAT,
name: util_1.defaultSubnetName(SubnetType.PRIVATE_WITH_NAT),
},
];
/**
* The default subnet configuration if natGateways specified to be 0
*
* 1 Public and 1 Isolated Subnet per AZ evenly split
*/
Vpc.DEFAULT_SUBNETS_NO_NAT = [
{
subnetType: SubnetType.PUBLIC,
name: util_1.defaultSubnetName(SubnetType.PUBLIC),
},
{
subnetType: SubnetType.PRIVATE_ISOLATED,
name: util_1.defaultSubnetName(SubnetType.PRIVATE_ISOLATED),
},
];
const SUBNETTYPE_TAG = 'aws-cdk:subnet-type';
const SUBNETNAME_TAG = 'aws-cdk:subnet-name';
function subnetTypeTagValue(type) {
switch (type) {
case SubnetType.PUBLIC: return 'Public';
case SubnetType.PRIVATE_WITH_NAT:
case SubnetType.PRIVATE:
return 'Private';
case SubnetType.PRIVATE_ISOLATED:
case SubnetType.ISOLATED:
return 'Isolated';
}
}
/**
* Represents a new VPC subnet resource
*
* @resource AWS::EC2::Subnet
*/
class Subnet extends core_1.Resource {
constructor(scope, id, props) {
super(scope, id);
/**
* Parts of this VPC subnet
*/
this.dependencyElements = [];
this._internetConnectivityEstablished = new core_1.ConcreteDependable();
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_SubnetProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, Subnet);
}
throw error;
}
Object.defineProperty(this, VPC_SUBNET_SYMBOL, { value: true });
core_1.Tags.of(this).add(NAME_TAG, this.node.path);
this.availabilityZone = props.availabilityZone;
this.ipv4CidrBlock = props.cidrBlock;
const subnet = new ec2_generated_1.CfnSubnet(this, 'Subnet', {
vpcId: props.vpcId,
cidrBlock: props.cidrBlock,
availabilityZone: props.availabilityZone,
mapPublicIpOnLaunch: props.mapPublicIpOnLaunch,
});
this.subnetId = subnet.ref;
this.subnetVpcId = subnet.attrVpcId;
this.subnetAvailabilityZone = subnet.attrAvailabilityZone;
this.subnetIpv6CidrBlocks = subnet.attrIpv6CidrBlocks;
this.subnetOutpostArn = subnet.attrOutpostArn;
// subnet.attrNetworkAclAssociationId is the default ACL after the subnet
// was just created. However, the ACL can be replaced at a later time.
this._networkAcl = network_acl_1.NetworkAcl.fromNetworkAclId(this, 'Acl', subnet.attrNetworkAclAssociationId);
this.subnetNetworkAclAssociationId = core_1.Lazy.string({ produce: () => this._networkAcl.networkAclId });
this.node.defaultChild = subnet;
const table = new ec2_generated_1.CfnRouteTable(this, 'RouteTable', {
vpcId: props.vpcId,
});
this.routeTable = { routeTableId: table.ref };
// Associate the public route table for this subnet, to this subnet
new ec2_generated_1.CfnSubnetRouteTableAssociation(this, 'RouteTableAssociation', {
subnetId: this.subnetId,
routeTableId: table.ref,
});
this.internetConnectivityEstablished = this._internetConnectivityEstablished;
}
static isVpcSubnet(x) {
return VPC_SUBNET_SYMBOL in x;
}
static fromSubnetAttributes(scope, id, attrs) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_SubnetAttributes(attrs);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.fromSubnetAttributes);
}
throw error;
}
return new ImportedSubnet(scope, id, attrs);
}
/**
* Import existing subnet from id.
*/
// eslint-disable-next-line @typescript-eslint/no-shadow
static fromSubnetId(scope, id, subnetId) {
return this.fromSubnetAttributes(scope, id, { subnetId });
}
/**
* Create a default route that points to a passed IGW, with a dependency
* on the IGW's attachment to the VPC.
*
* @param gatewayId the logical ID (ref) of the gateway attached to your VPC
* @param gatewayAttachment the gateway attachment construct to be added as a dependency
*/
addDefaultInternetRoute(gatewayId, gatewayAttachment) {
const route = new ec2_generated_1.CfnRoute(this, 'DefaultRoute', {
routeTableId: this.routeTable.routeTableId,
destinationCidrBlock: '0.0.0.0/0',
gatewayId,
});
route.node.addDependency(gatewayAttachment);
// Since the 'route' depends on the gateway attachment, just
// depending on the route is enough.
this._internetConnectivityEstablished.add(route);
}
/**
* Network ACL associated with this Subnet
*
* Upon creation, this is the default ACL which allows all traffic, except
* explicit DENY entries that you add.
*
* You can replace it with a custom ACL which denies all traffic except
* the explicit ALLOW entries that you add by creating a `NetworkAcl`
* object and calling `associateNetworkAcl()`.
*/
get networkAcl() {
return this._networkAcl;
}
/**
* Adds an entry to this subnets route table that points to the passed NATGatewayId
* @param natGatewayId The ID of the NAT gateway
*/
addDefaultNatRoute(natGatewayId) {
this.addRoute('DefaultRoute', {
routerType: RouterType.NAT_GATEWAY,
routerId: natGatewayId,
enablesInternetConnectivity: true,
});
}
/**
* Adds an entry to this subnets route table
*/
addRoute(id, options) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_AddRouteOptions(options);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.addRoute);
}
throw error;
}
if (options.destinationCidrBlock && options.destinationIpv6CidrBlock) {
throw new Error('Cannot specify both \'destinationCidrBlock\' and \'destinationIpv6CidrBlock\'');
}
const route = new ec2_generated_1.CfnRoute(this, id, {
routeTableId: this.routeTable.routeTableId,
destinationCidrBlock: options.destinationCidrBlock || (options.destinationIpv6CidrBlock === undefined ? '0.0.0.0/0' : undefined),
destinationIpv6CidrBlock: options.destinationIpv6CidrBlock,
[routerTypeToPropName(options.routerType)]: options.routerId,
});
if (options.enablesInternetConnectivity) {
this._internetConnectivityEstablished.add(route);
}
}
associateNetworkAcl(id, networkAcl) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_INetworkAcl(networkAcl);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.associateNetworkAcl);
}
throw error;
}
this._networkAcl = networkAcl;
const scope = core_2.Construct.isConstruct(networkAcl) ? networkAcl : this;
const other = core_2.Construct.isConstruct(networkAcl) ? this : networkAcl;
new network_acl_1.SubnetNetworkAclAssociation(scope, id + core_1.Names.nodeUniqueId(other.node), {
networkAcl,
subnet: this,
});
}
}
exports.Subnet = Subnet;
_b = JSII_RTTI_SYMBOL_1;
Subnet[_b] = { fqn: "@aws-cdk/aws-ec2.Subnet", version: "1.204.0" };
/**
* Type of router used in route
*/
var RouterType;
(function (RouterType) {
/**
* Carrier gateway
*/
RouterType["CARRIER_GATEWAY"] = "CarrierGateway";
/**
* Egress-only Internet Gateway
*/
RouterType["EGRESS_ONLY_INTERNET_GATEWAY"] = "EgressOnlyInternetGateway";
/**
* Internet Gateway
*/
RouterType["GATEWAY"] = "Gateway";
/**
* Instance
*/
RouterType["INSTANCE"] = "Instance";
/**
* Local Gateway
*/
RouterType["LOCAL_GATEWAY"] = "LocalGateway";
/**
* NAT Gateway
*/
RouterType["NAT_GATEWAY"] = "NatGateway";
/**
* Network Interface
*/
RouterType["NETWORK_INTERFACE"] = "NetworkInterface";
/**
* Transit Gateway
*/
RouterType["TRANSIT_GATEWAY"] = "TransitGateway";
/**
* VPC peering connection
*/
RouterType["VPC_PEERING_CONNECTION"] = "VpcPeeringConnection";
/**
* VPC Endpoint for gateway load balancers
*/
RouterType["VPC_ENDPOINT"] = "VpcEndpoint";
})(RouterType = exports.RouterType || (exports.RouterType = {}));
function routerTypeToPropName(routerType) {
return ({
[RouterType.CARRIER_GATEWAY]: 'carrierGatewayId',
[RouterType.EGRESS_ONLY_INTERNET_GATEWAY]: 'egressOnlyInternetGatewayId',
[RouterType.GATEWAY]: 'gatewayId',
[RouterType.INSTANCE]: 'instanceId',
[RouterType.LOCAL_GATEWAY]: 'localGatewayId',
[RouterType.NAT_GATEWAY]: 'natGatewayId',
[RouterType.NETWORK_INTERFACE]: 'networkInterfaceId',
[RouterType.TRANSIT_GATEWAY]: 'transitGatewayId',
[RouterType.VPC_PEERING_CONNECTION]: 'vpcPeeringConnectionId',
[RouterType.VPC_ENDPOINT]: 'vpcEndpointId',
})[routerType];
}
/**
* Represents a public VPC subnet resource
*/
class PublicSubnet extends Subnet {
constructor(scope, id, props) {
super(scope, id, props);
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_PublicSubnetProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, PublicSubnet);
}
throw error;
}
}
static fromPublicSubnetAttributes(scope, id, attrs) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_PublicSubnetAttributes(attrs);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.fromPublicSubnetAttributes);
}
throw error;
}
return new ImportedSubnet(scope, id, attrs);
}
/**
* Creates a new managed NAT gateway attached to this public subnet.
* Also adds the EIP for the managed NAT.
* @returns A ref to the the NAT Gateway ID
*/
addNatGateway(eipAllocationId) {
// Create a NAT Gateway in this public subnet
const ngw = new ec2_generated_1.CfnNatGateway(this, 'NATGateway', {
subnetId: this.subnetId,
allocationId: eipAllocationId ?? new ec2_generated_1.CfnEIP(this, 'EIP', {
domain: 'vpc',
}).attrAllocationId,
});
return ngw;
}
}
exports.PublicSubnet = PublicSubnet;
_c = JSII_RTTI_SYMBOL_1;
PublicSubnet[_c] = { fqn: "@aws-cdk/aws-ec2.PublicSubnet", version: "1.204.0" };
/**
* Represents a private VPC subnet resource
*/
class PrivateSubnet extends Subnet {
constructor(scope, id, props) {
super(scope, id, props);
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_PrivateSubnetProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, PrivateSubnet);
}
throw error;
}
}
static fromPrivateSubnetAttributes(scope, id, attrs) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_ec2_PrivateSubnetAttributes(attrs);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.fromPrivateSubnetAttributes);
}
throw error;
}
return new ImportedSubnet(scope, id, attrs);
}
}
exports.PrivateSubnet = PrivateSubnet;
_d = JSII_RTTI_SYMBOL_1;
PrivateSubnet[_d] = { fqn: "@aws-cdk/aws-ec2.PrivateSubnet", version: "1.204.0" };
function ifUndefined(value, defaultValue) {
return value ?? defaultValue;
}
class ImportedVpc extends VpcBase {
constructor(scope, id, props, isIncomplete) {
super(scope, id);
this.internetConnectivityEstablished = new core_1.ConcreteDependable();
this.vpcId = props.vpcId;
this.vpcArn = core_1.Arn.format({
service: 'ec2',
resource: 'vpc',
resourceName: this.vpcId,
}, core_1.Stack.of(this));
this.cidr = props.vpcCidrBlock;
this.availabilityZones = props.availabilityZones;
this._vpnGatewayId = props.vpnGatewayId;
this.incompleteSubnetDefinition = isIncomplete;
// None of the values may be unresolved list tokens
for (const k of Object.keys(props)) {
if (Array.isArray(props[k]) && core_1.Token.isUnresolved(props[k])) {
core_1.Annotations.of(this).addWarning(`fromVpcAttributes: '${k}' is a list token: the imported VPC will not work with constructs that require a list of subnets at synthesis time. Use 'Vpc.fromLookup()' or 'Fn.importListValue' instead.`);
}
}
/* eslint-disable max-len */
const pub = new util_1.ImportSubnetGroup(props.publicSubnetIds, props.publicSubnetNames, props.publicSubnetRouteTableIds, SubnetType.PUBLIC, this.availabilityZones, 'publicSubnetIds', 'publicSubnetNames', 'publicSubnetRouteTableIds');
const priv = new util_1.ImportSubnetGroup(props.privateSubnetIds, props.privateSubnetNames, props.privateSubnetRouteTableIds, SubnetType.PRIVATE_WITH_NAT, this.availabilityZones, 'privateSubnetIds', 'privateSubnetNames', 'privateSubnetRouteTableIds');
const iso = new util_1.ImportSubnetGroup(props.isolatedSubnetIds, props.isolatedSubnetNames, props.isolatedSubnetRouteTableIds, SubnetType.PRIVATE_ISOLATED, this.availabilityZones, 'isolatedSubnetIds', 'isolatedSubnetNames', 'isolatedSubnetRouteTableIds');
/* eslint-enable max-len */
this.publicSubnets = pub.import(this);
this.privateSubnets = priv.import(this);
this.isolatedSubnets = iso.import(this);
}
get vpcCidrBlock() {
if (this.cidr === undefined) {
throw new Error('Cannot perform this operation: \'vpcCidrBlock\' was not supplied when creating this VPC');
}
return this.cidr;
}
}
class LookedUpVpc extends VpcBase {
constructor(scope, id, props, isIncomplete) {
super(scope, id);
this.internetConnectivityEstablished = new core_1.ConcreteDependable();
this.vpcId = props.vpcId;
this.vpcArn = core_1.Arn.format({
service: 'ec2',
resource: 'vpc',
resourceName: this.vpcId,
}, core_1.Stack.of(this));
this.cidr = props.vpcCidrBlock;
this._vpnGatewayId = props.vpnGatewayId;
this.incompleteSubnetDefinition = isIncomplete;
const subnetGroups = props.subnetGroups || [];
const availabilityZones = Array.from(new Set(flatMap(subnetGroups, subnetGroup => {
return subnetGroup.subnets.map(subnet => subnet.availabilityZone);
})));
availabilityZones.sort((az1, az2) => az1.localeCompare(az2));
this.availabilityZones = availabilityZones;
this.publicSubnets = this.extractSubnetsOfType(subnetGroups, cxapi.VpcSubnetGroupType.PUBLIC);
this.privateSubnets = this.extractSubnetsOfType(subnetGroups, cxapi.VpcSubnetGroupType.PRIVATE);
this.isolatedSubnets = this.extractSubnetsOfType(subnetGroups, cxapi.VpcSubnetGroupType.ISOLATED);
}
get vpcCidrBlock() {
if (this.cidr === undefined) {
// Value might be cached from an old CLI version, so bumping the CX API protocol to
// force the value to exist would not have helped.
throw new Error('Cannot perform this operation: \'vpcCidrBlock\' was not found when looking up this VPC. Use a newer version of the CDK CLI and clear the old context value.');
}
return this.cidr;
}
extractSubnetsOfType(subnetGroups, subnetGroupType) {
return flatMap(subnetGroups.filter(subnetGroup => subnetGroup.type === subnetGroupType), subnetGroup => this.subnetGroupToSubnets(subnetGroup));
}
subnetGroupToSubnets(subnetGroup) {
const ret = new Array();
for (let i = 0; i < subnetGroup.subnets.length; i++) {
const vpcSubnet = subnetGroup.subnets[i];
ret.push(Subnet.fromSubnetAttributes(this, `${subnetGroup.name}Subnet${i + 1}`, {
availabilityZone: vpcSubnet.availabilityZone,
subnetId: vpcSubnet.subnetId,
routeTableId: vpcSubnet.routeTableId,
ipv4CidrBlock: vpcSubnet.cidr,
}));
}
return ret;
}
}
function flatMap(xs, fn) {
const ret = new Array();
for (const x of xs) {
ret.push(...fn(x));
}
return ret;
}
class CompositeDependable {
constructor() {
this.dependables = new Array();
const self = this;
core_1.DependableTrait.implement(this, {
get dependencyRoots() {
const ret = new Array();
for (const dep of self.dependables) {
ret.push(...core_1.DependableTrait.get(dep).dependencyRoots);
}
return ret;
},
});
}
/**
* Add a construct to the dependency roots
*/
add(dep) {
this.dependables.push(dep);
}
}
/**
* In