Repository: pulumi/pulumi-awsx
Pull request #792 (2022-04-28 14:38:58)

AWS VPC component works with default args.


Patch:
diff --git a/awsx-classic/tslint.json b/awsx-classic/tslint.json
index b6bdfb36..d21af548 100644
--- a/awsx-classic/tslint.json
+++ b/awsx-classic/tslint.json
@@ -26,10 +26,7 @@
         "jsdoc-format": false,
         "label-position": true,
         "member-access": false,
-        "member-ordering": [
-            true,
-            "variables-before-functions"
-        ],
+        "member-ordering": false,
         "no-any": false,
         "no-arg": true,
         "no-bitwise": false,
@@ -65,7 +62,7 @@
             "check-open-brace",
             "check-whitespace"
         ],
-        "ordered-imports": true,
+        "ordered-imports": false,
         "prefer-const": true,
         "quotemark": [
             true,
@@ -119,4 +116,4 @@
             "check-type"
         ]
     }
-}
+}
\ No newline at end of file
diff --git a/awsx/ec2/vpc.ts b/awsx/ec2/vpc.ts
new file mode 100644
index 00000000..81d7f505
--- /dev/null
+++ b/awsx/ec2/vpc.ts
@@ -0,0 +1,362 @@
+// Copyright 2016-2022, Pulumi Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// 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.
+
+import * as aws from "@pulumi/aws";
+import * as pulumi from "@pulumi/pulumi";
+import * as schema from "../schema-types";
+import { getSubnetSpecs, SubnetSpec } from "./subnetDistributor";
+
+interface VpcData {
+    vpc: aws.ec2.Vpc;
+    subnets: aws.ec2.Subnet[];
+    routeTables: aws.ec2.RouteTable[];
+    routes: aws.ec2.Route[];
+    routeTableAssociations: aws.ec2.RouteTableAssociation[];
+    igw: aws.ec2.InternetGateway;
+    natGateways: aws.ec2.NatGateway[];
+    eips: aws.ec2.Eip[];
+}
+
+export class Vpc extends schema.Vpc<VpcData> {
+    constructor(
+        name: string,
+        args: schema.VpcArgs,
+        opts: pulumi.ComponentResourceOptions = {},
+    ) {
+        super(name, args, opts);
+
+        const data = pulumi.output(this.getData());
+
+        this.vpc = data.vpc;
+        this.subnets = data.subnets;
+        this.routeTables = data.routeTables;
+        this.routes = data.routes;
+        this.routeTableAssociations = data.routeTableAssociations;
+        this.internetGateway = data.igw;
+        this.natGateways = data.natGateways;
+        this.eips = data.eips;
+    }
+
+    protected async initialize(props: {
+        name: string;
+        args: schema.VpcArgs;
+        opts: pulumi.ComponentResourceOptions;
+    }) {
+        const { name, args } = props;
+        const availabilityZones =
+            args.availabilityZoneNames ?? (await this.getDefaultAzs());
+
+        const natGatewayStrategy: schema.NatGatewayStrategyInputs =
+            args.natGateways?.strategy ?? "OnePerAz";
+        const allocationIds = args.natGateways?.elasticIpAllocationIds ?? [];
+        validateEips(natGatewayStrategy, allocationIds, availabilityZones);
+
+        const cidrBlock = args.cidrBlock ?? "10.0.0.0/16";
+
+        const subnetSpecs = getSubnetSpecs(
+            name,
+            cidrBlock,
+            availabilityZones,
+            args.subnetsPerAz,
+        );
+        validateNatGatewayStrategy(natGatewayStrategy, subnetSpecs);
+
+        const vpc = new aws.ec2.Vpc(
+            name,
+            {
+                ...args,
+                cidrBlock,
+                tags: {
+                    ...args.tags,
+                    Name: name,
+                },
+            },
+            { parent: this },
+        );
+
+        // We unconditionally create the IGW (even if it's not needed because we
+        // only have isolated subnets) because AWS does not charge for it, and
+        // therefore there's no harm in adding it, whereas conditional resources
+        // must be declared in the constructor, which significantly complicates the
+        // code.
+        const igw = new aws.ec2.InternetGateway(
+            `${name}`,
+            {
+                vpcId: vpc.id,
+            },
+            { parent: vpc, dependsOn: [vpc] },
+        );
+
+        const subnets: aws.ec2.Subnet[] = [];
+        const routeTables: aws.ec2.RouteTable[] = [];
+        const routeTableAssociations: aws.ec2.RouteTableAssociation[] = [];
+        const routes: aws.ec2.Route[] = [];
+        const natGateways: aws.ec2.NatGateway[] = [];
+        const eips: aws.ec2.Eip[] = [];
+
+        for (let i = 0; i < availabilityZones.length; i++) {
+            subnetSpecs
+                .filter((x) => x.azName === availabilityZones[i])
+                .sort(compareSubnetSpecs)
+                .forEach((spec) => {
+                    const subnet = new aws.ec2.Subnet(
+                        spec.subnetName,
+                        {
+                            vpcId: vpc.id,
+                            availabilityZone: spec.azName,
+                            mapPublicIpOnLaunch: spec.type === "Public",
+                            cidrBlock: spec.cidrBlock,
+                            tags: {
+                                Name: spec.subnetName,
+                            },
+                        },
+                        { parent: vpc, dependsOn: [vpc] },
+                    );
+                    subnets.push(subnet);
+
+                    const routeTable = new aws.ec2.RouteTable(
+                        spec.subnetName,
+                        {
+                            vpcId: vpc.id,
+                            tags: {
+                                Name: spec.subnetName,
+                            },
+                        },
+                        { parent: subnet, dependsOn: [subnet] },
+                    );
+                    routeTables.push(routeTable);
+
+                    const routeTableAssoc = new aws.ec2.RouteTableAssociation(
+                        spec.subnetName,
+                        {
+                            routeTableId: routeTable.id,
+                            subnetId: subnet.id,
+                        },
+                        { parent: routeTable, dependsOn: [routeTable] },
+                    );
+                    routeTableAssociations.push(routeTableAssoc);
+
+                    if (
+                        spec.type === "Public" &&
+                        shouldCreateNatGateway(
+                            natGatewayStrategy,
+                            natGateways.length,
+                            i,
+                        )
+                    ) {
+                        const createEip = allocationIds.length === 0;
+
+                        if (createEip) {
+                            const eip = new aws.ec2.Eip(
+                                `${name}-${i + 1}`,
+                                {},
+                                { parent: subnet, dependsOn: [subnet] },
+                            );
+                            eips.push(eip);
+                        }
+
+                        const natGateway = new aws.ec2.NatGateway(
+                            `${name}-${i + 1}`,
+                            {
+                                subnetId: subnet.id,
+                                allocationId: createEip
+                                    ? eips[i].allocationId
+                                    : allocationIds[i],
+                                tags: {
+                                    Name: `${name}-${i + 1}`,
+                                },
+                            },
+                            { parent: subnet, dependsOn: [subnet] },
+                        );
+                        natGateways.push(natGateway);
+                    }
+
+                    if (spec.type === "Public") {
+                        // Public subnets communicate directly with the internet via the Internet Gateway.
+                        const route = new aws.ec2.Route(
+                            spec.subnetName,
+                            {
+                                routeTableId: routeTable.id,
+                                gatewayId: igw.id,
+                                destinationCidrBlock: "0.0.0.0/0",
+                            },
+                            { parent: routeTable, dependsOn: [routeTable] },
+                        );
+                        routes.push(route);
+                    } else if (spec.type === "Private") {
+                        // Private subnets communicate indirectly with the internet via a NAT Gateway.
+
+                        // Because we've already validated the strategy and have ensured that public subnets are created
+                        // first via the sort above, we know the necessary NAT Gateway already exists.
+                        const natGatewayId =
+                            natGatewayStrategy === "Single"
+                                ? natGateways[0].id
+                                : natGateways[i].id;
+
+                        const route = new aws.ec2.Route(
+                            spec.subnetName,
+                            {
+                                routeTableId: routeTable.id,
+                                natGatewayId,
+                                destinationCidrBlock: "0.0.0.0/0",
+                            },
+                            { parent: routeTable, dependsOn: [routeTable] },
+                        );
+                        routes.push(route);
+                    }
+
+                    // Isolated subnets do not have any route to the internet and therefore need no route created.
+                });
+        }
+
+        return {
+            vpc,
+            subnets,
+            igw,
+            routeTables,
+            routeTableAssociations,
+            routes,
+            natGateways,
+            eips,
+        };
+    }
+
+    async getDefaultAzs(): Promise<string[]> {
+        const result = await aws.getAvailabilityZones();
+        if (result.names.length < 3) {
+            throw new Error(
+                "The configured region for this provider does not have at least 3 Availability Zones. Either specify an explicit list of zones in availabilityZoneNames or choose a region with at least 3 AZs.",
+            );
+        }
+        return result.names.slice(0, 3);
+    }
+}
+
+export function validateEips(
+    natGatewayStrategy: schema.NatGatewayStrategyInputs,
+    eips: pulumi.Input<string>[] | undefined,
+    availabilityZones: string[] = [],
+) {
+    // We are strict about matching NAT Gateway strategies with the number of supplied EIPs (if supplied) because if
+    // EIPs are supplied, we are assuming the user has a scenario where all outbound traffic from the VPC must come
+    // from known IP addresses, e.g. because they are hitting a data center-based service with a firewall that must
+    // allowlist specific IP addresses. Assuming this is the user's scenario, e.g. we have 3 AZs, and only 2 EIPs
+    // specified, 1/3 of the traffic coming from the VPC will be seemingly randomly rejected, which is a difficult
+    // and frustrating problem to debug. Therefore, we feel it's better to be strict about the acceptable inputs to
+    // avoid potentially confusing problems in production.
+    switch (natGatewayStrategy) {
+        case "None":
+            if (eips?.length ?? 0 !== 0) {
+                throw new Error(
+                    `Elastic IP allocation IDs cannot be specified when NAT Gateway strategy is '${natGatewayStrategy}'.`,
+                );
+            }
+            break;
+        case "Single":
+            if (eips && eips.length !== 1) {
+                throw new Error(
+                    `Exactly one Elastic IP may be specified when NAT Gateway strategy is '${natGatewayStrategy}'.`,
+                );
+            }
+            break;
+        case "OnePerAz":
+            if (
+                eips &&
+                eips.length > 0 &&
+                eips.length !== availabilityZones.length
+            ) {
+                throw new Error(
+                    `The number of Elastic IPs, if specified, must match the number of availability zones for the VPC (${availabilityZones.length}) when NAT Gateway strategy is '${natGatewayStrategy}'`,
+                );
+            }
+            break;
+        default:
+            throw new Error(
+                `Unknown NatGatewayStrategy '${natGatewayStrategy}'`,
+            );
+    }
+}
+
+export function validateNatGatewayStrategy(
+    natGatewayStrategy: schema.NatGatewayStrategyInputs,
+    subnets: SubnetSpec[],
+) {
+    // This logic assumes that the same subnets exist in every AZ:
+    switch (natGatewayStrategy) {
+        case "OnePerAz":
+        case "Single":
+            if (
+                subnets.some((x) => x.type === "Public") &&
+                subnets.some((x) => x.type === "Private")
+            ) {
+                return;
+            }
+            throw new Error(
+                "If NAT Gateway strategy is 'OnePerAz' or 'Single', both private and public subnets must be declared. The private subnet creates the need for a NAT Gateway, and the public subnet is required to host the NAT Gateway resource.",
+            );
+        case "None":
+            if (subnets.some((x) => x.type === "Private")) {
+                throw new Error(
+                    "If private subnets are specified, NAT Gateway strategy cannot be 'None'.",
+                );
+            }
+            break;
+        default:
+            throw new Error(
+                `Unknown NAT Gateway strategy '${natGatewayStrategy}'`,
+            );
+    }
+}
+
+export function shouldCreateNatGateway(
+    strategy: schema.NatGatewayStrategyInputs,
+    numGateways: number,
+    azIndex: number,
+) {
+    switch (strategy) {
+        case "None":
+            return false;
+        case "Single":
+            return numGateways < 1;
+        case "OnePerAz":
+            return numGateways < azIndex + 1;
+        default:
+            throw new Error(`Unknown NatGatewayStrategy "${strategy}"`);
+    }
+}
+
+export function compareSubnetSpecs(
+    spec1: SubnetSpec,
+    spec2: SubnetSpec,
+): number {
+    if (spec1.type === spec2.type) {
+        return 0;
+    }
+
+    // Public always comes first
+    if (spec1.type === "Public") {
+        return -1;
+    }
+
+    if (spec1.type === "Private" && spec2.type === "Public") {
+        return 1;
+    }
+
+    if (spec1.type === "Private" && spec2.type === "Isolated") {
+        return -1;
+    }
+
+    // Isolated is the only remaining case, and they always come last.
+    return 1;
+}
diff --git a/awsx/ecs/fargateService.ts b/awsx/ecs/fargateService.ts
index 7cad41f7..e3be16e1 100644
--- a/awsx/ecs/fargateService.ts
+++ b/awsx/ecs/fargateService.ts
@@ -14,9 +14,9 @@
 
 import * as aws from "@pulumi/aws";
 import * as pulumi from "@pulumi/pulumi";
+import { getDefaultVpc } from "../ec2";
 import * as schema from "../schema-types";
 import * as utils from "../utils";
-import { getDefaultVpc } from "../vpc";
 import { FargateTaskDefinition } from "./fargateTaskDefinition";
 
 /**
diff --git a/awsx/lb/applicationLoadBalancer.ts b/awsx/lb/applicationLoadBalancer.ts
index db159c74..2d7edec1 100644
--- a/awsx/lb/applicationLoadBalancer.ts
+++ b/awsx/lb/applicationLoadBalancer.ts
@@ -14,9 +14,9 @@
 
 import * as aws from "@pulumi/aws";
 import * as pulumi from "@pulumi/pulumi";
+import { getDefaultVpc } from "../ec2";
 import * as schema from "../schema-types";
 import * as utils from "../utils";
-import { getDefaultVpc } from "../vpc";
 
 export class ApplicationLoadBalancer extends schema.ApplicationLoadBalancer {
     constructor(
diff --git a/awsx/lb/networkLoadBalancer.ts b/awsx/lb/networkLoadBalancer.ts
index dcb44d53..4c468dc9 100644
--- a/awsx/lb/networkLoadBalancer.ts
+++ b/awsx/lb/networkLoadBalancer.ts
@@ -14,9 +14,9 @@
 
 import * as aws from "@pulumi/aws";
 import * as pulumi from "@pulumi/pulumi";
+import { getDefaultVpc } from "../ec2";
 import * as schema from "../schema-types";
 import * as utils from "../utils";
-import { getDefaultVpc } from "../vpc";
 
 export class NetworkLoadBalancer extends schema.NetworkLoadBalancer {
     constructor(
diff --git a/awsx/resources.ts b/awsx/resources.ts
index 746f11e0..b7ef9d64 100644
--- a/awsx/resources.ts
+++ b/awsx/resources.ts
@@ -14,6 +14,7 @@
 
 import * as pulumi from "@pulumi/pulumi";
 import { Trail } from "./cloudtrail";
+import { DefaultVpc, getDefaultVpc, Vpc } from "./ec2";
 import { Repository } from "./ecr";
 import { Image } from "./ecr/image";
 import {
@@ -28,8 +29,6 @@ import {
     Functions,
     ResourceConstructor,
 } from "./schema-types";
-import { DefaultVpc, Vpc } from "./vpc";
-import { getDefaultVpc } from "./vpc/getDefaultVpc";
 
 const resources: ResourceConstructor = {
     "awsx:cloudtrail:Trail": (...args) => new Trail(...args),
@@ -42,8 +41,8 @@ const resources: ResourceConstructor = {
         new ApplicationLoadBalancer(...args),
     "awsx:lb:NetworkLoadBalancer": (...args) =>
         new NetworkLoadBalancer(...args),
-    "awsx:vpc:Vpc": (...args) => new Vpc(...args),
-    "awsx:vpc:DefaultVpc": (...args) => new DefaultVpc(...args),
+    "awsx:ec2:Vpc": (...args) => new Vpc(...args),
+    "awsx:ec2:DefaultVpc": (...args) => new DefaultVpc(...args),
     "awsx:ecr:Repository": (...args) => new Repository(...args),
     "awsx:ecr:Image": (...args) => new Image(...args),
 };
@@ -63,5 +62,5 @@ export function construct(
 }
 
 export const functions: Functions = {
-    "awsx:vpc:getDefaultVpc": getDefaultVpc,
+    "awsx:ec2:getDefaultVpc": getDefaultVpc,
 };
diff --git a/awsx/schema-types.ts b/awsx/schema-types.ts
index 962eb688..7ccd6a22 100644
--- a/awsx/schema-types.ts
+++ b/awsx/schema-types.ts
@@ -7,6 +7,8 @@ import * as pulumi from "@pulumi/pulumi";
 export type ConstructComponent<T extends pulumi.ComponentResource = pulumi.ComponentResource> = (name: string, inputs: any, options: pulumi.ComponentResourceOptions) => T;
 export type ResourceConstructor = {
     readonly "awsx:cloudtrail:Trail": ConstructComponent<Trail>;
+    readonly "awsx:ec2:DefaultVpc": ConstructComponent<DefaultVpc>;
+    readonly "awsx:ec2:Vpc": ConstructComponent<Vpc>;
     readonly "awsx:ecr:Image": ConstructComponent<Image>;
     readonly "awsx:ecr:Repository": ConstructComponent<Repository>;
     readonly "awsx:ecs:EC2Service": ConstructComponent<EC2Service>;
@@ -15,11 +17,9 @@ export type ResourceConstructor = {
     readonly "awsx:ecs:FargateTaskDefinition": ConstructComponent<FargateTaskDefinition>;
     readonly "awsx:lb:ApplicationLoadBalancer": ConstructComponent<ApplicationLoadBalancer>;
     readonly "awsx:lb:NetworkLoadBalancer": ConstructComponent<NetworkLoadBalancer>;
-    readonly "awsx:vpc:DefaultVpc": ConstructComponent<DefaultVpc>;
-    readonly "awsx:vpc:Vpc": ConstructComponent<Vpc>;
 };
 export type Functions = {
-    "awsx:vpc:getDefaultVpc": (inputs: getDefaultVpcInputs) => Promise<getDefaultVpcOutputs>;
+    "awsx:ec2:getDefaultVpc": (inputs: getDefaultVpcInputs) => Promise<getDefaultVpcOutputs>;
 };
 import * as aws from "@pulumi/aws";
 import * as docker from "@pulumi/docker";
@@ -48,6 +48,48 @@ export interface TrailArgs {
     readonly snsTopicName?: pulumi.Input<string>;
     readonly tags?: pulumi.Input<Record<string, pulumi.Input<string>>>;
 }
+export abstract class DefaultVpc<TData = any> extends pulumi.ComponentResource<TData> {
+    public privateSubnetIds!: string[] | pulumi.Output<string[]>;
+    public publicSubnetIds!: string[] | pulumi.Output<string[]>;
+    public vpcId!: string | pulumi.Output<string>;
+    constructor(name: string, args: pulumi.Inputs, opts: pulumi.ComponentResourceOptions = {}) {
+        super("awsx:ec2:DefaultVpc", name, opts.urn ? { privateSubnetIds: undefined, publicSubnetIds: undefined, vpcId: undefined } : { name, args, opts }, opts);
+    }
+}
+export interface DefaultVpcArgs {
+}
+export abstract class Vpc<TData = any> extends pulumi.ComponentResource<TData> {
+    public eips?: aws.ec2.Eip[] | pulumi.Output<aws.ec2.Eip[]>;
+    public internetGateway?: aws.ec2.InternetGateway | pulumi.Output<aws.ec2.InternetGateway>;
+    public natGateways?: aws.ec2.NatGateway[] | pulumi.Output<aws.ec2.NatGateway[]>;
+    public routeTableAssociations?: aws.ec2.RouteTableAssociation[] | pulumi.Output<aws.ec2.RouteTableAssociation[]>;
+    public routeTables?: aws.ec2.RouteTable[] | pulumi.Output<aws.ec2.RouteTable[]>;
+    public routes?: aws.ec2.Route[] | pulumi.Output<aws.ec2.Route[]>;
+    public subnets?: aws.ec2.Subnet[] | pulumi.Output<aws.ec2.Subnet[]>;
+    public vpc?: aws.ec2.Vpc | pulumi.Output<aws.ec2.Vpc>;
+    constructor(name: string, args: pulumi.Inputs, opts: pulumi.ComponentResourceOptions = {}) {
+        super("awsx:ec2:Vpc", name, opts.urn ? { eips: undefined, internetGateway: undefined, natGateways: undefined, routeTableAssociations: undefined, routeTables: undefined, routes: undefined, subnets: undefined, vpc: undefined } : { name, args, opts }, opts);
+    }
+}
+export interface VpcArgs {
+    readonly assignGeneratedIpv6CidrBlock?: pulumi.Input<boolean>;
+    readonly availabilityZoneNames?: string[];
+    readonly cidrBlock?: string;
+    readonly enableClassiclink?: pulumi.Input<boolean>;
+    readonly enableClassiclinkDnsSupport?: pulumi.Input<boolean>;
+    readonly enableDnsHostnames?: pulumi.Input<boolean>;
+    readonly enableDnsSupport?: pulumi.Input<boolean>;
+    readonly instanceTenancy?: pulumi.Input<string>;
+    readonly ipv4IpamPoolId?: pulumi.Input<string>;
+    readonly ipv4NetmaskLength?: pulumi.Input<number>;
+    readonly ipv6CidrBlock?: pulumi.Input<string>;
+    readonly ipv6CidrBlockNetworkBorderGroup?: pulumi.Input<string>;
+    readonly ipv6IpamPoolId?: pulumi.Input<string>;
+    readonly ipv6NetmaskLength?: pulumi.Input<number>;
+    readonly natGateways?: NatGatewayConfigurationInputs;
+    readonly subnetsPerAz?: SubnetConfigurationInputs[];
+    readonly tags?: pulumi.Input<Record<string, pulumi.Input<string>>>;
+}
 export abstract class Image<TData = any> extends pulumi.ComponentResource<TData> {
     public imageUri!: string | pulumi.Output<string>;
     constructor(name: string, args: pulumi.Inputs, opts: pulumi.ComponentResourceOptions = {}) {
@@ -270,42 +312,6 @@ export interface NetworkLoadBalancerArgs {
     readonly subnets?: pulumi.Input<pulumi.Input<aws.ec2.Subnet>[]>;
     readonly tags?: pulumi.Input<Record<string, pulumi.Input<string>>>;
 }
-export abstract class DefaultVpc<TData = any> extends pulumi.ComponentResource<TData> {
-    public privateSubnetIds!: string[] | pulumi.Output<string[]>;
-    public publicSubnetIds!: string[] | pulumi.Output<string[]>;
-    public vpcId!: string | pulumi.Output<string>;
-    constructor(name: string, args: pulumi.Inputs, opts: pulumi.ComponentResourceOptions = {}) {
-        super("awsx:vpc:DefaultVpc", name, opts.urn ? { privateSubnetIds: undefined, publicSubnetIds: undefined, vpcId: undefined } : { name, args, opts }, opts);
-    }
-}
-export interface DefaultVpcArgs {
-}
-export abstract class Vpc<TData = any> extends pulumi.ComponentResource<TData> {
-    public subnets?: aws.ec2.Subnet[] | pulumi.Output<aws.ec2.Subnet[]>;
-    public vpc?: aws.ec2.Vpc | pulumi.Output<aws.ec2.Vpc>;
-    constructor(name: string, args: pulumi.Inputs, opts: pulumi.ComponentResourceOptions = {}) {
-        super("awsx:vpc:Vpc", name, opts.urn ? { subnets: undefined, vpc: undefined } : { name, args, opts }, opts);
-    }
-}
-export interface VpcArgs {
-    readonly assignGeneratedIpv6CidrBlock?: pulumi.Input<boolean>;
-    readonly availabilityZoneNames?: string[];
-    readonly cidrBlock?: string;
-    readonly enableClassiclink?: pulumi.Input<boolean>;
-    readonly enableClassiclinkDnsSupport?: pulumi.Input<boolean>;
-    readonly enableDnsHostnames?: pulumi.Input<boolean>;
-    readonly enableDnsSupport?: pulumi.Input<boolean>;
-    readonly instanceTenancy?: pulumi.Input<string>;
-    readonly ipv4IpamPoolId?: pulumi.Input<string>;
-    readonly ipv4NetmaskLength?: pulumi.Input<number>;
-    readonly ipv6CidrBlock?: pulumi.Input<string>;
-    readonly ipv6CidrBlockNetworkBorderGroup?: pulumi.Input<string>;
-    readonly ipv6IpamPoolId?: pulumi.Input<string>;
-    readonly ipv6NetmaskLength?: pulumi.Input<number>;
-    readonly natGateways?: NatGatewayConfigurationInputs;
-    readonly subnetsPerAz?: SubnetConfigurationInputs[];
-    readonly tags?: pulumi.Input<Record<string, pulumi.Input<string>>>;
-}
 export interface BucketInputs {
     readonly accelerationStatus?: pulumi.Input<string>;
     readonly acl?: pulumi.Input<string>;
@@ -508,6 +514,28 @@ export interface LogGroupOutputs {
     readonly retentionInDays?: pulumi.Output<number>;
     readonly tags?: pulumi.Output<Record<string, string>>;
 }
+export interface NatGatewayConfigurationInputs {
+    readonly elasticIpAllocationIds?: pulumi.Input<string>[];
+    readonly strategy: NatGatewayStrategyInputs;
+}
+export interface NatGatewayConfigurationOutputs {
+    readonly elasticIpAllocationIds?: string[];
+    readonly strategy: NatGatewayStrategyOutputs;
+}
+export type NatGatewayStrategyInputs = "None" | "Single" | "OnePerAz";
+export type NatGatewayStrategyOutputs = "None" | "Single" | "OnePerAz";
+export interface SubnetConfigurationInputs {
+    readonly cidrMask: number;
+    readonly name: string;
+    readonly type: SubnetTypeInputs;
+}
+export interface SubnetConfigurationOutputs {
+    readonly cidrMask: number;
+    readonly name: string;
+    readonly type: SubnetTypeOutputs;
+}
+export type SubnetTypeInputs = "Public" | "Private" | "Isolated";
+export type SubnetTypeOutputs = "Public" | "Private" | "Isolated";
 export interface DockerBuildInputs {
     readonly args?: pulumi.Input<Record<string, pulumi.Input<string>>>;
     readonly cacheFrom?: pulumi.Input<pulumi.Input<string>[]>;
@@ -950,28 +978,6 @@ export interface TargetGroupOutputs {
     readonly targetType?: pulumi.Output<string>;
     readonly vpcId?: pulumi.Output<string>;
 }
-export interface NatGatewayConfigurationInputs {
-    readonly elasticIpAllocationIds?: pulumi.Input<string>[];
-    readonly strategy: NatGatewayStrategyInputs;
-}
-export interface NatGatewayConfigurationOutputs {
-    readonly elasticIpAllocationIds?: string[];
-    readonly strategy: NatGatewayStrategyOutputs;
-}
-export type NatGatewayStrategyInputs = "None" | "Single" | "OnePerAz";
-export type NatGatewayStrategyOutputs = "None" | "Single" | "OnePerAz";
-export interface SubnetConfigurationInputs {
-    readonly cidrMask: number;
-    readonly name: string;
-    readonly type: SubnetTypeInputs;
-}
-export interface SubnetConfigurationOutputs {
-    readonly cidrMask: number;
-    readonly name: string;
-    readonly type: SubnetTypeOutputs;
-}
-export type SubnetTypeInputs = "Public" | "Private" | "Isolated";
-export type SubnetTypeOutputs = "Public" | "Private" | "Isolated";
 export interface getDefaultVpcInputs {
 }
 export interface getDefaultVpcOutputs {
diff --git a/awsx/schema.json b/awsx/schema.json
index e2ad5c3f..ec865cd4 100644
--- a/awsx/schema.json
+++ b/awsx/schema.json
@@ -454,6 +454,90 @@
             },
             "type": "object"
         },
+        "awsx:ec2:NatGatewayConfiguration": {
+            "description": "Configuration for NAT Gateways.",
+            "properties": {
+                "elasticIpAllocationIds": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    },
+                    "plain": true,
+                    "description": "A list of EIP allocation IDs to assign to the NAT Gateways. Optional. If specified, the number of supplied values must match the chosen strategy (either one, or the number of availability zones)."
+                },
+                "strategy": {
+                    "$ref": "#/types/awsx:ec2:NatGatewayStrategy",
+                    "plain": true,
+                    "description": "The strategy for deploying NAT Gateways."
+                }
+            },
+            "type": "object",
+            "required": [
+                "strategy"
+            ]
+        },
+        "awsx:ec2:NatGatewayStrategy": {
+            "description": "A strategy for creating NAT Gateways for private subnets within a VPC.",
+            "type": "string",
+            "enum": [
+                {
+                    "description": "Do not create any NAT Gateways. Resources in private subnets will not be able to access the internet.",
+                    "value": "None"
+                },
+                {
+                    "description": "Create a single NAT Gateway for the entire VPC. This configuration is not recommended for production infrastructure as it creates a single point of failure.",
+                    "value": "Single"
+                },
+                {
+                    "description": "Create a NAT Gateway in each availability zone. This is the recommended configuration for production infrastructure.",
+                    "value": "OnePerAz"
+                }
+            ]
+        },
+        "awsx:ec2:SubnetConfiguration": {
+            "description": "Configuration for a VPC subnet.",
+            "properties": {
+                "cidrMask": {
+                    "type": "integer",
+                    "plain": true,
+                    "description": "The bitmask for the subnet's CIDR block."
+                },
+                "name": {
+                    "type": "string",
+                    "plain": true,
+                    "description": "The subnet's name. Will be templated upon creation."
+                },
+                "type": {
+                    "$ref": "#/types/awsx:ec2:SubnetType",
+                    "plain": true,
+                    "description": "The type of subnet."
+                }
+            },
+            "type": "object",
+            "required": [
+                "type",
+                "name",
+                "cidrMask"
+            ]
+        },
+        "awsx:ec2:SubnetType": {
+            "description": "A type of subnet within a VPC.",
+            "type": "string",
+            "enum": [
+                {
+                    "description": "A subnet whose hosts can directly communicate with the internet.",
+                    "value": "Public"
+                },
+                {
+                    "description": "A subnet whose hosts can not directly communicate with the internet, but can initiate outbound network traffic via a NAT Gateway.",
+                    "value": "Private"
+                },
+                {
+                    "description": "A subnet whose hosts have no connectivity with the internet.",
+                    "value": "Isolated"
+                }
+            ]
+        },
         "awsx:ecr:DockerBuild": {
             "description": "Arguments for building a docker image",
             "properties": {
@@ -1366,90 +1450,6 @@
                 }
             },
             "type": "object"
-        },
-        "awsx:vpc:NatGatewayConfiguration": {
-            "description": "Configuration for NAT Gateways.",
-            "properties": {
-                "elasticIpAllocationIds": {
-                    "type": "array",
-                    "items": {
-                        "type": "string"
-                    },
-                    "plain": true,
-                    "description": "A list of EIP allocation IDs to assign to the NAT Gateways. Optional. If specified, the number of supplied values must match the chosen strategy (either one, or the number of availability zones)."
-                },
-                "strategy": {
-                    "$ref": "#/types/awsx:vpc:NatGatewayStrategy",
-                    "plain": true,
-                    "description": "The strategy for deploying NAT Gateways."
-                }
-            },
-            "type": "object",
-            "required": [
-                "strategy"
-            ]
-        },
-        "awsx:vpc:NatGatewayStrategy": {
-            "description": "A strategy for creating NAT Gateways for private subnets within a VPC.",
-            "type": "string",
-            "enum": [
-                {
-                    "description": "Do not create any NAT Gateways. Resources in private subnets will not be able to access the internet.",
-                    "value": "None"
-                },
-                {
-                    "description": "Create a single NAT Gateway for the entire VPC. This configuration is not recommended for production infrastructure as it creates a single point of failure.",
-                    "value": "Single"
-                },
-                {
-                    "description": "Create a NAT Gateway in each availability zone. This is the recommended configuration for production infrastructure.",
-                    "value": "OnePerAz"
-                }
-            ]
-        },
-        "awsx:vpc:SubnetConfiguration": {
-            "description": "Configuration for a VPC subnet.",
-            "properties": {
-                "cidrMask": {
-                    "type": "integer",
-                    "plain": true,
-                    "description": "The bitmask for the subnet's CIDR block."
-                },
-                "name": {
-                    "type": "string",
-                    "plain": true,
-                    "description": "The subnet's name. Will be templated upon creation."
-                },
-                "type": {
-                    "$ref": "#/types/awsx:vpc:SubnetType",
-                    "plain": true,
-                    "description": "The type of subnet."
-                }
-            },
-            "type": "object",
-            "required": [
-                "type",
-                "name",
-                "cidrMask"
-            ]
-        },
-        "awsx:vpc:SubnetType": {
-            "description": "A type of subnet within a VPC.",
-            "type": "string",
-            "enum": [
-                {
-                    "description": "A subnet whose hosts can directly communicate with the internet.",
-                    "value": "Public"
-                },
-                {
-                    "description": "A subnet whose hosts can not directly communicate with the internet, but can initiate outbound network traffic via a NAT Gateway.",
-                    "value": "Private"
-                },
-                {
-                    "description": "A subnet whose hosts have no connectivity with the internet.",
-                    "value": "Isolated"
-                }
-            ]
         }
     },
     "provider": {},
@@ -1555,6 +1555,178 @@
             },
             "isComponent": true
         },
+        "awsx:ec2:DefaultVpc": {
+            "description": "Pseudo resource representing the default VPC and associated subnets for an account and region. This does not create any resources. This will be replaced with `getDefaultVpc` in the future.",
+            "properties": {
+                "privateSubnetIds": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "publicSubnetIds": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "vpcId": {
+                    "type": "string",
+                    "description": "The VPC ID for the default VPC"
+                }
+            },
+            "required": [
+                "vpcId",
+                "publicSubnetIds",
+                "privateSubnetIds"
+            ],
+            "isComponent": true
+        },
+        "awsx:ec2:Vpc": {
+            "properties": {
+                "eips": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2feip:Eip"
+                    },
+                    "description": "The EIPs for any NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list."
+                },
+                "internetGateway": {
+                    "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2finternetGateway:InternetGateway",
+                    "description": "The Internet Gateway for the VPC."
+                },
+                "natGateways": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2fnatGateway:NatGateway"
+                    },
+                    "description": "The NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list."
+                },
+                "routeTableAssociations": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2frouteTableAssociation:RouteTableAssociation"
+                    },
+                    "description": "The Route Table Associations for the VPC."
+                },
+                "routeTables": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2frouteTable:RouteTable"
+                    },
+                    "description": "The Route Tables for the VPC."
+                },
+                "routes": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2froute:Route"
+                    },
+                    "description": "The Routes for the VPC."
+                },
+                "subnets": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2fsubnet:Subnet"
+                    },
+                    "description": "The VPC's subnets."
+                },
+                "vpc": {
+                    "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2fvpc:Vpc",
+                    "description": "The VPC.",
+                    "language": {
+                        "csharp": {
+                            "name": "AwsVpc"
+                        }
+                    }
+                }
+            },
+            "inputProperties": {
+                "assignGeneratedIpv6CidrBlock": {
+                    "type": "boolean",
+                    "description": "Requests an Amazon-provided IPv6 CIDR block with a /56 prefix length for the VPC. You cannot specify the range of IP addresses, or the size of the CIDR block. Default is `false`. Conflicts with `ipv6_ipam_pool_id`\n"
+                },
+                "availabilityZoneNames": {
+                    "type": "array",
+                    "items": {
+                        "type": "string",
+                        "plain": true
+                    },
+                    "plain": true,
+                    "description": "A list of availability zones to which the subnets defined in subnetsPerAz will be deployed. Optional, defaults to the first 3 AZs in the current region."
+                },
+                "cidrBlock": {
+                    "type": "string",
+                    "plain": true,
+                    "description": "The CIDR block for the VPC. Optional. Defaults to 10.0.0.0/16."
+                },
+                "enableClassiclink": {
+                    "type": "boolean",
+                    "description": "A boolean flag to enable/disable ClassicLink\nfor the VPC. Only valid in regions and accounts that support EC2 Classic.\nSee the [ClassicLink documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-classiclink.html) for more information. Defaults false.\n"
+                },
+                "enableClassiclinkDnsSupport": {
+                    "type": "boolean",
+                    "description": "A boolean flag to enable/disable ClassicLink DNS Support for the VPC.\nOnly valid in regions and accounts that support EC2 Classic.\n"
+                },
+                "enableDnsHostnames": {
+                    "type": "boolean",
+                    "description": "A boolean flag to enable/disable DNS hostnames in the VPC. Defaults false.\n"
+                },
+                "enableDnsSupport": {
+                    "type": "boolean",
+                    "description": "A boolean flag to enable/disable DNS support in the VPC. Defaults true.\n"
+                },
+                "instanceTenancy": {
+                    "type": "string",
+                    "description": "A tenancy option for instances launched into the VPC. Default is `default`, which ensures that EC2 instances launched in this VPC use the EC2 instance tenancy attribute specified when the EC2 instance is launched. The only other option is `dedicated`, which ensures that EC2 instances launched in this VPC are run on dedicated tenancy instances regardless of the tenancy attribute specified at launch. This has a dedicated per region fee of $2 per hour, plus an hourly per instance usage fee.\n"
+                },
+                "ipv4IpamPoolId": {
+                    "type": "string",
+                    "description": "The ID of an IPv4 IPAM pool you want to use for allocating this VPC's CIDR. IPAM is a VPC feature that you can use to automate your IP address management workflows including assigning, tracking, troubleshooting, and auditing IP addresses across AWS Regions and accounts. Using IPAM you can monitor IP address usage throughout your AWS Organization.\n"
+                },
+                "ipv4NetmaskLength": {
+                    "type": "integer",
+                    "description": "The netmask length of the IPv4 CIDR you want to allocate to this VPC. Requires specifying a `ipv4_ipam_pool_id`.\n"
+                },
+                "ipv6CidrBlock": {
+                    "type": "string",
+                    "description": "IPv6 CIDR block to request from an IPAM Pool. Can be set explicitly or derived from IPAM using `ipv6_netmask_length`.\n"
+                },
+                "ipv6CidrBlockNetworkBorderGroup": {
+                    "type": "string",
+                    "description": "By default when an IPv6 CIDR is assigned to a VPC a default ipv6_cidr_block_network_border_group will be set to the region of the VPC. This can be changed to restrict advertisement of public addresses to specific Network Border Groups such as LocalZones.\n"
+                },
+                "ipv6IpamPoolId": {
+                    "type": "string",
+                    "description": "IPAM Pool ID for a IPv6 pool. Conflicts with `assign_generated_ipv6_cidr_block`.\n"
+                },
+                "ipv6NetmaskLength": {
+                    "type": "integer",
+                    "description": "Netmask length to request from IPAM Pool. Conflicts with `ipv6_cidr_block`. This can be omitted if IPAM pool as a `allocation_default_netmask_length` set. Valid values: `56`.\n"
+                },
+                "natGateways": {
+                    "$ref": "#/types/awsx:ec2:NatGatewayConfiguration",
+                    "plain": true,
+                    "description": "Configuration for NAT Gateways. Optional. If private and public subnets are both specified, defaults to one gateway per availability zone. Otherwise, no gateways will be created."
+                },
+                "subnetsPerAz": {
+                    "type": "array",
+                    "items": {
+                        "$ref": "#/types/awsx:ec2:SubnetConfiguration",
+                        "plain": true
+                    },
+                    "plain": true,
+                    "description": "A list of subnets that should be deployed to each AZ specified in availabilityZoneNames. Optional. Defaults to a (smaller) public subnet and a (larger) private subnet based on the size of the CIDR block for the VPC."
+                },
+                "tags": {
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "string"
+                    },
+                    "description": "A map of tags to assign to the resource. If configured with a provider `default_tags` configuration block present, tags with matching keys will overwrite those defined at the provider-level.\n"
+                }
+            },
+            "isComponent": true
+        },
         "awsx:ecr:Image": {
             "description": "Builds a docker image and pushes to the ECR repository",
             "properties": {
@@ -2460,143 +2632,10 @@
                 }
             },
             "isComponent": true
-        },
-        "awsx:vpc:DefaultVpc": {
-            "description": "Pseudo resource representing the default VPC and associated subnets for an account and region. This does not create any resources. This will be replaced with `getDefaultVpc` in the future.",
-            "properties": {
-                "privateSubnetIds": {
-                    "type": "array",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "publicSubnetIds": {
-                    "type": "array",
-                    "items": {
-                        "type": "string"
-                    }
-                },
-                "vpcId": {
-                    "type": "string",
-                    "description": "The VPC ID for the default VPC"
-                }
-            },
-            "required": [
-                "vpcId",
-                "publicSubnetIds",
-                "privateSubnetIds"
-            ],
-            "isComponent": true
-        },
-        "awsx:vpc:Vpc": {
-            "properties": {
-                "subnets": {
-                    "type": "array",
-                    "items": {
-                        "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2fsubnet:Subnet"
-                    },
-                    "description": "The VPC's subnets."
-                },
-                "vpc": {
-                    "$ref": "/aws/v5.3.0/schema.json#/resources/aws:ec2%2fvpc:Vpc",
-                    "description": "The VPC.",
-                    "language": {
-                        "csharp": {
-                            "name": "AwsVpc"
-                        }
-                    }
-                }
-            },
-            "inputProperties": {
-                "assignGeneratedIpv6CidrBlock": {
-                    "type": "boolean",
-                    "description": "Requests an Amazon-provided IPv6 CIDR block with a /56 prefix length for the VPC. You cannot specify the range of IP addresses, or the size of the CIDR block. Default is `false`. Conflicts with `ipv6_ipam_pool_id`\n"
-                },
-                "availabilityZoneNames": {
-                    "type": "array",
-                    "items": {
-                        "type": "string",
-                        "plain": true
-                    },
-                    "plain": true,
-                    "description": "A list of availability zones to which the subnets defined in subnetsPerAz will be deployed. Optional, defaults to the first 3 AZs in the current region."
-                },
-                "cidrBlock": {
-                    "type": "string",
-                    "plain": true,
-                    "description": "The CIDR block for the VPC. Optional. Defaults to 10.0.0.0/16."
-                },
-                "enableClassiclink": {
-                    "type": "boolean",
-                    "description": "A boolean flag to enable/disable ClassicLink\nfor the VPC. Only valid in regions and accounts that support EC2 Classic.\nSee the [ClassicLink documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/vpc-classiclink.html) for more information. Defaults false.\n"
-                },
-                "enableClassiclinkDnsSupport": {
-                    "type": "boolean",
-                    "description": "A boolean flag to enable/disable ClassicLink DNS Support for the VPC.\nOnly valid in regions and accounts that support EC2 Classic.\n"
-                },
-                "enableDnsHostnames": {
-                    "type": "boolean",
-                    "description": "A boolean flag to enable/disable DNS hostnames in the VPC. Defaults false.\n"
-                },
-                "enableDnsSupport": {
-                    "type": "boolean",
-                    "description": "A boolean flag to enable/disable DNS support in the VPC. Defaults true.\n"
-                },
-                "instanceTenancy": {
-                    "type": "string",
-                    "description": "A tenancy option for instances launched into the VPC. Default is `default`, which ensures that EC2 instances launched in this VPC use the EC2 instance tenancy attribute specified when the EC2 instance is launched. The only other option is `dedicated`, which ensures that EC2 instances launched in this VPC are run on dedicated tenancy instances regardless of the tenancy attribute specified at launch. This has a dedicated per region fee of $2 per hour, plus an hourly per instance usage fee.\n"
-                },
-                "ipv4IpamPoolId": {
-                    "type": "string",
-                    "description": "The ID of an IPv4 IPAM pool you want to use for allocating this VPC's CIDR. IPAM is a VPC feature that you can use to automate your IP address management workflows including assigning, tracking, troubleshooting, and auditing IP addresses across AWS Regions and accounts. Using IPAM you can monitor IP address usage throughout your AWS Organization.\n"
-                },
-                "ipv4NetmaskLength": {
-                    "type": "integer",
-                    "description": "The netmask length of the IPv4 CIDR you want to allocate to this VPC. Requires specifying a `ipv4_ipam_pool_id`.\n"
-                },
-                "ipv6CidrBlock": {
-                    "type": "string",
-                    "description": "IPv6 CIDR block to request from an IPAM Pool. Can be set explicitly or derived from IPAM using `ipv6_netmask_length`.\n"
-                },
-                "ipv6CidrBlockNetworkBorderGroup": {
-                    "type": "string",
-                    "description": "By default when an IPv6 CIDR is assigned to a VPC a default ipv6_cidr_block_network_border_group will be set to the region of the VPC. This can be changed to restrict advertisement of public addresses to specific Network Border Groups such as LocalZones.\n"
-                },
-                "ipv6IpamPoolId": {
-                    "type": "string",
-                    "description": "IPAM Pool ID for a IPv6 pool. Conflicts with `assign_generated_ipv6_cidr_block`.\n"
-                },
-                "ipv6NetmaskLength": {
-                    "type": "integer",
-                    "description": "Netmask length to request from IPAM Pool. Conflicts with `ipv6_cidr_block`. This can be omitted if IPAM pool as a `allocation_default_netmask_length` set. Valid values: `56`.\n"
-                },
-                "natGateways": {
-                    "$ref": "#/types/awsx:vpc:NatGatewayConfiguration",
-                    "plain": true,
-                    "description": "Configuration for NAT Gateways. Optional. If private and public subnets are both specified, defaults to one gateway per availability zone. Otherwise, no gateways will be created."
-                },
-                "subnetsPerAz": {
-                    "type": "array",
-                    "items": {
-                        "$ref": "#/types/awsx:vpc:SubnetConfiguration",
-                        "plain": true
-                    },
-                    "plain": true,
-                    "description": "A list of subnets that should be deployed to each AZ specified in availabilityZoneNames. Optional. Defaults to a (smaller) public subnet and a (larger) private subnet based on the size of the CIDR block for the VPC."
-                },
-                "tags": {
-                    "type": "object",
-                    "additionalProperties": {
-                        "type": "string"
-                    },
-                    "description": "A map of tags to assign to the resource. If configured with a provider `default_tags` configuration block present, tags with matching keys will overwrite those defined at the provider-level.\n"
-                }
-            },
-            "isComponent": true
         }
     },
     "functions": {
-        "awsx:vpc:getDefaultVpc": {
+        "awsx:ec2:getDefaultVpc": {
             "description": "[NOT YET IMPLEMENTED] Get the Default VPC for a region.",
             "inputs": {
                 "description": "Arguments for getting the default VPC"
diff --git a/awsx/tslint.json b/awsx/tslint.json
index f2023c7b..558c409c 100644
--- a/awsx/tslint.json
+++ b/awsx/tslint.json
@@ -26,10 +26,7 @@
         "jsdoc-format": false,
         "label-position": true,
         "member-access": false,
-        "member-ordering": [
-            true,
-            "variables-before-functions"
-        ],
+        "member-ordering": false,
         "no-any": false,
         "no-arg": true,
         "no-bitwise": false,
@@ -65,7 +62,7 @@
             "check-open-brace",
             "check-whitespace"
         ],
-        "ordered-imports": true,
+        "ordered-imports": false,
         "prefer-const": true,
         "quotemark": [
             true,
@@ -119,4 +116,4 @@
             "check-type"
         ]
     }
-}
+}
\ No newline at end of file
diff --git a/awsx/vpc/vpc.ts b/awsx/vpc/vpc.ts
deleted file mode 100644
index fd1bcebb..00000000
--- a/awsx/vpc/vpc.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2016-2022, Pulumi Corporation.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// 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.
-
-import * as aws from "@pulumi/aws";
-import * as pulumi from "@pulumi/pulumi";
-import * as schema from "../schema-types";
-import { getSubnetSpecs } from "./subnetDistributor";
-
-interface VpcData {
-    vpc: aws.ec2.Vpc;
-    subnets: aws.ec2.Subnet[];
-}
-
-export class Vpc extends schema.Vpc<VpcData> {
-    constructor(name: string, args: schema.VpcArgs, opts: pulumi.ComponentResourceOptions = {}) {
-        super(name, args, opts);
-
-        const data = pulumi.output(this.getData());
-        this.vpc = data.vpc;
-    }
-
-    protected async initialize(props: { name: string, args: schema.VpcArgs, opts: pulumi.ComponentResourceOptions }) {
-        const {name, args} = props;
-        const availabilityZones = args.availabilityZoneNames ?? await this.getDefaultAzs();
-
-        const natGatewayStrategy: schema.NatGatewayStrategyInputs = args.natGateways?.strategy ?? "OnePerAz";
-        const eips = args.natGateways?.elasticIpAllocationIds;
-
-        const cidrBlock = args.cidrBlock ?? "10.0.0.0/16";
-        const subnetSpecs = args.subnetsPerAz;
-
-        validateNatGatewayStrategy(natGatewayStrategy, eips, availabilityZones);
-
-        const vpc = new aws.ec2.Vpc(
-            name,
-            {
-                ...args,
-                cidrBlock,
-                tags: {
-                    ...args.tags,
-                    Name: name,
-                },
-            },
-            {parent: this},
-        );
-        const subnets = this.getSubnets(vpc, name, cidrBlock, availabilityZones, subnetSpecs);
-
-        // TODO: Create IGW only if any subnet is non-isolated
-
-        return {
-            vpc: vpc,
-            subnets: subnets,
-        };
-    }
-
-    async getDefaultAzs(): Promise<string[]> {
-        const result = await aws.getAvailabilityZones();
-        if (result.names.length < 3) {
-            throw new Error("The configured region for this provider does not have at least 3 Availability Zones. Either specify an explicit list of zones in availabilityZoneNames or choose a region with at least 3 AZs.");
-        }
-        return result.names.slice(0, 2);
-    }
-
-    getSubnets(vpc: aws.ec2.Vpc, vpcName: string, vpcCidr: string, azs: string[], inputs?: schema.SubnetConfigurationInputs[]): aws.ec2.Subnet[] {
-        const specs = getSubnetSpecs(vpcName, vpcCidr, azs, inputs);
-        const subnets: aws.ec2.Subnet[] = [];
-
-        for (let i = 0; i < azs.length; i++) {
-            specs
-                .filter(x => x.azName === azs[i])
-                .forEach(spec => {
-                    const subnet = new aws.ec2.Subnet(spec.subnetName, {
-                        vpcId: vpc.id,
-                        availabilityZone: spec.azName,
-                        mapPublicIpOnLaunch: (spec.type === "Public"),
-                        cidrBlock: spec.cidrBlock,
-                        tags: {
-                            "Name": spec.subnetName,
-                        },
-                    }, { parent: this});
-                    subnets.push(subnet);
-                });
-        }
-
-        return subnets;
-    }
-}
-
-export function validateNatGatewayStrategy(
-    natGatewayStrategy: schema.NatGatewayStrategyInputs,
-    eips: pulumi.Input<string>[] | undefined,
-    availabilityZones: string[] = [],
-) {
-    // We are strict about matching NAT Gateway strategies with the number of supplied EIPs (if supplied) because if
-    // EIPs are supplied, we are assuming the user has a scenario where all outbound traffic from the VPC must come
-    // from known IP addresses, e.g. because they are hitting a data center-based service with a firewall that must
-    // allowlist specific IP addresses. Assuming this is the user's scenario, e.g. we have 3 AZs, and only 2 EIPs
-    // specified, 1/3 of the traffic coming from the VPC will be seemingly randomly rejected, which is a difficult
-    // and frustrating problem to debug. Therefore, we feel it's better to be strict about the acceptable inputs to
-    // avoid potentially confusing problems in production.
-    switch (natGatewayStrategy) {
-        case "None":
-            if (eips?.length ?? 0 !== 0) {
-                throw new Error(`Elastic IP allocation IDs cannot be specified when NAT Gateway strategy is '${natGatewayStrategy}'.`);
-            }
-            break;
-        case "Single":
-            if (eips && eips.length !== 1) {
-                throw new Error(`Exactly one Elastic IP may be specified when NAT Gateway strategy is '${natGatewayStrategy}'.`);
-            }
-            break;
-        case "OnePerAz":
-            if (eips && eips.length !== availabilityZones.length) {
-                throw new Error(`The number of Elastic IPs, if specified, must match the number of availability zones for the VPC (${availabilityZones.length}) when NAT Gateway strategy is '${natGatewayStrategy}'`);
-            }
-            break;
-        default:
-            throw new Error(`Unknown NatGatewayStrategy '${natGatewayStrategy}'`);
-    }
-}
diff --git a/examples/vpc/nodejs/default-args/index.ts b/examples/vpc/nodejs/default-args/index.ts
index c68d3067..140c081c 100644
--- a/examples/vpc/nodejs/default-args/index.ts
+++ b/examples/vpc/nodejs/default-args/index.ts
@@ -1,3 +1,3 @@
 import * as awsx from "@pulumi/awsx";
 
-new awsx.vpc.Vpc('my-vpc', {});
+new awsx.ec2.Vpc('awsx-nodejs-default-args', {});
diff --git a/schemagen/pkg/gen/ec2.go b/schemagen/pkg/gen/ec2.go
index ec7e0b53..18b97131 100644
--- a/schemagen/pkg/gen/ec2.go
+++ b/schemagen/pkg/gen/ec2.go
@@ -15,18 +15,36 @@
 package gen
 
 import (
+	"fmt"
 	"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
+	"strings"
 )
 
 func generateEc2(awsSpec schema.PackageSpec) schema.PackageSpec {
 	return schema.PackageSpec{
+		Resources: map[string]schema.ResourceSpec{
+			"awsx:ec2:Vpc":        vpcResource(awsSpec),
+			"awsx:ec2:DefaultVpc": defaultVpcResource(awsSpec),
+		},
 		Types: map[string]schema.ComplexTypeSpec{
-			"awsx:awsx:DefaultSecurityGroup": defaultSecurityGroupArgs(awsSpec),
-			"awsx:awsx:SecurityGroup":        securityGroupArgs(awsSpec),
+			"awsx:awsx:DefaultSecurityGroup":   defaultSecurityGroupArgs(awsSpec),
+			"awsx:awsx:SecurityGroup":          securityGroupArgs(awsSpec),
+			"awsx:ec2:NatGatewayStrategy":      natGatewayStrategyType(),
+			"awsx:ec2:NatGatewayConfiguration": natGatewayConfigurationType(),
+			"awsx:ec2:SubnetType":              subnetType(),
+			"awsx:ec2:SubnetConfiguration":     subnetConfigType(),
+		},
+		Functions: map[string]schema.FunctionSpec{
+			"awsx:ec2:getDefaultVpc": defaultVpcArgs(),
 		},
 	}
 }
 
+const (
+	subnetsPerAz          = "subnetsPerAz"
+	availabilityZoneNames = "availabilityZoneNames"
+)
+
 func defaultSecurityGroupArgs(awsSpec schema.PackageSpec) schema.ComplexTypeSpec {
 	return schema.ComplexTypeSpec{
 		ObjectTypeSpec: schema.ObjectTypeSpec{
@@ -69,3 +87,340 @@ func securityGroupArgs(awsSpec schema.PackageSpec) schema.ComplexTypeSpec {
 		},
 	}
 }
+
+func vpcResource(awsSpec schema.PackageSpec) schema.ResourceSpec {
+	awsVpcResource := awsSpec.Resources["aws:ec2/vpc:Vpc"]
+	inputProperties := map[string]schema.PropertySpec{
+		availabilityZoneNames: {
+			Description: fmt.Sprintf("A list of availability zones to which the subnets defined in %s will be deployed. Optional, defaults to the first 3 AZs in the current region.", subnetsPerAz),
+			TypeSpec:    plainArrayOfPlainStrings(),
+		},
+		"cidrBlock": {
+			Description: "The CIDR block for the VPC. Optional. Defaults to 10.0.0.0/16.",
+			TypeSpec:    plainString(),
+		},
+		"natGateways": {
+			Description: "Configuration for NAT Gateways. Optional. If private and public subnets are both specified, defaults to one gateway per availability zone. Otherwise, no gateways will be created.",
+			TypeSpec: schema.TypeSpec{
+				Ref:   localRef("NatGatewayConfiguration"),
+				Plain: true,
+			},
+		},
+		subnetsPerAz: {
+			Description: fmt.Sprintf("A list of subnets that should be deployed to each AZ specified in %s. Optional. Defaults to a (smaller) public subnet and a (larger) private subnet based on the size of the CIDR block for the VPC.", availabilityZoneNames),
+			TypeSpec:    plainArrayOfPlainComplexType("SubnetConfiguration"),
+		},
+	}
+	for k, v := range awsVpcResource.InputProperties {
+		// We redefine some of the aws.Vpc properties above as plain types because they have default values in the
+		// provider implementation. In this case, we should skip the property on the raw VPC resource:
+		_, containsKey := inputProperties[k]
+		if containsKey {
+			continue
+		}
+
+		inputProperties[k] = renamePropertyRefs(v, "#/types/aws:", packageRef(awsSpec, "/types/aws:"))
+	}
+
+	return schema.ResourceSpec{
+		IsComponent: true,
+		ObjectTypeSpec: schema.ObjectTypeSpec{
+			Properties: map[string]schema.PropertySpec{
+				"vpc": {
+					Description: "The VPC.",
+					TypeSpec:    awsType(awsSpec, "ec2", "vpc"),
+					Language: map[string]schema.RawMessage{
+						"csharp": schema.RawMessage(`{
+									"name": "AwsVpc"
+								}`),
+					},
+				},
+				"subnets": {
+					Description: "The VPC's subnets.",
+					TypeSpec:    arrayOfAwsType(awsSpec, "ec2", "subnet"),
+				},
+				"routeTables": {
+					Description: "The Route Tables for the VPC.",
+					TypeSpec:    arrayOfAwsType(awsSpec, "ec2", "routeTable"),
+				},
+				"routeTableAssociations": {
+					Description: "The Route Table Associations for the VPC.",
+					TypeSpec:    arrayOfAwsType(awsSpec, "ec2", "routeTableAssociation"),
+				},
+				"routes": {
+					Description: "The Routes for the VPC.",
+					TypeSpec:    arrayOfAwsType(awsSpec, "ec2", "route"),
+				},
+				"internetGateway": {
+					Description: "The Internet Gateway for the VPC.",
+					TypeSpec:    awsType(awsSpec, "ec2", "internetGateway"),
+				},
+				"natGateways": {
+					Description: "The NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.",
+					TypeSpec:    arrayOfAwsType(awsSpec, "ec2", "natGateway"),
+				},
+				"eips": {
+					Description: "The EIPs for any NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.",
+					TypeSpec:    arrayOfAwsType(awsSpec, "ec2", "eip"),
+				},
+			},
+		},
+		InputProperties: inputProperties,
+	}
+}
+
+func arrayOfAwsType(packageSpec schema.PackageSpec, awsNamespace, resourceNameCamelCase string) schema.TypeSpec {
+	awsRefInput := fmt.Sprintf(
+		"#/resources/aws:%s%s%s:%s",
+		strings.ToLower(awsNamespace),
+		"%2f",
+		resourceNameCamelCase,
+		strings.Title(resourceNameCamelCase),
+	)
+
+	return schema.TypeSpec{
+		Type: "array",
+		Items: &schema.TypeSpec{
+			Ref: packageRef(packageSpec, awsRefInput),
+		},
+	}
+}
+
+func awsType(packageSpec schema.PackageSpec, awsNamespace, resourceNameCamelCase string) schema.TypeSpec {
+	awsRefInput := fmt.Sprintf(
+		"#/resources/aws:%s%s%s:%s",
+		strings.ToLower(awsNamespace),
+		"%2f",
+		resourceNameCamelCase,
+		strings.Title(resourceNameCamelCase),
+	)
+	return schema.TypeSpec{
+		Ref: packageRef(packageSpec, awsRefInput),
+	}
+}
+
+func subnetConfigType() schema.ComplexTypeSpec {
+	return schema.ComplexTypeSpec{
+		ObjectTypeSpec: schema.ObjectTypeSpec{
+			Type:        "object",
+			Description: "Configuration for a VPC subnet.",
+			Properties: map[string]schema.PropertySpec{
+				"type": {
+					Description: "The type of subnet.",
+					TypeSpec: schema.TypeSpec{
+						Ref:   localRef("SubnetType"),
+						Plain: true,
+					},
+				},
+				"name": {
+					Description: "The subnet's name. Will be templated upon creation.",
+					TypeSpec:    plainString(),
+				},
+				"cidrMask": {
+					// The validation rules are too difficult to concisely describe here, so we'll leave that job to any
+					// error messages generated from the component itself.
+					Description: "The bitmask for the subnet's CIDR block.",
+					TypeSpec:    plainInt(),
+				},
+			},
+			Required: []string{
+				"type",
+				"name",
+				"cidrMask",
+			},
+		},
+	}
+}
+
+func subnetType() schema.ComplexTypeSpec {
+	return schema.ComplexTypeSpec{
+		ObjectTypeSpec: schema.ObjectTypeSpec{
+			Type:        "string",
+			Description: "A type of subnet within a VPC.",
+		},
+		Enum: []schema.EnumValueSpec{
+			{
+				Value:       "Public",
+				Description: "A subnet whose hosts can directly communicate with the internet.",
+			},
+			{
+				Value:       "Private",
+				Description: "A subnet whose hosts can not directly communicate with the internet, but can initiate outbound network traffic via a NAT Gateway.",
+			},
+			{
+				Value:       "Isolated",
+				Description: "A subnet whose hosts have no connectivity with the internet.",
+			},
+		},
+	}
+}
+
+func natGatewayConfigurationType() schema.ComplexTypeSpec {
+	return schema.ComplexTypeSpec{
+		ObjectTypeSpec: schema.ObjectTypeSpec{
+			Type:        "object",
+			Description: "Configuration for NAT Gateways.",
+			Properties: map[string]schema.PropertySpec{
+				"strategy": {
+					Description: "The strategy for deploying NAT Gateways.",
+					TypeSpec: schema.TypeSpec{
+						Ref:   localRef("NatGatewayStrategy"),
+						Plain: true,
+					},
+				},
+				"elasticIpAllocationIds": {
+					Description: "A list of EIP allocation IDs to assign to the NAT Gateways. Optional. If specified, the number of supplied values must match the chosen strategy (either one, or the number of availability zones).",
+					TypeSpec:    plainArrayOfPulumiStrings(),
+				},
+			},
+			Required: []string{"strategy"},
+		},
+	}
+}
+
+func natGatewayStrategyType() schema.ComplexTypeSpec {
+	return schema.ComplexTypeSpec{
+		ObjectTypeSpec: schema.ObjectTypeSpec{
+			Type:        "string",
+			Description: "A strategy for creating NAT Gateways for private subnets within a VPC.",
+		},
+		Enum: []schema.EnumValueSpec{
+			{
+				Value:       "None",
+				Description: "Do not create any NAT Gateways. Resources in private subnets will not be able to access the internet.",
+			},
+			{
+				Value:       "Single",
+				Description: "Create a single NAT Gateway for the entire VPC. This configuration is not recommended for production infrastructure as it creates a single point of failure.",
+			},
+			{
+				Value:       "OnePerAz",
+				Description: "Create a NAT Gateway in each availability zone. This is the recommended configuration for production infrastructure.",
+			},
+		},
+	}
+}
+
+func defaultVpcResource(spec schema.PackageSpec) schema.ResourceSpec {
+	return schema.ResourceSpec{
+		IsComponent: true,
+		ObjectTypeSpec: schema.ObjectTypeSpec{
+			Description: "Pseudo resource representing the default VPC and associated subnets for an account and region. This does not create any resources. This will be replaced with `getDefaultVpc` in the future.",
+			Properties: map[string]schema.PropertySpec{
+				"vpcId": {
+					Description: "The VPC ID for the default VPC",
+					TypeSpec: schema.TypeSpec{
+						Type: "string",
+					},
+				},
+				"publicSubnetIds": {
+					TypeSpec: schema.TypeSpec{
+						Type: "array",
+						Items: &schema.TypeSpec{
+							Type: "string",
+						},
+					},
+				},
+				"privateSubnetIds": {
+					TypeSpec: schema.TypeSpec{
+						Type: "array",
+						Items: &schema.TypeSpec{
+							Type: "string",
+						},
+					},
+				},
+			},
+			Required: []string{"vpcId", "publicSubnetIds", "privateSubnetIds"},
+		},
+	}
+}
+
+func defaultVpcArgs() schema.FunctionSpec {
+	spec := schema.FunctionSpec{
+		Description:        "[NOT YET IMPLEMENTED] Get the Default VPC for a region.",
+		DeprecationMessage: "Waiting for https://github.com/pulumi/pulumi/issues/7583. Use the DefaultVpc resource until resolved.",
+		Inputs: &schema.ObjectTypeSpec{
+			Description: "Arguments for getting the default VPC",
+			Properties:  map[string]schema.PropertySpec{},
+		},
+		Outputs: &schema.ObjectTypeSpec{
+			Description: "Outputs from the default VPC configuration",
+			Properties: map[string]schema.PropertySpec{
+				"vpcId": {
+					Description: "The VPC ID for the default VPC",
+					TypeSpec: schema.TypeSpec{
+						Type: "string",
+					},
+				},
+				"publicSubnetIds": {
+					TypeSpec: schema.TypeSpec{
+						Type: "array",
+						Items: &schema.TypeSpec{
+							Type: "string",
+						},
+					},
+				},
+				"privateSubnetIds": {
+					TypeSpec: schema.TypeSpec{
+						Type: "array",
+						Items: &schema.TypeSpec{
+							Type: "string",
+						},
+					},
+				},
+			},
+			Required: []string{"vpcId", "publicSubnetIds", "privateSubnetIds"},
+		},
+	}
+	return spec
+}
+
+func plainInt() schema.TypeSpec {
+	return schema.TypeSpec{
+		Type:  "integer",
+		Plain: true,
+	}
+}
+
+func plainString() schema.TypeSpec {
+	return schema.TypeSpec{
+		Type:  "string",
+		Plain: true,
+	}
+}
+
+func plainArrayOfPlainStrings() schema.TypeSpec {
+	return schema.TypeSpec{
+		Type: "array",
+		Items: &schema.TypeSpec{
+			Type:  "string",
+			Plain: true,
+		},
+		Plain: true,
+	}
+}
+
+func plainArrayOfPulumiStrings() schema.TypeSpec {
+	return schema.TypeSpec{
+		Type: "array",
+		Items: &schema.TypeSpec{
+			Type: "string",
+		},
+		Plain: true,
+	}
+}
+
+func plainArrayOfPlainComplexType(name string) schema.TypeSpec {
+	return schema.TypeSpec{
+		Type: "array",
+		Items: &schema.TypeSpec{
+			Ref:   localRef(name),
+			Plain: true,
+		},
+		Plain: true,
+	}
+}
+
+func localRef(name string) string {
+	return fmt.Sprintf("#/types/awsx:ec2:%s", name)
+}
diff --git a/schemagen/pkg/gen/schema.go b/schemagen/pkg/gen/schema.go
index 1ea5ce72..10f31228 100644
--- a/schemagen/pkg/gen/schema.go
+++ b/schemagen/pkg/gen/schema.go
@@ -96,7 +96,6 @@ func GenerateSchema(packageDir string) schema.PackageSpec {
 		generateIam(awsSpec),
 		generateS3(awsSpec),
 		generateEc2(awsSpec),
-		generateVpc(awsSpec),
 		generateEcr(awsSpec, dockerSpec),
 	)
 }
diff --git a/schemagen/pkg/gen/vpc.go b/schemagen/pkg/gen/vpc.go
deleted file mode 100644
index 4d9b342e..00000000
--- a/schemagen/pkg/gen/vpc.go
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright 2016-2022, Pulumi Corporation.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// 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.
-
-package gen
-
-import (
-	"fmt"
-	"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
-)
-
-func generateVpc(awsSpec schema.PackageSpec) schema.PackageSpec {
-	packageSpec := schema.PackageSpec{
-		Resources: map[string]schema.ResourceSpec{
-			"awsx:vpc:Vpc":        vpcResource(awsSpec),
-			"awsx:vpc:DefaultVpc": defaultVpcResource(awsSpec),
-		},
-		Types: map[string]schema.ComplexTypeSpec{
-			"awsx:vpc:NatGatewayStrategy":      natGatewayStrategyType(),
-			"awsx:vpc:NatGatewayConfiguration": natGatewayConfigurationType(),
-			"awsx:vpc:SubnetType":              subnetType(),
-			"awsx:vpc:SubnetConfiguration":     subnetConfigType(),
-		},
-		Functions: map[string]schema.FunctionSpec{
-			"awsx:vpc:getDefaultVpc": defaultVpcArgs(),
-		},
-	}
-
-	return packageSpec
-}
-
-const (
-	subnetsPerAz          = "subnetsPerAz"
-	availabilityZoneNames = "availabilityZoneNames"
-)
-
-func vpcResource(awsSpec schema.PackageSpec) schema.ResourceSpec {
-	awsVpcResource := awsSpec.Resources["aws:ec2/vpc:Vpc"]
-	inputProperties := map[string]schema.PropertySpec{
-		availabilityZoneNames: {
-			Description: fmt.Sprintf("A list of availability zones to which the subnets defined in %s will be deployed. Optional, defaults to the first 3 AZs in the current region.", subnetsPerAz),
-			TypeSpec:    plainArrayOfPlainStrings(),
-		},
-		"cidrBlock": {
-			Description: "The CIDR block for the VPC. Optional. Defaults to 10.0.0.0/16.",
-			TypeSpec:    plainString(),
-		},
-		"natGateways": {
-			Description: "Configuration for NAT Gateways. Optional. If private and public subnets are both specified, defaults to one gateway per availability zone. Otherwise, no gateways will be created.",
-			TypeSpec: schema.TypeSpec{
-				Ref:   localRef("NatGatewayConfiguration"),
-				Plain: true,
-			},
-		},
-		subnetsPerAz: {
-			Description: fmt.Sprintf("A list of subnets that should be deployed to each AZ specified in %s. Optional. Defaults to a (smaller) public subnet and a (larger) private subnet based on the size of the CIDR block for the VPC.", availabilityZoneNames),
-			TypeSpec:    plainArrayOfPlainComplexType("SubnetConfiguration"),
-		},
-	}
-	for k, v := range awsVpcResource.InputProperties {
-		// We redefine some of the aws.Vpc properties above as plain types because they have default values in the
-		// provider implementation. In this case, we should skip the property on the raw VPC resource:
-		_, containsKey := inputProperties[k]
-		if containsKey {
-			continue
-		}
-
-		inputProperties[k] = renamePropertyRefs(v, "#/types/aws:", packageRef(awsSpec, "/types/aws:"))
-	}
-
-	return schema.ResourceSpec{
-		IsComponent: true,
-		ObjectTypeSpec: schema.ObjectTypeSpec{
-			Properties: map[string]schema.PropertySpec{
-				"vpc": {
-					Description: "The VPC.",
-					TypeSpec: schema.TypeSpec{
-						Ref: packageRef(awsSpec, "/resources/aws:ec2%2fvpc:Vpc"),
-					},
-					Language: map[string]schema.RawMessage{
-						"csharp": schema.RawMessage(`{
-									"name": "AwsVpc"
-								}`),
-					},
-				},
-				"subnets": {
-					Description: "The VPC's subnets.",
-					TypeSpec: schema.TypeSpec{
-						Type: "array",
-						Items: &schema.TypeSpec{
-							Ref: packageRef(awsSpec, "/resources/aws:ec2%2fsubnet:Subnet"),
-						},
-					},
-				},
-			},
-		},
-		InputProperties: inputProperties,
-	}
-}
-
-func subnetConfigType() schema.ComplexTypeSpec {
-	return schema.ComplexTypeSpec{
-		ObjectTypeSpec: schema.ObjectTypeSpec{
-			Type:        "object",
-			Description: "Configuration for a VPC subnet.",
-			Properties: map[string]schema.PropertySpec{
-				"type": {
-					Description: "The type of subnet.",
-					TypeSpec: schema.TypeSpec{
-						Ref:   localRef("SubnetType"),
-						Plain: true,
-					},
-				},
-				"name": {
-					Description: "The subnet's name. Will be templated upon creation.",
-					TypeSpec:    plainString(),
-				},
-				"cidrMask": {
-					// The validation rules are too difficult to concisely describe here, so we'll leave that job to any
-					// error messages generated from the component itself.
-					Description: "The bitmask for the subnet's CIDR block.",
-					TypeSpec:    plainInt(),
-				},
-			},
-			Required: []string{
-				"type",
-				"name",
-				"cidrMask",
-			},
-		},
-	}
-}
-
-func subnetType() schema.ComplexTypeSpec {
-	return schema.ComplexTypeSpec{
-		ObjectTypeSpec: schema.ObjectTypeSpec{
-			Type:        "string",
-			Description: "A type of subnet within a VPC.",
-		},
-		Enum: []schema.EnumValueSpec{
-			{
-				Value:       "Public",
-				Description: "A subnet whose hosts can directly communicate with the internet.",
-			},
-			{
-				Value:       "Private",
-				Description: "A subnet whose hosts can not directly communicate with the internet, but can initiate outbound network traffic via a NAT Gateway.",
-			},
-			{
-				Value:       "Isolated",
-				Description: "A subnet whose hosts have no connectivity with the internet.",
-			},
-		},
-	}
-}
-
-func natGatewayConfigurationType() schema.ComplexTypeSpec {
-	return schema.ComplexTypeSpec{
-		ObjectTypeSpec: schema.ObjectTypeSpec{
-			Type:        "object",
-			Description: "Configuration for NAT Gateways.",
-			Properties: map[string]schema.PropertySpec{
-				"strategy": {
-					Description: "The strategy for deploying NAT Gateways.",
-					TypeSpec: schema.TypeSpec{
-						Ref:   localRef("NatGatewayStrategy"),
-						Plain: true,
-					},
-				},
-				"elasticIpAllocationIds": {
-					Description: "A list of EIP allocation IDs to assign to the NAT Gateways. Optional. If specified, the number of supplied values must match the chosen strategy (either one, or the number of availability zones).",
-					TypeSpec:    plainArrayOfPulumiStrings(),
-				},
-			},
-			Required: []string{"strategy"},
-		},
-	}
-}
-
-func natGatewayStrategyType() schema.ComplexTypeSpec {
-	return schema.ComplexTypeSpec{
-		ObjectTypeSpec: schema.ObjectTypeSpec{
-			Type:        "string",
-			Description: "A strategy for creating NAT Gateways for private subnets within a VPC.",
-		},
-		Enum: []schema.EnumValueSpec{
-			{
-				Value:       "None",
-				Description: "Do not create any NAT Gateways. Resources in private subnets will not be able to access the internet.",
-			},
-			{
-				Value:       "Single",
-				Description: "Create a single NAT Gateway for the entire VPC. This configuration is not recommended for production infrastructure as it creates a single point of failure.",
-			},
-			{
-				Value:       "OnePerAz",
-				Description: "Create a NAT Gateway in each availability zone. This is the recommended configuration for production infrastructure.",
-			},
-		},
-	}
-}
-
-func defaultVpcResource(spec schema.PackageSpec) schema.ResourceSpec {
-	return schema.ResourceSpec{
-		IsComponent: true,
-		ObjectTypeSpec: schema.ObjectTypeSpec{
-			Description: "Pseudo resource representing the default VPC and associated subnets for an account and region. This does not create any resources. This will be replaced with `getDefaultVpc` in the future.",
-			Properties: map[string]schema.PropertySpec{
-				"vpcId": {
-					Description: "The VPC ID for the default VPC",
-					TypeSpec: schema.TypeSpec{
-						Type: "string",
-					},
-				},
-				"publicSubnetIds": {
-					TypeSpec: schema.TypeSpec{
-						Type: "array",
-						Items: &schema.TypeSpec{
-							Type: "string",
-						},
-					},
-				},
-				"privateSubnetIds": {
-					TypeSpec: schema.TypeSpec{
-						Type: "array",
-						Items: &schema.TypeSpec{
-							Type: "string",
-						},
-					},
-				},
-			},
-			Required: []string{"vpcId", "publicSubnetIds", "privateSubnetIds"},
-		},
-	}
-}
-
-func defaultVpcArgs() schema.FunctionSpec {
-	spec := schema.FunctionSpec{
-		Description:        "[NOT YET IMPLEMENTED] Get the Default VPC for a region.",
-		DeprecationMessage: "Waiting for https://github.com/pulumi/pulumi/issues/7583. Use the DefaultVpc resource until resolved.",
-		Inputs: &schema.ObjectTypeSpec{
-			Description: "Arguments for getting the default VPC",
-			Properties:  map[string]schema.PropertySpec{},
-		},
-		Outputs: &schema.ObjectTypeSpec{
-			Description: "Outputs from the default VPC configuration",
-			Properties: map[string]schema.PropertySpec{
-				"vpcId": {
-					Description: "The VPC ID for the default VPC",
-					TypeSpec: schema.TypeSpec{
-						Type: "string",
-					},
-				},
-				"publicSubnetIds": {
-					TypeSpec: schema.TypeSpec{
-						Type: "array",
-						Items: &schema.TypeSpec{
-							Type: "string",
-						},
-					},
-				},
-				"privateSubnetIds": {
-					TypeSpec: schema.TypeSpec{
-						Type: "array",
-						Items: &schema.TypeSpec{
-							Type: "string",
-						},
-					},
-				},
-			},
-			Required: []string{"vpcId", "publicSubnetIds", "privateSubnetIds"},
-		},
-	}
-	return spec
-}
-
-func plainInt() schema.TypeSpec {
-	return schema.TypeSpec{
-		Type:  "integer",
-		Plain: true,
-	}
-}
-
-func plainString() schema.TypeSpec {
-	return schema.TypeSpec{
-		Type:  "string",
-		Plain: true,
-	}
-}
-
-func plainArrayOfPlainStrings() schema.TypeSpec {
-	return schema.TypeSpec{
-		Type: "array",
-		Items: &schema.TypeSpec{
-			Type:  "string",
-			Plain: true,
-		},
-		Plain: true,
-	}
-}
-
-func plainArrayOfPulumiStrings() schema.TypeSpec {
-	return schema.TypeSpec{
-		Type: "array",
-		Items: &schema.TypeSpec{
-			Type: "string",
-		},
-		Plain: true,
-	}
-}
-
-func plainArrayOfPlainComplexType(name string) schema.TypeSpec {
-	return schema.TypeSpec{
-		Type: "array",
-		Items: &schema.TypeSpec{
-			Ref:   localRef(name),
-			Plain: true,
-		},
-		Plain: true,
-	}
-}
-
-func localRef(name string) string {
-	return fmt.Sprintf("#/types/awsx:vpc:%s", name)
-}
diff --git a/sdk/dotnet/Vpc/DefaultVpc.cs b/sdk/dotnet/Ec2/DefaultVpc.cs
similarity index 94%
rename from sdk/dotnet/Vpc/DefaultVpc.cs
rename to sdk/dotnet/Ec2/DefaultVpc.cs
index 7c0500f6..ac7d54b1 100644
--- a/sdk/dotnet/Vpc/DefaultVpc.cs
+++ b/sdk/dotnet/Ec2/DefaultVpc.cs
@@ -7,12 +7,12 @@ using System.Collections.Immutable;
 using System.Threading.Tasks;
 using Pulumi.Serialization;
 
-namespace Pulumi.Awsx.Vpc
+namespace Pulumi.Awsx.Ec2
 {
     /// <summary>
     /// Pseudo resource representing the default VPC and associated subnets for an account and region. This does not create any resources. This will be replaced with `getDefaultVpc` in the future.
     /// </summary>
-    [AwsxResourceType("awsx:vpc:DefaultVpc")]
+    [AwsxResourceType("awsx:ec2:DefaultVpc")]
     public partial class DefaultVpc : Pulumi.ComponentResource
     {
         [Output("privateSubnetIds")]
@@ -36,7 +36,7 @@ namespace Pulumi.Awsx.Vpc
         /// <param name="args">The arguments used to populate this resource's properties</param>
         /// <param name="options">A bag of options that control this resource's behavior</param>
         public DefaultVpc(string name, DefaultVpcArgs? args = null, ComponentResourceOptions? options = null)
-            : base("awsx:vpc:DefaultVpc", name, args ?? new DefaultVpcArgs(), MakeResourceOptions(options, ""), remote: true)
+            : base("awsx:ec2:DefaultVpc", name, args ?? new DefaultVpcArgs(), MakeResourceOptions(options, ""), remote: true)
         {
         }
 
diff --git a/sdk/dotnet/Vpc/Enums.cs b/sdk/dotnet/Ec2/Enums.cs
similarity index 99%
rename from sdk/dotnet/Vpc/Enums.cs
rename to sdk/dotnet/Ec2/Enums.cs
index 9a89505d..7e809f51 100644
--- a/sdk/dotnet/Vpc/Enums.cs
+++ b/sdk/dotnet/Ec2/Enums.cs
@@ -5,7 +5,7 @@ using System;
 using System.ComponentModel;
 using Pulumi;
 
-namespace Pulumi.Awsx.Vpc
+namespace Pulumi.Awsx.Ec2
 {
     /// <summary>
     /// A strategy for creating NAT Gateways for private subnets within a VPC.
diff --git a/sdk/dotnet/Vpc/GetDefaultVpc.cs b/sdk/dotnet/Ec2/GetDefaultVpc.cs
similarity index 94%
rename from sdk/dotnet/Vpc/GetDefaultVpc.cs
rename to sdk/dotnet/Ec2/GetDefaultVpc.cs
index 74e472e6..20055270 100644
--- a/sdk/dotnet/Vpc/GetDefaultVpc.cs
+++ b/sdk/dotnet/Ec2/GetDefaultVpc.cs
@@ -7,7 +7,7 @@ using System.Collections.Immutable;
 using System.Threading.Tasks;
 using Pulumi.Serialization;
 
-namespace Pulumi.Awsx.Vpc
+namespace Pulumi.Awsx.Ec2
 {
     [Obsolete(@"Waiting for https://github.com/pulumi/pulumi/issues/7583. Use the DefaultVpc resource until resolved.")]
     public static class GetDefaultVpc
@@ -16,7 +16,7 @@ namespace Pulumi.Awsx.Vpc
         /// [NOT YET IMPLEMENTED] Get the Default VPC for a region.
         /// </summary>
         public static Task<GetDefaultVpcResult> InvokeAsync(GetDefaultVpcArgs? args = null, InvokeOptions? options = null)
-            => Pulumi.Deployment.Instance.InvokeAsync<GetDefaultVpcResult>("awsx:vpc:getDefaultVpc", args ?? new GetDefaultVpcArgs(), options.WithDefaults());
+            => Pulumi.Deployment.Instance.InvokeAsync<GetDefaultVpcResult>("awsx:ec2:getDefaultVpc", args ?? new GetDefaultVpcArgs(), options.WithDefaults());
     }
 
 
diff --git a/sdk/dotnet/Vpc/Inputs/NatGatewayConfigurationArgs.cs b/sdk/dotnet/Ec2/Inputs/NatGatewayConfigurationArgs.cs
similarity index 92%
rename from sdk/dotnet/Vpc/Inputs/NatGatewayConfigurationArgs.cs
rename to sdk/dotnet/Ec2/Inputs/NatGatewayConfigurationArgs.cs
index 4dbf2cdc..7aaed1c3 100644
--- a/sdk/dotnet/Vpc/Inputs/NatGatewayConfigurationArgs.cs
+++ b/sdk/dotnet/Ec2/Inputs/NatGatewayConfigurationArgs.cs
@@ -7,7 +7,7 @@ using System.Collections.Immutable;
 using System.Threading.Tasks;
 using Pulumi.Serialization;
 
-namespace Pulumi.Awsx.Vpc.Inputs
+namespace Pulumi.Awsx.Ec2.Inputs
 {
 
     /// <summary>
@@ -31,7 +31,7 @@ namespace Pulumi.Awsx.Vpc.Inputs
         /// The strategy for deploying NAT Gateways.
         /// </summary>
         [Input("strategy", required: true)]
-        public Pulumi.Awsx.Vpc.NatGatewayStrategy Strategy { get; set; }
+        public Pulumi.Awsx.Ec2.NatGatewayStrategy Strategy { get; set; }
 
         public NatGatewayConfigurationArgs()
         {
diff --git a/sdk/dotnet/Vpc/Inputs/SubnetConfigurationArgs.cs b/sdk/dotnet/Ec2/Inputs/SubnetConfigurationArgs.cs
similarity index 91%
rename from sdk/dotnet/Vpc/Inputs/SubnetConfigurationArgs.cs
rename to sdk/dotnet/Ec2/Inputs/SubnetConfigurationArgs.cs
index ea3c6c73..a4a6196d 100644
--- a/sdk/dotnet/Vpc/Inputs/SubnetConfigurationArgs.cs
+++ b/sdk/dotnet/Ec2/Inputs/SubnetConfigurationArgs.cs
@@ -7,7 +7,7 @@ using System.Collections.Immutable;
 using System.Threading.Tasks;
 using Pulumi.Serialization;
 
-namespace Pulumi.Awsx.Vpc.Inputs
+namespace Pulumi.Awsx.Ec2.Inputs
 {
 
     /// <summary>
@@ -31,7 +31,7 @@ namespace Pulumi.Awsx.Vpc.Inputs
         /// The type of subnet.
         /// </summary>
         [Input("type", required: true)]
-        public Pulumi.Awsx.Vpc.SubnetType Type { get; set; }
+        public Pulumi.Awsx.Ec2.SubnetType Type { get; set; }
 
         public SubnetConfigurationArgs()
         {
diff --git a/sdk/dotnet/Vpc/README.md b/sdk/dotnet/Ec2/README.md
similarity index 100%
rename from sdk/dotnet/Vpc/README.md
rename to sdk/dotnet/Ec2/README.md
diff --git a/sdk/dotnet/Vpc/Vpc.cs b/sdk/dotnet/Ec2/Vpc.cs
similarity index 83%
rename from sdk/dotnet/Vpc/Vpc.cs
rename to sdk/dotnet/Ec2/Vpc.cs
index 87c33d23..3e0c2ab5 100644
--- a/sdk/dotnet/Vpc/Vpc.cs
+++ b/sdk/dotnet/Ec2/Vpc.cs
@@ -7,11 +7,47 @@ using System.Collections.Immutable;
 using System.Threading.Tasks;
 using Pulumi.Serialization;
 
-namespace Pulumi.Awsx.Vpc
+namespace Pulumi.Awsx.Ec2
 {
-    [AwsxResourceType("awsx:vpc:Vpc")]
+    [AwsxResourceType("awsx:ec2:Vpc")]
     public partial class Vpc : Pulumi.ComponentResource
     {
+        /// <summary>
+        /// The EIPs for any NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+        /// </summary>
+        [Output("eips")]
+        public Output<ImmutableArray<Pulumi.Aws.Ec2.Eip>> Eips { get; private set; } = null!;
+
+        /// <summary>
+        /// The Internet Gateway for the VPC.
+        /// </summary>
+        [Output("internetGateway")]
+        public Output<Pulumi.Aws.Ec2.InternetGateway?> InternetGateway { get; private set; } = null!;
+
+        /// <summary>
+        /// The NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+        /// </summary>
+        [Output("natGateways")]
+        public Output<ImmutableArray<Pulumi.Aws.Ec2.NatGateway>> NatGateways { get; private set; } = null!;
+
+        /// <summary>
+        /// The Route Table Associations for the VPC.
+        /// </summary>
+        [Output("routeTableAssociations")]
+        public Output<ImmutableArray<Pulumi.Aws.Ec2.RouteTableAssociation>> RouteTableAssociations { get; private set; } = null!;
+
+        /// <summary>
+        /// The Route Tables for the VPC.
+        /// </summary>
+        [Output("routeTables")]
+        public Output<ImmutableArray<Pulumi.Aws.Ec2.RouteTable>> RouteTables { get; private set; } = null!;
+
+        /// <summary>
+        /// The Routes for the VPC.
+        /// </summary>
+        [Output("routes")]
+        public Output<ImmutableArray<Pulumi.Aws.Ec2.Route>> Routes { get; private set; } = null!;
+
         /// <summary>
         /// The VPC's subnets.
         /// </summary>
@@ -33,7 +69,7 @@ namespace Pulumi.Awsx.Vpc
         /// <param name="args">The arguments used to populate this resource's properties</param>
         /// <param name="options">A bag of options that control this resource's behavior</param>
         public Vpc(string name, VpcArgs? args = null, ComponentResourceOptions? options = null)
-            : base("awsx:vpc:Vpc", name, args ?? new VpcArgs(), MakeResourceOptions(options, ""), remote: true)
+            : base("awsx:ec2:Vpc", name, args ?? new VpcArgs(), MakeResourceOptions(options, ""), remote: true)
         {
         }
 
diff --git a/sdk/go/awsx/vpc/defaultVpc.go b/sdk/go/awsx/ec2/defaultVpc.go
similarity index 98%
rename from sdk/go/awsx/vpc/defaultVpc.go
rename to sdk/go/awsx/ec2/defaultVpc.go
index 47d2b1f9..d2f66279 100644
--- a/sdk/go/awsx/vpc/defaultVpc.go
+++ b/sdk/go/awsx/ec2/defaultVpc.go
@@ -1,7 +1,7 @@
 // Code generated by pulumi-gen-awsx DO NOT EDIT.
 // *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***
 
-package vpc
+package ec2
 
 import (
 	"context"
@@ -28,7 +28,7 @@ func NewDefaultVpc(ctx *pulumi.Context,
 	}
 
 	var resource DefaultVpc
-	err := ctx.RegisterRemoteComponentResource("awsx:vpc:DefaultVpc", name, args, &resource, opts...)
+	err := ctx.RegisterRemoteComponentResource("awsx:ec2:DefaultVpc", name, args, &resource, opts...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/sdk/go/awsx/vpc/getDefaultVpc.go b/sdk/go/awsx/ec2/getDefaultVpc.go
similarity index 92%
rename from sdk/go/awsx/vpc/getDefaultVpc.go
rename to sdk/go/awsx/ec2/getDefaultVpc.go
index 40266738..686aa92d 100644
--- a/sdk/go/awsx/vpc/getDefaultVpc.go
+++ b/sdk/go/awsx/ec2/getDefaultVpc.go
@@ -1,7 +1,7 @@
 // Code generated by pulumi-gen-awsx DO NOT EDIT.
 // *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***
 
-package vpc
+package ec2
 
 import (
 	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
@@ -12,7 +12,7 @@ import (
 // Deprecated: Waiting for https://github.com/pulumi/pulumi/issues/7583. Use the DefaultVpc resource until resolved.
 func GetDefaultVpc(ctx *pulumi.Context, args *GetDefaultVpcArgs, opts ...pulumi.InvokeOption) (*GetDefaultVpcResult, error) {
 	var rv GetDefaultVpcResult
-	err := ctx.Invoke("awsx:vpc:getDefaultVpc", args, &rv, opts...)
+	err := ctx.Invoke("awsx:ec2:getDefaultVpc", args, &rv, opts...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/sdk/go/awsx/vpc/init.go b/sdk/go/awsx/ec2/init.go
similarity index 92%
rename from sdk/go/awsx/vpc/init.go
rename to sdk/go/awsx/ec2/init.go
index 7ac10c8d..d84328ad 100644
--- a/sdk/go/awsx/vpc/init.go
+++ b/sdk/go/awsx/ec2/init.go
@@ -1,7 +1,7 @@
 // Code generated by pulumi-gen-awsx DO NOT EDIT.
 // *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***
 
-package vpc
+package ec2
 
 import (
 	"fmt"
@@ -21,9 +21,9 @@ func (m *module) Version() semver.Version {
 
 func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi.Resource, err error) {
 	switch typ {
-	case "awsx:vpc:DefaultVpc":
+	case "awsx:ec2:DefaultVpc":
 		r = &DefaultVpc{}
-	case "awsx:vpc:Vpc":
+	case "awsx:ec2:Vpc":
 		r = &Vpc{}
 	default:
 		return nil, fmt.Errorf("unknown resource type: %s", typ)
@@ -40,7 +40,7 @@ func init() {
 	}
 	pulumi.RegisterResourceModule(
 		"awsx",
-		"vpc",
+		"ec2",
 		&module{version},
 	)
 }
diff --git a/sdk/go/awsx/vpc/pulumiEnums.go b/sdk/go/awsx/ec2/pulumiEnums.go
similarity index 99%
rename from sdk/go/awsx/vpc/pulumiEnums.go
rename to sdk/go/awsx/ec2/pulumiEnums.go
index 72006bc2..29e27359 100644
--- a/sdk/go/awsx/vpc/pulumiEnums.go
+++ b/sdk/go/awsx/ec2/pulumiEnums.go
@@ -1,7 +1,7 @@
 // Code generated by pulumi-gen-awsx DO NOT EDIT.
 // *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***
 
-package vpc
+package ec2
 
 // A strategy for creating NAT Gateways for private subnets within a VPC.
 type NatGatewayStrategy string
diff --git a/sdk/go/awsx/vpc/pulumiTypes.go b/sdk/go/awsx/ec2/pulumiTypes.go
similarity index 98%
rename from sdk/go/awsx/vpc/pulumiTypes.go
rename to sdk/go/awsx/ec2/pulumiTypes.go
index 3b308a1c..4094bffd 100644
--- a/sdk/go/awsx/vpc/pulumiTypes.go
+++ b/sdk/go/awsx/ec2/pulumiTypes.go
@@ -1,7 +1,7 @@
 // Code generated by pulumi-gen-awsx DO NOT EDIT.
 // *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***
 
-package vpc
+package ec2
 
 // Configuration for NAT Gateways.
 type NatGatewayConfiguration struct {
diff --git a/sdk/go/awsx/vpc/vpc.go b/sdk/go/awsx/ec2/vpc.go
similarity index 94%
rename from sdk/go/awsx/vpc/vpc.go
rename to sdk/go/awsx/ec2/vpc.go
index 74a96d0b..d2d30d65 100644
--- a/sdk/go/awsx/vpc/vpc.go
+++ b/sdk/go/awsx/ec2/vpc.go
@@ -1,7 +1,7 @@
 // Code generated by pulumi-gen-awsx DO NOT EDIT.
 // *** WARNING: Do not edit by hand unless you're certain you know what you are doing! ***
 
-package vpc
+package ec2
 
 import (
 	"context"
@@ -14,6 +14,18 @@ import (
 type Vpc struct {
 	pulumi.ResourceState
 
+	// The EIPs for any NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+	Eips ec2.EipArrayOutput `pulumi:"eips"`
+	// The Internet Gateway for the VPC.
+	InternetGateway ec2.InternetGatewayOutput `pulumi:"internetGateway"`
+	// The NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+	NatGateways ec2.NatGatewayArrayOutput `pulumi:"natGateways"`
+	// The Route Table Associations for the VPC.
+	RouteTableAssociations ec2.RouteTableAssociationArrayOutput `pulumi:"routeTableAssociations"`
+	// The Route Tables for the VPC.
+	RouteTables ec2.RouteTableArrayOutput `pulumi:"routeTables"`
+	// The Routes for the VPC.
+	Routes ec2.RouteArrayOutput `pulumi:"routes"`
 	// The VPC's subnets.
 	Subnets ec2.SubnetArrayOutput `pulumi:"subnets"`
 	// The VPC.
@@ -28,7 +40,7 @@ func NewVpc(ctx *pulumi.Context,
 	}
 
 	var resource Vpc
-	err := ctx.RegisterRemoteComponentResource("awsx:vpc:Vpc", name, args, &resource, opts...)
+	err := ctx.RegisterRemoteComponentResource("awsx:ec2:Vpc", name, args, &resource, opts...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/sdk/nodejs/vpc/defaultVpc.ts b/sdk/nodejs/ec2/defaultVpc.ts
similarity index 97%
rename from sdk/nodejs/vpc/defaultVpc.ts
rename to sdk/nodejs/ec2/defaultVpc.ts
index de4646f0..8056dbd0 100644
--- a/sdk/nodejs/vpc/defaultVpc.ts
+++ b/sdk/nodejs/ec2/defaultVpc.ts
@@ -9,7 +9,7 @@ import * as utilities from "../utilities";
  */
 export class DefaultVpc extends pulumi.ComponentResource {
     /** @internal */
-    public static readonly __pulumiType = 'awsx:vpc:DefaultVpc';
+    public static readonly __pulumiType = 'awsx:ec2:DefaultVpc';
 
     /**
      * Returns true if the given object is an instance of DefaultVpc.  This is designed to work even
diff --git a/sdk/nodejs/vpc/getDefaultVpc.ts b/sdk/nodejs/ec2/getDefaultVpc.ts
similarity index 95%
rename from sdk/nodejs/vpc/getDefaultVpc.ts
rename to sdk/nodejs/ec2/getDefaultVpc.ts
index 09fba745..c9ff39e8 100644
--- a/sdk/nodejs/vpc/getDefaultVpc.ts
+++ b/sdk/nodejs/ec2/getDefaultVpc.ts
@@ -16,7 +16,7 @@ export function getDefaultVpc(args?: GetDefaultVpcArgs, opts?: pulumi.InvokeOpti
     }
 
     opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts);
-    return pulumi.runtime.invoke("awsx:vpc:getDefaultVpc", {
+    return pulumi.runtime.invoke("awsx:ec2:getDefaultVpc", {
     }, opts);
 }
 
diff --git a/sdk/nodejs/vpc/index.ts b/sdk/nodejs/ec2/index.ts
similarity index 83%
rename from sdk/nodejs/vpc/index.ts
rename to sdk/nodejs/ec2/index.ts
index 992406fe..d3ac4b81 100644
--- a/sdk/nodejs/vpc/index.ts
+++ b/sdk/nodejs/ec2/index.ts
@@ -10,7 +10,7 @@ export * from "./getDefaultVpc";
 export * from "./vpc";
 
 // Export enums:
-export * from "../types/enums/vpc";
+export * from "../types/enums/ec2";
 
 // Import resources to register:
 import { DefaultVpc } from "./defaultVpc";
@@ -20,13 +20,13 @@ const _module = {
     version: utilities.getVersion(),
     construct: (name: string, type: string, urn: string): pulumi.Resource => {
         switch (type) {
-            case "awsx:vpc:DefaultVpc":
+            case "awsx:ec2:DefaultVpc":
                 return new DefaultVpc(name, <any>undefined, { urn })
-            case "awsx:vpc:Vpc":
+            case "awsx:ec2:Vpc":
                 return new Vpc(name, <any>undefined, { urn })
             default:
                 throw new Error(`unknown resource type ${type}`);
         }
     },
 };
-pulumi.runtime.registerResourceModule("awsx", "vpc", _module)
+pulumi.runtime.registerResourceModule("awsx", "ec2", _module)
diff --git a/sdk/nodejs/vpc/vpc.ts b/sdk/nodejs/ec2/vpc.ts
similarity index 80%
rename from sdk/nodejs/vpc/vpc.ts
rename to sdk/nodejs/ec2/vpc.ts
index 4c6371c0..a59a98f4 100644
--- a/sdk/nodejs/vpc/vpc.ts
+++ b/sdk/nodejs/ec2/vpc.ts
@@ -9,7 +9,7 @@ import * as pulumiAws from "@pulumi/aws";
 
 export class Vpc extends pulumi.ComponentResource {
     /** @internal */
-    public static readonly __pulumiType = 'awsx:vpc:Vpc';
+    public static readonly __pulumiType = 'awsx:ec2:Vpc';
 
     /**
      * Returns true if the given object is an instance of Vpc.  This is designed to work even
@@ -22,6 +22,30 @@ export class Vpc extends pulumi.ComponentResource {
         return obj['__pulumiType'] === Vpc.__pulumiType;
     }
 
+    /**
+     * The EIPs for any NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+     */
+    public /*out*/ readonly eips!: pulumi.Output<pulumiAws.ec2.Eip[] | undefined>;
+    /**
+     * The Internet Gateway for the VPC.
+     */
+    public /*out*/ readonly internetGateway!: pulumi.Output<pulumiAws.ec2.InternetGateway | undefined>;
+    /**
+     * The NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+     */
+    public readonly natGateways!: pulumi.Output<pulumiAws.ec2.NatGateway[] | undefined>;
+    /**
+     * The Route Table Associations for the VPC.
+     */
+    public /*out*/ readonly routeTableAssociations!: pulumi.Output<pulumiAws.ec2.RouteTableAssociation[] | undefined>;
+    /**
+     * The Route Tables for the VPC.
+     */
+    public /*out*/ readonly routeTables!: pulumi.Output<pulumiAws.ec2.RouteTable[] | undefined>;
+    /**
+     * The Routes for the VPC.
+     */
+    public /*out*/ readonly routes!: pulumi.Output<pulumiAws.ec2.Route[] | undefined>;
     /**
      * The VPC's subnets.
      */
@@ -59,9 +83,20 @@ export class Vpc extends pulumi.ComponentResource {
             resourceInputs["natGateways"] = args ? args.natGateways : undefined;
             resourceInputs["subnetsPerAz"] = args ? args.subnetsPerAz : undefined;
             resourceInputs["tags"] = args ? args.tags : undefined;
+            resourceInputs["eips"] = undefined /*out*/;
+            resourceInputs["internetGateway"] = undefined /*out*/;
+            resourceInputs["routeTableAssociations"] = undefined /*out*/;
+            resourceInputs["routeTables"] = undefined /*out*/;
+            resourceInputs["routes"] = undefined /*out*/;
             resourceInputs["subnets"] = undefined /*out*/;
             resourceInputs["vpc"] = undefined /*out*/;
         } else {
+            resourceInputs["eips"] = undefined /*out*/;
+            resourceInputs["internetGateway"] = undefined /*out*/;
+            resourceInputs["natGateways"] = undefined /*out*/;
+            resourceInputs["routeTableAssociations"] = undefined /*out*/;
+            resourceInputs["routeTables"] = undefined /*out*/;
+            resourceInputs["routes"] = undefined /*out*/;
             resourceInputs["subnets"] = undefined /*out*/;
             resourceInputs["vpc"] = undefined /*out*/;
         }
@@ -136,11 +171,11 @@ export interface VpcArgs {
     /**
      * Configuration for NAT Gateways. Optional. If private and public subnets are both specified, defaults to one gateway per availability zone. Otherwise, no gateways will be created.
      */
-    natGateways?: inputs.vpc.NatGatewayConfigurationArgs;
+    natGateways?: inputs.ec2.NatGatewayConfigurationArgs;
     /**
      * A list of subnets that should be deployed to each AZ specified in availabilityZoneNames. Optional. Defaults to a (smaller) public subnet and a (larger) private subnet based on the size of the CIDR block for the VPC.
      */
-    subnetsPerAz?: inputs.vpc.SubnetConfigurationArgs[];
+    subnetsPerAz?: inputs.ec2.SubnetConfigurationArgs[];
     /**
      * A map of tags to assign to the resource. If configured with a provider `default_tags` configuration block present, tags with matching keys will overwrite those defined at the provider-level.
      */
diff --git a/sdk/nodejs/index.ts b/sdk/nodejs/index.ts
index db5221f5..230f1b27 100644
--- a/sdk/nodejs/index.ts
+++ b/sdk/nodejs/index.ts
@@ -10,20 +10,20 @@ export * from "./provider";
 // Export sub-modules:
 import * as classic from "./classic";
 import * as cloudtrail from "./cloudtrail";
+import * as ec2 from "./ec2";
 import * as ecr from "./ecr";
 import * as ecs from "./ecs";
 import * as lb from "./lb";
 import * as types from "./types";
-import * as vpc from "./vpc";
 
 export {
     classic,
     cloudtrail,
+    ec2,
     ecr,
     ecs,
     lb,
     types,
-    vpc,
 };
 
 import { Provider } from "./provider";
diff --git a/sdk/nodejs/tsconfig.json b/sdk/nodejs/tsconfig.json
index 9a75f42d..9eab7395 100644
--- a/sdk/nodejs/tsconfig.json
+++ b/sdk/nodejs/tsconfig.json
@@ -105,6 +105,10 @@
         "classic/utils.ts",
         "cloudtrail/index.ts",
         "cloudtrail/trail.ts",
+        "ec2/defaultVpc.ts",
+        "ec2/getDefaultVpc.ts",
+        "ec2/index.ts",
+        "ec2/vpc.ts",
         "ecr/image.ts",
         "ecr/index.ts",
         "ecr/repository.ts",
@@ -118,16 +122,12 @@
         "lb/index.ts",
         "lb/networkLoadBalancer.ts",
         "provider.ts",
+        "types/enums/ec2/index.ts",
         "types/enums/ecr/index.ts",
         "types/enums/index.ts",
-        "types/enums/vpc/index.ts",
         "types/index.ts",
         "types/input.ts",
         "types/output.ts",
-        "utilities.ts",
-        "vpc/defaultVpc.ts",
-        "vpc/getDefaultVpc.ts",
-        "vpc/index.ts",
-        "vpc/vpc.ts"
+        "utilities.ts"
     ]
 }
diff --git a/sdk/nodejs/types/enums/vpc/index.ts b/sdk/nodejs/types/enums/ec2/index.ts
similarity index 100%
rename from sdk/nodejs/types/enums/vpc/index.ts
rename to sdk/nodejs/types/enums/ec2/index.ts
diff --git a/sdk/nodejs/types/enums/index.ts b/sdk/nodejs/types/enums/index.ts
index 658dcc5c..3da6bdf2 100644
--- a/sdk/nodejs/types/enums/index.ts
+++ b/sdk/nodejs/types/enums/index.ts
@@ -2,10 +2,10 @@
 // *** Do not edit by hand unless you're certain you know what you are doing! ***
 
 // Export sub-modules:
+import * as ec2 from "./ec2";
 import * as ecr from "./ecr";
-import * as vpc from "./vpc";
 
 export {
+    ec2,
     ecr,
-    vpc,
 };
diff --git a/sdk/nodejs/types/input.ts b/sdk/nodejs/types/input.ts
index 0d5a2676..60700059 100644
--- a/sdk/nodejs/types/input.ts
+++ b/sdk/nodejs/types/input.ts
@@ -359,6 +359,40 @@ export namespace awsx {
 export namespace cloudtrail {
 }
 
+export namespace ec2 {
+    /**
+     * Configuration for NAT Gateways.
+     */
+    export interface NatGatewayConfigurationArgs {
+        /**
+         * A list of EIP allocation IDs to assign to the NAT Gateways. Optional. If specified, the number of supplied values must match the chosen strategy (either one, or the number of availability zones).
+         */
+        elasticIpAllocationIds?: pulumi.Input<string>[];
+        /**
+         * The strategy for deploying NAT Gateways.
+         */
+        strategy: enums.ec2.NatGatewayStrategy;
+    }
+
+    /**
+     * Configuration for a VPC subnet.
+     */
+    export interface SubnetConfigurationArgs {
+        /**
+         * The bitmask for the subnet's CIDR block.
+         */
+        cidrMask: number;
+        /**
+         * The subnet's name. Will be templated upon creation.
+         */
+        name: string;
+        /**
+         * The type of subnet.
+         */
+        type: enums.ec2.SubnetType;
+    }
+}
+
 export namespace ecr {
     /**
      * Simplified lifecycle policy model consisting of one or more rules that determine which images in a repository should be expired. See https://docs.aws.amazon.com/AmazonECR/latest/userguide/lifecycle_policy_examples.html for more details.
@@ -2118,37 +2152,3 @@ export namespace lb {
         vpcId?: pulumi.Input<string>;
     }
 }
-
-export namespace vpc {
-    /**
-     * Configuration for NAT Gateways.
-     */
-    export interface NatGatewayConfigurationArgs {
-        /**
-         * A list of EIP allocation IDs to assign to the NAT Gateways. Optional. If specified, the number of supplied values must match the chosen strategy (either one, or the number of availability zones).
-         */
-        elasticIpAllocationIds?: pulumi.Input<string>[];
-        /**
-         * The strategy for deploying NAT Gateways.
-         */
-        strategy: enums.vpc.NatGatewayStrategy;
-    }
-
-    /**
-     * Configuration for a VPC subnet.
-     */
-    export interface SubnetConfigurationArgs {
-        /**
-         * The bitmask for the subnet's CIDR block.
-         */
-        cidrMask: number;
-        /**
-         * The subnet's name. Will be templated upon creation.
-         */
-        name: string;
-        /**
-         * The type of subnet.
-         */
-        type: enums.vpc.SubnetType;
-    }
-}
diff --git a/sdk/nodejs/types/output.ts b/sdk/nodejs/types/output.ts
index 4e021f8f..ced128c1 100644
--- a/sdk/nodejs/types/output.ts
+++ b/sdk/nodejs/types/output.ts
@@ -13,6 +13,9 @@ export namespace awsx {
 export namespace cloudtrail {
 }
 
+export namespace ec2 {
+}
+
 export namespace ecr {
 }
 
@@ -21,6 +24,3 @@ export namespace ecs {
 
 export namespace lb {
 }
-
-export namespace vpc {
-}
diff --git a/sdk/python/pulumi_awsx/__init__.py b/sdk/python/pulumi_awsx/__init__.py
index 569d077c..eb0c2179 100644
--- a/sdk/python/pulumi_awsx/__init__.py
+++ b/sdk/python/pulumi_awsx/__init__.py
@@ -13,21 +13,21 @@ if typing.TYPE_CHECKING:
     awsx = __awsx
     import pulumi_awsx.cloudtrail as __cloudtrail
     cloudtrail = __cloudtrail
+    import pulumi_awsx.ec2 as __ec2
+    ec2 = __ec2
     import pulumi_awsx.ecr as __ecr
     ecr = __ecr
     import pulumi_awsx.ecs as __ecs
     ecs = __ecs
     import pulumi_awsx.lb as __lb
     lb = __lb
-    import pulumi_awsx.vpc as __vpc
-    vpc = __vpc
 else:
     awsx = _utilities.lazy_import('pulumi_awsx.awsx')
     cloudtrail = _utilities.lazy_import('pulumi_awsx.cloudtrail')
+    ec2 = _utilities.lazy_import('pulumi_awsx.ec2')
     ecr = _utilities.lazy_import('pulumi_awsx.ecr')
     ecs = _utilities.lazy_import('pulumi_awsx.ecs')
     lb = _utilities.lazy_import('pulumi_awsx.lb')
-    vpc = _utilities.lazy_import('pulumi_awsx.vpc')
 
 _utilities.register(
     resource_modules="""
@@ -40,6 +40,15 @@ _utilities.register(
    "awsx:cloudtrail:Trail": "Trail"
   }
  },
+ {
+  "pkg": "awsx",
+  "mod": "ec2",
+  "fqn": "pulumi_awsx.ec2",
+  "classes": {
+   "awsx:ec2:DefaultVpc": "DefaultVpc",
+   "awsx:ec2:Vpc": "Vpc"
+  }
+ },
  {
   "pkg": "awsx",
   "mod": "ecr",
@@ -68,15 +77,6 @@ _utilities.register(
    "awsx:lb:ApplicationLoadBalancer": "ApplicationLoadBalancer",
    "awsx:lb:NetworkLoadBalancer": "NetworkLoadBalancer"
   }
- },
- {
-  "pkg": "awsx",
-  "mod": "vpc",
-  "fqn": "pulumi_awsx.vpc",
-  "classes": {
-   "awsx:vpc:DefaultVpc": "DefaultVpc",
-   "awsx:vpc:Vpc": "Vpc"
-  }
  }
 ]
 """,
diff --git a/sdk/python/pulumi_awsx/vpc/__init__.py b/sdk/python/pulumi_awsx/ec2/__init__.py
similarity index 100%
rename from sdk/python/pulumi_awsx/vpc/__init__.py
rename to sdk/python/pulumi_awsx/ec2/__init__.py
diff --git a/sdk/python/pulumi_awsx/vpc/_enums.py b/sdk/python/pulumi_awsx/ec2/_enums.py
similarity index 100%
rename from sdk/python/pulumi_awsx/vpc/_enums.py
rename to sdk/python/pulumi_awsx/ec2/_enums.py
diff --git a/sdk/python/pulumi_awsx/vpc/_inputs.py b/sdk/python/pulumi_awsx/ec2/_inputs.py
similarity index 100%
rename from sdk/python/pulumi_awsx/vpc/_inputs.py
rename to sdk/python/pulumi_awsx/ec2/_inputs.py
diff --git a/sdk/python/pulumi_awsx/vpc/default_vpc.py b/sdk/python/pulumi_awsx/ec2/default_vpc.py
similarity index 99%
rename from sdk/python/pulumi_awsx/vpc/default_vpc.py
rename to sdk/python/pulumi_awsx/ec2/default_vpc.py
index b6ece850..245fc701 100644
--- a/sdk/python/pulumi_awsx/vpc/default_vpc.py
+++ b/sdk/python/pulumi_awsx/ec2/default_vpc.py
@@ -73,7 +73,7 @@ class DefaultVpc(pulumi.ComponentResource):
             __props__.__dict__["public_subnet_ids"] = None
             __props__.__dict__["vpc_id"] = None
         super(DefaultVpc, __self__).__init__(
-            'awsx:vpc:DefaultVpc',
+            'awsx:ec2:DefaultVpc',
             resource_name,
             __props__,
             opts,
diff --git a/sdk/python/pulumi_awsx/vpc/get_default_vpc.py b/sdk/python/pulumi_awsx/ec2/get_default_vpc.py
similarity index 97%
rename from sdk/python/pulumi_awsx/vpc/get_default_vpc.py
rename to sdk/python/pulumi_awsx/ec2/get_default_vpc.py
index 9fd5c4e6..c4da994a 100644
--- a/sdk/python/pulumi_awsx/vpc/get_default_vpc.py
+++ b/sdk/python/pulumi_awsx/ec2/get_default_vpc.py
@@ -72,7 +72,7 @@ def get_default_vpc(opts: Optional[pulumi.InvokeOptions] = None) -> AwaitableGet
         opts = pulumi.InvokeOptions()
     if opts.version is None:
         opts.version = _utilities.get_version()
-    __ret__ = pulumi.runtime.invoke('awsx:vpc:getDefaultVpc', __args__, opts=opts, typ=GetDefaultVpcResult).value
+    __ret__ = pulumi.runtime.invoke('awsx:ec2:getDefaultVpc', __args__, opts=opts, typ=GetDefaultVpcResult).value
 
     return AwaitableGetDefaultVpcResult(
         private_subnet_ids=__ret__.private_subnet_ids,
diff --git a/sdk/python/pulumi_awsx/vpc/vpc.py b/sdk/python/pulumi_awsx/ec2/vpc.py
similarity index 93%
rename from sdk/python/pulumi_awsx/vpc/vpc.py
rename to sdk/python/pulumi_awsx/ec2/vpc.py
index f4d26823..4cad1692 100644
--- a/sdk/python/pulumi_awsx/vpc/vpc.py
+++ b/sdk/python/pulumi_awsx/ec2/vpc.py
@@ -418,15 +418,68 @@ class Vpc(pulumi.ComponentResource):
             __props__.__dict__["nat_gateways"] = nat_gateways
             __props__.__dict__["subnets_per_az"] = subnets_per_az
             __props__.__dict__["tags"] = tags
+            __props__.__dict__["eips"] = None
+            __props__.__dict__["internet_gateway"] = None
+            __props__.__dict__["route_table_associations"] = None
+            __props__.__dict__["route_tables"] = None
+            __props__.__dict__["routes"] = None
             __props__.__dict__["subnets"] = None
             __props__.__dict__["vpc"] = None
         super(Vpc, __self__).__init__(
-            'awsx:vpc:Vpc',
+            'awsx:ec2:Vpc',
             resource_name,
             __props__,
             opts,
             remote=True)
 
+    @property
+    @pulumi.getter
+    def eips(self) -> pulumi.Output[Optional[Sequence['pulumi_aws.ec2.Eip']]]:
+        """
+        The EIPs for any NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+        """
+        return pulumi.get(self, "eips")
+
+    @property
+    @pulumi.getter(name="internetGateway")
+    def internet_gateway(self) -> pulumi.Output[Optional['pulumi_aws.ec2.InternetGateway']]:
+        """
+        The Internet Gateway for the VPC.
+        """
+        return pulumi.get(self, "internet_gateway")
+
+    @property
+    @pulumi.getter(name="natGateways")
+    def nat_gateways(self) -> pulumi.Output[Optional[Sequence['pulumi_aws.ec2.NatGateway']]]:
+        """
+        The NAT Gateways for the VPC. If no NAT Gateways are specified, this will be an empty list.
+        """
+        return pulumi.get(self, "nat_gateways")
+
+    @property
+    @pulumi.getter(name="routeTableAssociations")
+    def route_table_associations(self) -> pulumi.Output[Optional[Sequence['pulumi_aws.ec2.RouteTableAssociation']]]:
+        """
+        The Route Table Associations for the VPC.
+        """
+        return pulumi.get(self, "route_table_associations")
+
+    @property
+    @pulumi.getter(name="routeTables")
+    def route_tables(self) -> pulumi.Output[Optional[Sequence['pulumi_aws.ec2.RouteTable']]]:
+        """
+        The Route Tables for the VPC.
+        """
+        return pulumi.get(self, "route_tables")
+
+    @property
+    @pulumi.getter
+    def routes(self) -> pulumi.Output[Optional[Sequence['pulumi_aws.ec2.Route']]]:
+        """
+        The Routes for the VPC.
+        """
+        return pulumi.get(self, "routes")
+
     @property
     @pulumi.getter
     def subnets(self) -> pulumi.Output[Optional[Sequence['pulumi_aws.ec2.Subnet']]]:


Test patch:
diff --git a/awsx/vpc/defaultVpc.ts b/awsx/ec2/defaultVpc.ts
similarity index 100%
rename from awsx/vpc/defaultVpc.ts
rename to awsx/ec2/defaultVpc.ts
diff --git a/awsx/vpc/getDefaultVpc.ts b/awsx/ec2/getDefaultVpc.ts
similarity index 100%
rename from awsx/vpc/getDefaultVpc.ts
rename to awsx/ec2/getDefaultVpc.ts
diff --git a/awsx/vpc/index.ts b/awsx/ec2/index.ts
similarity index 100%
rename from awsx/vpc/index.ts
rename to awsx/ec2/index.ts
diff --git a/awsx/vpc/subnetDistributor.test.ts b/awsx/ec2/subnetDistributor.test.ts
similarity index 91%
rename from awsx/vpc/subnetDistributor.test.ts
rename to awsx/ec2/subnetDistributor.test.ts
index bf468f76..b3fe36b1 100644
--- a/awsx/vpc/subnetDistributor.test.ts
+++ b/awsx/ec2/subnetDistributor.test.ts
@@ -82,19 +82,19 @@ describe("getSubnetSpecs", () => {
                     type: type,
                     cidrBlock: "10.0.0.0/19",
                     azName: "us-east-1a",
-                    name: "vpcname-foo-1",
+                    subnetName: "vpcname-foo-1",
                 },
                 {
                     type: type,
                     cidrBlock: "10.0.64.0/19",
                     azName: "us-east-1b",
-                    name: "vpcname-foo-2",
+                    subnetName: "vpcname-foo-2",
                 },
                 {
                     type: type,
                     cidrBlock: "10.0.128.0/19",
                     azName: "us-east-1c",
-                    name: "vpcname-foo-3",
+                    subnetName: "vpcname-foo-3",
                 },
             ];
 
@@ -125,37 +125,37 @@ describe("getSubnetSpecs", () => {
                     type: slash19Type,
                     cidrBlock: "10.0.0.0/19",
                     azName: "us-east-1a",
-                    name: "vpcname-foo-1",
+                    subnetName: "vpcname-foo-1",
                 },
                 {
                     type: slash19Type,
                     cidrBlock: "10.0.64.0/19",
                     azName: "us-east-1b",
-                    name: "vpcname-foo-2",
+                    subnetName: "vpcname-foo-2",
                 },
                 {
                     type: slash19Type,
                     cidrBlock: "10.0.128.0/19",
                     azName: "us-east-1c",
-                    name: "vpcname-foo-3",
+                    subnetName: "vpcname-foo-3",
                 },
                 {
                     type: slash20Type,
                     cidrBlock: "10.0.32.0/20",
                     azName: "us-east-1a",
-                    name: "vpcname-bar-1",
+                    subnetName: "vpcname-bar-1",
                 },
                 {
                     type: slash20Type,
                     cidrBlock: "10.0.96.0/20",
                     azName: "us-east-1b",
-                    name: "vpcname-bar-2",
+                    subnetName: "vpcname-bar-2",
                 },
                 {
                     type: slash20Type,
                     cidrBlock: "10.0.160.0/20",
                     azName: "us-east-1c",
-                    name: "vpcname-bar-3",
+                    subnetName: "vpcname-bar-3",
                 },
             ];
 
diff --git a/awsx/vpc/subnetDistributor.ts b/awsx/ec2/subnetDistributor.ts
similarity index 100%
rename from awsx/vpc/subnetDistributor.ts
rename to awsx/ec2/subnetDistributor.ts
diff --git a/awsx/ec2/vpc.test.ts b/awsx/ec2/vpc.test.ts
new file mode 100644
index 00000000..37161175
--- /dev/null
+++ b/awsx/ec2/vpc.test.ts
@@ -0,0 +1,137 @@
+// Copyright 2016-2022, Pulumi Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// 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.
+
+import { NatGatewayStrategyInputs, SubnetTypeInputs } from "../schema-types";
+import {
+    compareSubnetSpecs,
+    shouldCreateNatGateway,
+    validateEips,
+    validateNatGatewayStrategy,
+} from "./vpc";
+
+describe("validateEips", () => {
+    it("should throw an exception if NAT Gateway strategy is None and EIPs are supplied", () => {
+        expect(() => validateEips("None", ["abc123"]))
+            .toThrowError("cannot be specified");
+    });
+
+    it("should throw an exception if NAT Gateway strategy is Single and more than 1 EIP is supplied", () => {
+        expect(() => validateEips("Single", ["abc123", "def456"]))
+            .toThrowError("Exactly one");
+    });
+
+    it("should throw an exception if NAT Gateway strategy is OnePerAz and too few EIPs are supplied", () => {
+        expect(() => validateEips("OnePerAz", ["abc123", "def456"], ["us-east-1a", "us-east-1b", "us-east-1c"]))
+            .toThrowError("must match the number");
+    });
+
+    it("should throw an exception if NAT Gateway strategy is OnePerAz and too many EIPs are supplied", () => {
+        expect(() => validateEips("OnePerAz", ["abc123", "def456", "ghi789"], ["us-east-1a", "us-east-1b"]))
+            .toThrowError("must match the number");
+    });
+});
+
+describe("validateNatGatewayStrategy", () => {
+    const runTest = (
+        strategy: NatGatewayStrategyInputs,
+        inputs: SubnetTypeInputs[], expectException: boolean,
+        expectedMessage?: string,
+    ) => {
+        const specs = inputs.map(x => {
+            return {
+                type: x,
+                subnetName: "dummy",
+                cidrBlock: "dummy",
+                azName: "us-dummy-1a",
+            };
+        });
+
+        if (expectException) {
+            expect(() => validateNatGatewayStrategy(strategy, specs))
+                .toThrowError(expectedMessage);
+        } else {
+            expect(() => validateNatGatewayStrategy(strategy, specs))
+                .not.toThrowError();
+        }
+    };
+
+    describe
+        .each<NatGatewayStrategyInputs>([
+            "OnePerAz",
+            "Single",
+        ])("strategy is OnePerAz or Single", (strategy: NatGatewayStrategyInputs) => {
+            it("should succeed if there's public and private subnets",
+                () => runTest(strategy, ["Public", "Private"], false));
+
+            it("should throw an exception if there are only isolated subnets",
+                () => runTest(strategy, ["Isolated"], true, "both private and public subnets"));
+
+            it("should throw an exception if there are only private subnets",
+                () => runTest(strategy, ["Private"], true, "both private and public subnets"));
+
+            it("should throw an exception if there are only public subnets",
+                () => runTest(strategy, ["Public"], true, "both private and public subnets"));
+        });
+
+    describe("strategy is None", () => {
+        it("should throw an exception if any private subnets are specified",
+            () => runTest("None", ["Private"], true, "cannot be 'None'"));
+
+        it("should succeed if only public and isolated subnets are specified",
+            () => runTest("None", ["Public", "Isolated"], false));
+    });
+});
+
+describe("shouldCreateNatGateway", () => {
+    describe
+        .each([
+            { strategy: "OnePerAz", numGateways: 0, azIndex: 0, expected: true },
+            { strategy: "OnePerAz", numGateways: 1, azIndex: 0, expected: false },
+            { strategy: "OnePerAz", numGateways: 1, azIndex: 1, expected: true },
+            { strategy: "OnePerAz", numGateways: 2, azIndex: 1, expected: false },
+            { strategy: "Single", numGateways: 0, azIndex: 0, expected: true },
+            { strategy: "Single", numGateways: 1, azIndex: 0, expected: false },
+            { strategy: "Single", numGateways: 1, azIndex: 1, expected: false },
+            { strategy: "None", numGateways: 0, azIndex: 0, expected: false },
+        ])(
+            "based off strategy, number of NAT Gateways already created, and the current AZ index",
+            ({ strategy, numGateways, azIndex, expected }) => {
+                const result = shouldCreateNatGateway(strategy as NatGatewayStrategyInputs, numGateways, azIndex);
+                expect(result).toBe(expected);
+            });
+});
+
+describe("compareSubnetSpecs", () => {
+    describe
+        .each([
+            { input: ["Private", "Public"], expected: ["Public", "Private"] },
+            { input: ["Isolated", "Public"], expected: ["Public", "Isolated"] },
+            { input: ["Private", "Public", "Public"], expected: ["Public", "Public", "Private"] },
+            { input: ["Isolated", "Public", "Private"], expected: ["Public", "Private", "Isolated"] },
+            { input: ["Private", "Public", "Public"], expected: ["Public", "Public", "Private"] },
+        ])("should sort Public first, then Private, then Isolated", (data) => {
+            const specs = data.input.map(x => {
+                return {
+                    type: x as SubnetTypeInputs,
+                    azName: "dummy",
+                    subnetName: "dummy",
+                    cidrBlock: "dummy",
+                };
+            });
+
+            const result = specs.sort(compareSubnetSpecs).map(x => x.type);
+
+            expect(result).toEqual(data.expected.map(x => x as SubnetTypeInputs));
+        });
+});
diff --git a/awsx/vpc/vpc.test.ts b/awsx/vpc/vpc.test.ts
deleted file mode 100644
index 89288f69..00000000
--- a/awsx/vpc/vpc.test.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2016-2022, Pulumi Corporation.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// 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.
-
-import { validateNatGatewayStrategy } from "./vpc";
-
-describe("validateNatGatewayStrategy", () => {
-    it("should throw an exception if NAT Gateway strategy is None and EIPs are supplied", () => {
-        expect(() => validateNatGatewayStrategy("None", ["abc123"]))
-            .toThrowError("cannot be specified");
-    });
-
-    it("should throw an exception if NAT Gateway strategy is Single and more than 1 EIP is supplied", () => {
-        expect(() => validateNatGatewayStrategy("Single", ["abc123", "def456"]))
-            .toThrowError("Exactly one");
-    });
-
-    it("should throw an exception if NAT Gateway strategy is OnePerAz and too few EIPs are supplied", () => {
-        expect(() => validateNatGatewayStrategy("OnePerAz", ["abc123", "def456"], ["us-east-1a", "us-east-1b", "us-east-1c"]))
-            .toThrowError("must match the number");
-    });
-
-    it("should throw an exception if NAT Gateway strategy is OnePerAz and too many EIPs are supplied", () => {
-        expect(() => validateNatGatewayStrategy("OnePerAz", ["abc123", "def456", "ghi789"], ["us-east-1a", "us-east-1b"]))
-            .toThrowError("must match the number");
-    });
-});
diff --git a/examples/examples_nodejs_test.go b/examples/examples_nodejs_test.go
index d88740b7..1d0dde03 100644
--- a/examples/examples_nodejs_test.go
+++ b/examples/examples_nodejs_test.go
@@ -84,6 +84,16 @@ func TestDefaultVpc(t *testing.T) {
 	integration.ProgramTest(t, &test)
 }
 
+func TestVpcDeaultArgs(t *testing.T) {
+	test := getNodeJSBaseOptions(t).
+		With(integration.ProgramTestOptions{
+			RunUpdateTest: false,
+			Dir:           filepath.Join(getCwd(t), "vpc", "nodejs", "default-args"),
+		})
+
+	integration.ProgramTest(t, &test)
+}
+
 func getNodeJSBaseOptions(t *testing.T) integration.ProgramTestOptions {
 	base := getBaseOptions(t)
 	nodeBase := base.With(integration.ProgramTestOptions{