@pulumi/awsx
Version:
[](https://github.com/pulumi/pulumi-awsx/actions) [](https://slack.pulumi.com) [;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.Vpc = void 0;
const aws = require("@pulumi/aws");
const pulumi = require("@pulumi/pulumi");
const internetGateway_1 = require("./internetGateway");
const natGateway_1 = require("./natGateway");
const subnet_1 = require("./subnet");
const topology = require("./vpcTopology");
const utils = require("../utils");
// Mapping from provider to Vpc. 'undefined' is used to encode the Vpc we create when no provider
// is passed in.
const providerToDefaultVpc = new Map();
class VpcData {
/** @internal */
constructor(name, parent, args, opts) {
// Convenience properties. Equivalent to getting the IDs from the corresponding XxxSubnets
// properties.
/** @internal */
this.publicSubnetIds = [];
/** @internal */
this.privateSubnetIds = [];
/** @internal */
this.isolatedSubnetIds = [];
/** @internal */
this.publicSubnets = [];
/** @internal */
this.privateSubnets = [];
/** @internal */
this.isolatedSubnets = [];
/**
* @internal
* The nat gateways created to allow private subnets access to the internet.
* Only available if this was created using [VpcArgs].
*/
this.natGateways = [];
this.parent = parent;
if (isExistingVpcArgs(args)) {
this.vpc = args.vpc;
this.id = this.vpc.id;
}
else if (isExistingVpcIdArgs(args)) {
this.vpc = aws.ec2.Vpc.get(name, args.vpcId, {}, opts),
this.id = this.vpc.id;
getExistingSubnets(this, this.parent, name, "public", args.publicSubnetIds);
getExistingSubnets(this, this.parent, name, "private", args.privateSubnetIds);
getExistingSubnets(this, this.parent, name, "isolated", args.isolatedSubnetIds);
// Pass along aliases so that the previously unparented resources are now properly parented
// to the vpc.
if (args.internetGatewayId) {
const igName = `${name}-ig`;
this.internetGateway = new internetGateway_1.InternetGateway(igName, parent, {
internetGateway: aws.ec2.InternetGateway.get(igName, args.internetGatewayId, {}, { parent }),
}, { parent, aliases: [{ parent: pulumi.rootStackResource }] });
}
if (args.natGatewayIds) {
for (let i = 0, n = args.natGatewayIds.length; i < n; i++) {
const natGatewayId = args.natGatewayIds[i];
const natName = `${name}-nat-${i}`;
this.natGateways.push(new natGateway_1.NatGateway(natName, parent, {
natGateway: aws.ec2.NatGateway.get(natName, natGatewayId, {}, { parent }),
}, { parent, aliases: [{ parent: pulumi.rootStackResource }] }));
}
}
}
else {
const cidrBlock = args.cidrBlock === undefined ? "10.0.0.0/16" : args.cidrBlock;
const availabilityZones = args.availabilityZones;
const numberOfNatGateways = args.numberOfNatGateways === undefined ? availabilityZones.length : args.numberOfNatGateways;
const assignGeneratedIpv6CidrBlock = utils.ifUndefined(args.assignGeneratedIpv6CidrBlock, false);
// We previously did not parent the underlying Vpc to this component. We now do. Provide
// an alias so this doesn't cause resources to be destroyed/recreated for existing
// stacks.
this.vpc = new aws.ec2.Vpc(name, {
...args,
cidrBlock,
enableDnsHostnames: utils.ifUndefined(args.enableDnsHostnames, true),
enableDnsSupport: utils.ifUndefined(args.enableDnsSupport, true),
instanceTenancy: utils.ifUndefined(args.instanceTenancy, "default"),
assignGeneratedIpv6CidrBlock,
}, { parent, aliases: [{ parent: pulumi.rootStackResource }] });
this.id = this.vpc.id;
const subnets = args.subnets || [
{ type: "public" },
{ type: "private" },
];
this.partition(name, cidrBlock, availabilityZones, numberOfNatGateways, assignGeneratedIpv6CidrBlock, subnets, opts);
// Create an internet gateway if we have public subnets.
this.addInternetGateway(name, this.publicSubnets);
}
}
/** @internal */
partition(name, cidrBlock, availabilityZones, numberOfNatGateways, assignGeneratedIpv6CidrBlock, subnetArgs, opts) {
// Create the appropriate subnets. Default to a single public and private subnet for each
// availability zone if none were specified.
const { subnets, natGateways, natRoutes } = topology.create(this.parent, name, cidrBlock, this.vpc.ipv6CidrBlock, availabilityZones, numberOfNatGateways, assignGeneratedIpv6CidrBlock, subnetArgs);
for (const desc of subnets) {
// We previously did not parent the subnet to this component. We now do. Provide an
// alias so this doesn't cause resources to be destroyed/recreated for existing
// stacks.
// Only set one of availabilityZone or availabilityZoneId
const availabilityZone = desc.args.availabilityZone;
const availabilityZoneId = availabilityZone ? undefined : desc.args.availabilityZoneId;
const subnet = new subnet_1.Subnet(desc.subnetName, this.parent, {
...desc.args,
availabilityZone,
availabilityZoneId,
tags: utils.mergeTags({ type: desc.type, Name: desc.subnetName }, desc.args.tags),
}, { aliases: [{ parent: opts.parent }], ignoreChanges: desc.ignoreChanges, parent: this.parent });
this.addSubnet(desc.type, subnet);
}
for (const desc of natGateways) {
const publicSubnet = this.publicSubnets.find(s => s.subnetName === desc.publicSubnet);
if (!publicSubnet) {
throw new pulumi.ResourceError(`Could not find public subnet named ${desc.publicSubnet}`, this.parent);
}
this.addNatGateway(desc.name, { subnet: publicSubnet });
}
for (const desc of natRoutes) {
const privateSubnet = this.privateSubnets.find(s => s.subnetName === desc.privateSubnet);
if (!privateSubnet) {
throw new pulumi.ResourceError(`Could not find private subnet named ${desc.privateSubnet}`, this.parent);
}
const natGateway = this.natGateways.find(g => g.natGatewayName === desc.natGateway);
if (!natGateway) {
throw new pulumi.ResourceError(`Could not find nat gateway named ${desc.natGateway}`, this.parent);
}
privateSubnet.createRoute(desc.name, natGateway);
}
}
/** @internal */
addSubnet(type, subnet) {
this.getSubnets(type).push(subnet);
this.getSubnetIds(type).push(subnet.id);
}
/** @internal */
getSubnets(type) {
switch (type) {
case "public": return this.publicSubnets;
case "private": return this.privateSubnets;
case "isolated": return this.isolatedSubnets;
default: throw new Error("Unexpected subnet type: " + type);
}
}
/** @internal */
getSubnetIds(type) {
switch (type) {
case "public": return this.publicSubnetIds;
case "private": return this.privateSubnetIds;
case "isolated": return this.isolatedSubnetIds;
default: throw new Error("Unexpected subnet type: " + type);
}
}
/**
* @internal
* Adds an [awsx.ec2.InternetGateway] to this VPC. Will fail if this Vpc already has an
* InternetGateway.
*
* @param subnets The subnets to route the InternetGateway to. Will default to the [public]
* subnets of this Vpc if not specified.
*/
addInternetGateway(name, subnets, args = {}, opts = {}) {
if (this.internetGateway) {
throw new Error("Cannot add InternetGateway to Vpc that already has one.");
}
// See https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html#Add_IGW_Attach_Gateway
// for more details.
this.internetGateway = new internetGateway_1.InternetGateway(name, this.parent, args, opts);
subnets = subnets || this.publicSubnets;
for (const subnet of subnets) {
subnet.createRoute("ig", this.internetGateway);
}
return this.internetGateway;
}
/**
* @internal
* Adds an [awsx.ec2.NatGateway] to this VPC. The NatGateway must be supplied a subnet (normally
* public) to be placed in. After adding the NatGateway you should update the route table
* associated with one or more of your private subnets to point Internet-bound traffic to the
* NAT gateway. This enables instances in your private subnets to communicate with the internet.
*
* This can be done by calling [subnet.createRoute] and passing in the newly created NatGateway.
*/
addNatGateway(name, args, opts = {}) {
const natGateway = new natGateway_1.NatGateway(name, this.parent, args, opts);
this.natGateways.push(natGateway);
return natGateway;
}
}
VpcData.doNotCapture = true;
utils.Capture(VpcData.prototype).addInternetGateway.doNotCapture = true;
utils.Capture(VpcData.prototype).addNatGateway.doNotCapture = true;
utils.Capture(VpcData.prototype).partition.doNotCapture = true;
class Vpc extends pulumi.ComponentResource {
constructor(name, args, opts = {}) {
super("awsx:x:ec2:Vpc", name, { name, args, opts }, opts);
const data = this.getData();
this.id = pulumi.output(data.then(v => v.id));
this.vpc = pulumi.output(data.then(d => d.vpc));
}
initialize(props) {
const name = props.name;
const args = props.args;
const opts = props.opts;
if (isExistingVpcArgs(args)) {
return this.initializeExistingVpcArgs(name, args, opts);
}
else if (isDefaultVpcArgs(args)) {
return this.initializeDefaultVpcArgs(name, args, opts);
}
else if (isExistingVpcIdArgs(args)) {
return this.initializeExistingVpcIdArgs(name, args, opts);
}
else {
return this.initializeVpcArgs(name, args, opts);
}
}
/** @internal */
async initializeExistingVpcArgs(name, args, opts) {
return new VpcData(name, this, args, opts);
}
/** @internal */
async initializeExistingVpcIdArgs(name, args, opts) {
return new VpcData(name, this, args, opts);
}
/** @internal */
async initializeDefaultVpcArgs(name, args, opts) {
// back compat. We always would just use the first two public subnets of the region
// we're in. So preserve that, even though we could get all of them here. Pulling in
// more than the two we pulled in before could have deep implications for clients as
// those subnets are used to make many downstream resource-creating decisions.
const vpcId = await args.vpcId;
const provider = args.provider;
const getSubnetsResult = await aws.ec2.getSubnets({
filters: [{ name: "vpc-id", values: [vpcId] }],
}, { provider, async: true });
const publicSubnetIds = getSubnetsResult.ids.slice(0, 2);
return new VpcData(name, this, {
vpcId,
publicSubnetIds,
}, opts);
}
/** @internal */
async initializeVpcArgs(name, args, opts) {
const provider = Vpc.getProvider(opts);
const availabilityZones = await getAvailabilityZones(opts.parent, provider, args.requestedAvailabilityZones ?? args.numberOfAvailabilityZones);
return new VpcData(name, this, {
...args,
availabilityZones,
}, opts);
}
static getProvider(opts = {}) {
// Pull out the provider to ensure we're looking up the default vpc in the right location.
// Note that we do not pass 'parent' along as we want the default vpc to always be parented
// logically by hte stack.
const provider = opts.provider ? opts.provider :
opts.parent ? opts.parent.getProvider("aws::") : undefined;
return provider;
}
async addInternetGateway(name, subnets, args = {}, opts = {}) {
const vpc = await this.getData();
vpc.addInternetGateway(name, subnets, args, opts);
}
async addNatGateway(name, args, opts = {}) {
const vpc = await this.getData();
vpc.addNatGateway(name, args, opts);
}
/**
* Get an existing Vpc resource's state with the given name and IDs of its relevant
* sub-resources. This will not cause a VPC (or any sub-resources) to be created, and removing
* this Vpc from your pulumi application will not cause the existing cloud resource (or
* sub-resources) to be destroyed.
*/
static fromExistingIds(name, idArgs, opts) {
return new Vpc(name, idArgs, opts);
}
/**
* Gets the default vpc for the current aws account and region.
*
* See https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html for more details.
*
* Note: the no-arg version of this call is not recommended. It will acquire the default Vpc
* for the current region and cache it. Instead, it is recommended that the `getDefault(opts)`
* version be used instead with either `opts.provider` or `opts.parent` set. This version will
* properly get the default vpc for the region the provider specifies.
*
* This method will return the same Vpc instance when passed the same `provider`.
*/
static getDefault(opts = {}) {
// Pull out the provider to ensure we're looking up the default vpc in the right location.
// Note that we do not pass 'parent' along as we want the default vpc to always be parented
// logically by hte stack.
const provider = Vpc.getProvider(opts);
let vpc = providerToDefaultVpc.get(provider);
if (!vpc) {
const vpcName = "default-" + (provider ? provider.__name : "vpc");
// For back compat with how we previously named things, also create an alias from
// "default-vpc" to this name for the very first default Vpc we create as that's how we
// used to name them.
const aliases = providerToDefaultVpc.size === 0
? [{ name: "default-vpc" }]
: [];
// Also add an alias to "default-vpc-id". This is the name we generated for
// default-vpcs when we had sync data-sources.
const vpcId = aws.ec2.getVpc({ default: true }, { provider, async: true }).then(r => r.id);
aliases.push({ name: vpcId.then(vpcId => "default-" + vpcId) });
vpc = new Vpc(vpcName, {
isDefault: true,
vpcId,
provider,
}, { provider, aliases });
providerToDefaultVpc.set(provider, vpc);
}
return vpc;
}
// lifted members of VpcData
/** @internal */
async liftMember(func, def) {
const data = await this.getData();
if (!data) {
return def;
}
return func(data);
}
/**
* Asynchronously retrieves the IDs for the public subnets in this Vpc. This will only retrieve
* data for the subnets specified when the Vpc was created. If subnets were created externally,
* they will not be included.
*/
get publicSubnetIds() { return this.liftMember(v => v.publicSubnetIds, []); }
/**
* Asynchronously retrieves the IDs for the private subnets in this Vpc. This will only retrieve
* data for the subnets specified when the Vpc was created. If subnets were created externally,
* they will not be included.
*/
get privateSubnetIds() { return this.liftMember(v => v.privateSubnetIds, []); }
/**
* Asynchronously retrieves the IDs for the isolated subnets in this Vpc. This will only retrieve
* data for the subnets specified when the Vpc was created. If subnets were created externally,
* they will not be included.
*/
get isolatedSubnetIds() { return this.liftMember(v => v.isolatedSubnetIds, []); }
/**
* Asynchronously retrieves the IDs for the subnets of a particular type in this Vpc. This will
* only retrieve data for the subnets specified when the Vpc was created. If subnets were
* created externally, they will not be included.
*/
getSubnetsIds(type) { return this.liftMember(d => d.getSubnetIds(type), []); }
/**
* Asynchronously retrieves the public subnets in this Vpc. This will only retrieve data for
* the subnets specified when the Vpc was created. If subnets were created externally, they
* will not be included.
*/
get publicSubnets() { return this.liftMember(v => v.publicSubnets, []); }
/**
* Asynchronously retrieves the private subnets in this Vpc. This will only retrieve data for
* the subnets specified when the Vpc was created. If subnets were created externally, they
* will not be included.
*/
get privateSubnets() { return this.liftMember(v => v.privateSubnets, []); }
/**
* Asynchronously retrieves the isolated subnets in this Vpc. This will only retrieve data for
* the subnets specified when the Vpc was created. If subnets were created externally, they
* will not be included.
*/
get isolatedSubnets() { return this.liftMember(v => v.isolatedSubnets, []); }
/**
* Asynchronously retrieves the subnets of a particular type in this Vpc. This will only
* retrieve data for the subnets specified when the Vpc was created. If subnets were created
* externally, they will not be included.
*/
getSubnets(type) { return this.liftMember(d => d.getSubnets(type), []); }
/**
* The internet gateway created to allow traffic to/from the internet to the public subnets.
* Only available if this was created using [VpcArgs].
*/
get internetGateway() { return this.liftMember(v => v.internetGateway, undefined); }
/**
* The nat gateways created to allow private subnets access to the internet.
* Only available if this was created using [VpcArgs].
*/
get natGateways() { return this.liftMember(v => v.natGateways, []); }
}
exports.Vpc = Vpc;
utils.Capture(Vpc.prototype).addInternetGateway.doNotCapture = true;
utils.Capture(Vpc.prototype).addNatGateway.doNotCapture = true;
utils.Capture(Vpc.prototype).initializeDefaultVpcArgs.doNotCapture = true;
utils.Capture(Vpc.prototype).initializeExistingVpcArgs.doNotCapture = true;
utils.Capture(Vpc.prototype).initializeExistingVpcIdArgs.doNotCapture = true;
utils.Capture(Vpc.prototype).initializeVpcArgs.doNotCapture = true;
async function getAvailabilityZones(parent, provider, requestedZones = 2) {
const result = await aws.getAvailabilityZones(/*args:*/ undefined, { provider, async: true });
if (result.names.length !== result.zoneIds.length) {
throw new pulumi.ResourceError("Availability zones for region had mismatched names and ids.", parent);
}
const descriptions = result.names.map((name, idx) => ({ name, id: result.zoneIds[idx] }));
if (Array.isArray(requestedZones) || typeof requestedZones === "object") {
return new Promise((resolve, reject) => {
pulumi.Output.create(requestedZones).apply(requestedZones => {
const mappedZones = descriptions.filter(zone => requestedZones.includes(zone.name));
mappedZones.length === requestedZones.length ?
resolve(mappedZones) :
reject(new pulumi.ResourceError("Availability zones did not match requested zones", parent));
});
});
}
else {
return descriptions.slice(0, requestedZones === "all" ? descriptions.length : requestedZones);
}
}
function getExistingSubnets(vpcData, vpc, vpcName, type, inputs = []) {
const subnets = vpcData.getSubnets(type);
const subnetIds = vpcData.getSubnetIds(type);
for (let i = 0, n = inputs.length; i < n; i++) {
const subnetName = `${vpcName}-${type}-${i}`;
const subnet = new subnet_1.Subnet(subnetName, vpc, {
subnet: aws.ec2.Subnet.get(subnetName, inputs[i], /*state:*/ undefined, { parent: vpc }),
}, { parent: vpc });
subnets.push(subnet);
subnetIds.push(subnet.id);
}
}
getExistingSubnets.doNotCapture = true;
function isExistingVpcIdArgs(obj) {
return !!obj.vpcId;
}
function isDefaultVpcArgs(obj) {
return obj.isDefault === true;
}
function isExistingVpcArgs(obj) {
return !!obj.vpc;
}
// Make sure our exported args shape is compatible with the overwrite shape we're trying to provide.
const test1 = utils.checkCompat();
//# sourceMappingURL=vpc.js.map