UNPKG

@aws-cdk/aws-ec2

Version:

The CDK Construct Library for AWS::EC2

1,177 lines 197 kB
"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