Repository: cdklabs/cdk-enterprise-iac (language: python)

Issue:
feat: Add Construct to create subnets in pre-provisioned VPC
As a developer that:
- Doesn't have IAM permissions to create a VPC
- Must have a VPC created for them by another entity (e.g. cloud center of excellence, security team)
- Are provided with:
  - VPC
  - Private route table (route to internal enterprise)
  - Isolated route table  (route to local VPC)

I want a construct for creating subnets inside that VPC with appropriate tags so that it can be [imported](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#importing-an-existing-vpc) using [`fromLookup()`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html#static-fromwbrlookupscope-id-options) or [fromVpcAttributes](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html#static-fromwbrvpcwbrattributesscope-id-attrs).

So that an enterprise provided VPC created outside of the CDK project can be used as an [IVpc](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.IVpc.html)


Discussion / hints:

Fix patch:
diff --git a/API.md b/API.md
index 0f16b08..8b5df23 100644
--- a/API.md
+++ b/API.md
@@ -106,6 +106,273 @@ The tree node.
 ---
 
 
+### PopulateWithConfig <a name="PopulateWithConfig" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig"></a>
+
+Populate a provided VPC with subnets based on a provided configuration.
+
+*Example*
+
+```typescript
+const mySubnetConfig: SubnetConfig[] = [
+   {
+     groupName: 'app',
+     cidrRange: '172.31.0.0/27',
+     availabilityZone: 'a',
+     subnetType: subnetType.PUBLIC,
+   },
+   {
+     groupName: 'app',
+     cidrRange: '172.31.0.32/27',
+     availabilityZone: 'b',
+     subnetType: subnetType.PUBLIC,
+   },
+   {
+     groupName: 'db',
+     cidrRange: '172.31.0.64/27',
+     availabilityZone: 'a',
+     subnetType: subnetType.PRIVATE_WITH_EGRESS,
+   },
+   {
+     groupName: 'db',
+     cidrRange: '172.31.0.96/27',
+     availabilityZone: 'b',
+     subnetType: subnetType.PRIVATE_WITH_EGRESS,
+   },
+   {
+     groupName: 'iso',
+     cidrRange: '172.31.0.128/26',
+     availabilityZone: 'a',
+     subnetType: subnetType.PRIVATE_ISOLATED,
+   },
+   {
+     groupName: 'iso',
+     cidrRange: '172.31.0.196/26',
+     availabilityZone: 'b',
+     subnetType: subnetType.PRIVATE_ISOLATED,
+   },
+ ];
+new PopulateWithConfig(this, "vpcPopulater", {
+  vpcId: 'vpc-abcdefg1234567',
+  privateRouteTableId: 'rt-abcdefg123456',
+  localRouteTableId: 'rt-123456abcdefg',
+  subnetConfig: mySubnetConfig,
+})
+```
+
+
+#### Initializers <a name="Initializers" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer"></a>
+
+```typescript
+import { PopulateWithConfig } from '@cdklabs/cdk-enterprise-iac'
+
+new PopulateWithConfig(scope: Construct, id: string, props: PopulateWithConfigProps)
+```
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer.parameter.scope">scope</a></code> | <code>constructs.Construct</code> | *No description.* |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer.parameter.id">id</a></code> | <code>string</code> | *No description.* |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer.parameter.props">props</a></code> | <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps">PopulateWithConfigProps</a></code> | *No description.* |
+
+---
+
+##### `scope`<sup>Required</sup> <a name="scope" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer.parameter.scope"></a>
+
+- *Type:* constructs.Construct
+
+---
+
+##### `id`<sup>Required</sup> <a name="id" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer.parameter.id"></a>
+
+- *Type:* string
+
+---
+
+##### `props`<sup>Required</sup> <a name="props" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.Initializer.parameter.props"></a>
+
+- *Type:* <a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps">PopulateWithConfigProps</a>
+
+---
+
+#### Methods <a name="Methods" id="Methods"></a>
+
+| **Name** | **Description** |
+| --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfig.toString">toString</a></code> | Returns a string representation of this construct. |
+
+---
+
+##### `toString` <a name="toString" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.toString"></a>
+
+```typescript
+public toString(): string
+```
+
+Returns a string representation of this construct.
+
+#### Static Functions <a name="Static Functions" id="Static Functions"></a>
+
+| **Name** | **Description** |
+| --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfig.isConstruct">isConstruct</a></code> | Checks if `x` is a construct. |
+
+---
+
+##### ~~`isConstruct`~~ <a name="isConstruct" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.isConstruct"></a>
+
+```typescript
+import { PopulateWithConfig } from '@cdklabs/cdk-enterprise-iac'
+
+PopulateWithConfig.isConstruct(x: any)
+```
+
+Checks if `x` is a construct.
+
+###### `x`<sup>Required</sup> <a name="x" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.isConstruct.parameter.x"></a>
+
+- *Type:* any
+
+Any object.
+
+---
+
+#### Properties <a name="Properties" id="Properties"></a>
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfig.property.node">node</a></code> | <code>constructs.Node</code> | The tree node. |
+
+---
+
+##### `node`<sup>Required</sup> <a name="node" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfig.property.node"></a>
+
+```typescript
+public readonly node: Node;
+```
+
+- *Type:* constructs.Node
+
+The tree node.
+
+---
+
+
+### SplitVpcEvenly <a name="SplitVpcEvenly" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly"></a>
+
+Splits a VPC evenly between a provided number of AZs (3 if not defined), and attaches a provided route table to each, and labels.
+
+*Example*
+
+```typescript
+// with more specific properties
+new SplitVpcEvenly(this, 'evenSplitVpc', {
+  vpcId: 'vpc-abcdefg123456',
+  vpcCidr: '172.16.0.0/16',
+  routeTableId: 'rt-abcdefgh123456',
+  cidrBits: '10',
+  numberOfAzs: 4,
+  subnetType: subnetType.PRIVATE_ISOLATED,
+});
+```
+
+
+#### Initializers <a name="Initializers" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer"></a>
+
+```typescript
+import { SplitVpcEvenly } from '@cdklabs/cdk-enterprise-iac'
+
+new SplitVpcEvenly(scope: Construct, id: string, props: SplitVpcEvenlyProps)
+```
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer.parameter.scope">scope</a></code> | <code>constructs.Construct</code> | *No description.* |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer.parameter.id">id</a></code> | <code>string</code> | *No description.* |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer.parameter.props">props</a></code> | <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps">SplitVpcEvenlyProps</a></code> | *No description.* |
+
+---
+
+##### `scope`<sup>Required</sup> <a name="scope" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer.parameter.scope"></a>
+
+- *Type:* constructs.Construct
+
+---
+
+##### `id`<sup>Required</sup> <a name="id" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer.parameter.id"></a>
+
+- *Type:* string
+
+---
+
+##### `props`<sup>Required</sup> <a name="props" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.Initializer.parameter.props"></a>
+
+- *Type:* <a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps">SplitVpcEvenlyProps</a>
+
+---
+
+#### Methods <a name="Methods" id="Methods"></a>
+
+| **Name** | **Description** |
+| --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.toString">toString</a></code> | Returns a string representation of this construct. |
+
+---
+
+##### `toString` <a name="toString" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.toString"></a>
+
+```typescript
+public toString(): string
+```
+
+Returns a string representation of this construct.
+
+#### Static Functions <a name="Static Functions" id="Static Functions"></a>
+
+| **Name** | **Description** |
+| --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.isConstruct">isConstruct</a></code> | Checks if `x` is a construct. |
+
+---
+
+##### ~~`isConstruct`~~ <a name="isConstruct" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.isConstruct"></a>
+
+```typescript
+import { SplitVpcEvenly } from '@cdklabs/cdk-enterprise-iac'
+
+SplitVpcEvenly.isConstruct(x: any)
+```
+
+Checks if `x` is a construct.
+
+###### `x`<sup>Required</sup> <a name="x" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.isConstruct.parameter.x"></a>
+
+- *Type:* any
+
+Any object.
+
+---
+
+#### Properties <a name="Properties" id="Properties"></a>
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.property.node">node</a></code> | <code>constructs.Node</code> | The tree node. |
+
+---
+
+##### `node`<sup>Required</sup> <a name="node" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenly.property.node"></a>
+
+```typescript
+public readonly node: Node;
+```
+
+- *Type:* constructs.Node
+
+The tree node.
+
+---
+
+
 ## Structs <a name="Structs" id="Structs"></a>
 
 ### AddPermissionBoundaryProps <a name="AddPermissionBoundaryProps" id="@cdklabs/cdk-enterprise-iac.AddPermissionBoundaryProps"></a>
@@ -336,6 +603,77 @@ The number of tasks that will scale out on scale out alarm status.
 
 ---
 
+### PopulateWithConfigProps <a name="PopulateWithConfigProps" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps"></a>
+
+#### Initializer <a name="Initializer" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.Initializer"></a>
+
+```typescript
+import { PopulateWithConfigProps } from '@cdklabs/cdk-enterprise-iac'
+
+const populateWithConfigProps: PopulateWithConfigProps = { ... }
+```
+
+#### Properties <a name="Properties" id="Properties"></a>
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.localRouteTableId">localRouteTableId</a></code> | <code>string</code> | Local route table ID, with routes only to local VPC. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.privateRouteTableId">privateRouteTableId</a></code> | <code>string</code> | Route table ID for a provided route table with routes to enterprise network. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.subnetConfig">subnetConfig</a></code> | <code><a href="#@cdklabs/cdk-enterprise-iac.SubnetConfig">SubnetConfig</a>[]</code> | List of Subnet configs to provision to provision. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.vpcId">vpcId</a></code> | <code>string</code> | ID of the VPC provided that needs to be populated. |
+
+---
+
+##### `localRouteTableId`<sup>Required</sup> <a name="localRouteTableId" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.localRouteTableId"></a>
+
+```typescript
+public readonly localRouteTableId: string;
+```
+
+- *Type:* string
+
+Local route table ID, with routes only to local VPC.
+
+---
+
+##### `privateRouteTableId`<sup>Required</sup> <a name="privateRouteTableId" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.privateRouteTableId"></a>
+
+```typescript
+public readonly privateRouteTableId: string;
+```
+
+- *Type:* string
+
+Route table ID for a provided route table with routes to enterprise network.
+
+Both subnetType.PUBLIC and subnetType.PRIVATE_WITH_EGRESS will use this property
+
+---
+
+##### `subnetConfig`<sup>Required</sup> <a name="subnetConfig" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.subnetConfig"></a>
+
+```typescript
+public readonly subnetConfig: SubnetConfig[];
+```
+
+- *Type:* <a href="#@cdklabs/cdk-enterprise-iac.SubnetConfig">SubnetConfig</a>[]
+
+List of Subnet configs to provision to provision.
+
+---
+
+##### `vpcId`<sup>Required</sup> <a name="vpcId" id="@cdklabs/cdk-enterprise-iac.PopulateWithConfigProps.property.vpcId"></a>
+
+```typescript
+public readonly vpcId: string;
+```
+
+- *Type:* string
+
+ID of the VPC provided that needs to be populated.
+
+---
+
 ### RemoveTagsProps <a name="RemoveTagsProps" id="@cdklabs/cdk-enterprise-iac.RemoveTagsProps"></a>
 
 #### Initializer <a name="Initializer" id="@cdklabs/cdk-enterprise-iac.RemoveTagsProps.Initializer"></a>
@@ -413,6 +751,186 @@ Defaults to EndpointType.REGIONAL
 
 ---
 
+### SplitVpcEvenlyProps <a name="SplitVpcEvenlyProps" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps"></a>
+
+#### Initializer <a name="Initializer" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.Initializer"></a>
+
+```typescript
+import { SplitVpcEvenlyProps } from '@cdklabs/cdk-enterprise-iac'
+
+const splitVpcEvenlyProps: SplitVpcEvenlyProps = { ... }
+```
+
+#### Properties <a name="Properties" id="Properties"></a>
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.routeTableId">routeTableId</a></code> | <code>string</code> | Route Table ID that will be attached to each subnet created. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.vpcCidr">vpcCidr</a></code> | <code>string</code> | CIDR range of the VPC you're populating. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.vpcId">vpcId</a></code> | <code>string</code> | ID of the existing VPC you're trying to populate. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.cidrBits">cidrBits</a></code> | <code>string</code> | `cidrBits` argument for the [`Fn::Cidr`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html) Cloudformation intrinsic function. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.numberOfAzs">numberOfAzs</a></code> | <code>number</code> | Number of AZs to evenly split into. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.subnetType">subnetType</a></code> | <code>aws-cdk-lib.aws_ec2.SubnetType</code> | *No description.* |
+
+---
+
+##### `routeTableId`<sup>Required</sup> <a name="routeTableId" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.routeTableId"></a>
+
+```typescript
+public readonly routeTableId: string;
+```
+
+- *Type:* string
+
+Route Table ID that will be attached to each subnet created.
+
+---
+
+##### `vpcCidr`<sup>Required</sup> <a name="vpcCidr" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.vpcCidr"></a>
+
+```typescript
+public readonly vpcCidr: string;
+```
+
+- *Type:* string
+
+CIDR range of the VPC you're populating.
+
+---
+
+##### `vpcId`<sup>Required</sup> <a name="vpcId" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.vpcId"></a>
+
+```typescript
+public readonly vpcId: string;
+```
+
+- *Type:* string
+
+ID of the existing VPC you're trying to populate.
+
+---
+
+##### `cidrBits`<sup>Optional</sup> <a name="cidrBits" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.cidrBits"></a>
+
+```typescript
+public readonly cidrBits: string;
+```
+
+- *Type:* string
+- *Default:* '6'
+
+`cidrBits` argument for the [`Fn::Cidr`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html) Cloudformation intrinsic function.
+
+---
+
+##### `numberOfAzs`<sup>Optional</sup> <a name="numberOfAzs" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.numberOfAzs"></a>
+
+```typescript
+public readonly numberOfAzs: number;
+```
+
+- *Type:* number
+- *Default:* 3
+
+Number of AZs to evenly split into.
+
+---
+
+##### `subnetType`<sup>Optional</sup> <a name="subnetType" id="@cdklabs/cdk-enterprise-iac.SplitVpcEvenlyProps.property.subnetType"></a>
+
+```typescript
+public readonly subnetType: SubnetType;
+```
+
+- *Type:* aws-cdk-lib.aws_ec2.SubnetType
+- *Default:* subnetType.PRIVATE
+
+---
+
+### SubnetConfig <a name="SubnetConfig" id="@cdklabs/cdk-enterprise-iac.SubnetConfig"></a>
+
+#### Initializer <a name="Initializer" id="@cdklabs/cdk-enterprise-iac.SubnetConfig.Initializer"></a>
+
+```typescript
+import { SubnetConfig } from '@cdklabs/cdk-enterprise-iac'
+
+const subnetConfig: SubnetConfig = { ... }
+```
+
+#### Properties <a name="Properties" id="Properties"></a>
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SubnetConfig.property.availabilityZone">availabilityZone</a></code> | <code>string</code> | Which availability zone the subnet should be in. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SubnetConfig.property.cidrRange">cidrRange</a></code> | <code>string</code> | Cidr range of the subnet to create. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SubnetConfig.property.groupName">groupName</a></code> | <code>string</code> | Logical group name of a subnet. |
+| <code><a href="#@cdklabs/cdk-enterprise-iac.SubnetConfig.property.subnetType">subnetType</a></code> | <code>aws-cdk-lib.aws_ec2.SubnetType</code> | What [SubnetType](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.SubnetType.html) to use. |
+
+---
+
+##### `availabilityZone`<sup>Required</sup> <a name="availabilityZone" id="@cdklabs/cdk-enterprise-iac.SubnetConfig.property.availabilityZone"></a>
+
+```typescript
+public readonly availabilityZone: string;
+```
+
+- *Type:* string
+
+Which availability zone the subnet should be in.
+
+---
+
+##### `cidrRange`<sup>Required</sup> <a name="cidrRange" id="@cdklabs/cdk-enterprise-iac.SubnetConfig.property.cidrRange"></a>
+
+```typescript
+public readonly cidrRange: string;
+```
+
+- *Type:* string
+
+Cidr range of the subnet to create.
+
+---
+
+##### `groupName`<sup>Required</sup> <a name="groupName" id="@cdklabs/cdk-enterprise-iac.SubnetConfig.property.groupName"></a>
+
+```typescript
+public readonly groupName: string;
+```
+
+- *Type:* string
+
+Logical group name of a subnet.
+
+---
+
+*Example*
+
+```typescript
+db
+```
+
+
+##### `subnetType`<sup>Required</sup> <a name="subnetType" id="@cdklabs/cdk-enterprise-iac.SubnetConfig.property.subnetType"></a>
+
+```typescript
+public readonly subnetType: SubnetType;
+```
+
+- *Type:* aws-cdk-lib.aws_ec2.SubnetType
+
+What [SubnetType](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.SubnetType.html) to use.
+
+This will govern the `aws-cdk:subnet-type` tag on the subnet
+
+SubnetType | `aws-cdk:subnet-type` tag value
+--- | ---
+`PRIVATE_ISOLATED` | 'Isolated'
+`PRIVATE_WITH_EGRESS` | 'Private'
+`PUBLIC` | 'Public'
+
+---
+
 ## Classes <a name="Classes" id="Classes"></a>
 
 ### AddLambdaEnvironmentVariables <a name="AddLambdaEnvironmentVariables" id="@cdklabs/cdk-enterprise-iac.AddLambdaEnvironmentVariables"></a>
diff --git a/src/constructs/populateProvidedVpc/populateProvidedVpc.ts b/src/constructs/populateProvidedVpc/populateProvidedVpc.ts
new file mode 100644
index 0000000..c41d8c4
--- /dev/null
+++ b/src/constructs/populateProvidedVpc/populateProvidedVpc.ts
@@ -0,0 +1,261 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+import { Fn, Stack, Tags } from 'aws-cdk-lib';
+import {
+  CfnSubnet,
+  CfnSubnetRouteTableAssociation,
+  SubnetType,
+} from 'aws-cdk-lib/aws-ec2';
+import { Construct } from 'constructs';
+
+export interface SubnetConfig {
+  /**
+   * Logical group name of a subnet
+   * @example app
+   * @example db
+   */
+  readonly groupName: string;
+  /**
+   * Which availability zone the subnet should be in
+   *
+   */
+  readonly availabilityZone: string;
+  /**
+   * Cidr range of the subnet to create
+   */
+  readonly cidrRange: string;
+  /**
+   * What [SubnetType](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.SubnetType.html) to use
+   *
+   * This will govern the `aws-cdk:subnet-type` tag on the subnet
+   *
+   * SubnetType | `aws-cdk:subnet-type` tag value
+   * --- | ---
+   * `PRIVATE_ISOLATED` | 'Isolated'
+   * `PRIVATE_WITH_EGRESS` | 'Private'
+   * `PUBLIC` | 'Public'
+   */
+  readonly subnetType: SubnetType;
+}
+
+export interface PopulateWithConfigProps {
+  /**
+   * ID of the VPC provided that needs to be populated
+   */
+  readonly vpcId: string;
+  /**
+   * Route table ID for a provided route table with routes to enterprise network
+   *
+   * Both subnetType.PUBLIC and subnetType.PRIVATE_WITH_EGRESS will use this property
+   */
+  readonly privateRouteTableId: string;
+  /**
+   * Local route table ID, with routes only to local VPC
+   */
+  readonly localRouteTableId: string;
+  /**
+   * List of Subnet configs to provision to provision
+   */
+  readonly subnetConfig: SubnetConfig[];
+}
+
+/**
+ * Populate a provided VPC with subnets based on a provided configuration
+ *
+ * @example
+ * const mySubnetConfig: SubnetConfig[] = [
+      {
+        groupName: 'app',
+        cidrRange: '172.31.0.0/27',
+        availabilityZone: 'a',
+        subnetType: subnetType.PUBLIC,
+      },
+      {
+        groupName: 'app',
+        cidrRange: '172.31.0.32/27',
+        availabilityZone: 'b',
+        subnetType: subnetType.PUBLIC,
+      },
+      {
+        groupName: 'db',
+        cidrRange: '172.31.0.64/27',
+        availabilityZone: 'a',
+        subnetType: subnetType.PRIVATE_WITH_EGRESS,
+      },
+      {
+        groupName: 'db',
+        cidrRange: '172.31.0.96/27',
+        availabilityZone: 'b',
+        subnetType: subnetType.PRIVATE_WITH_EGRESS,
+      },
+      {
+        groupName: 'iso',
+        cidrRange: '172.31.0.128/26',
+        availabilityZone: 'a',
+        subnetType: subnetType.PRIVATE_ISOLATED,
+      },
+      {
+        groupName: 'iso',
+        cidrRange: '172.31.0.196/26',
+        availabilityZone: 'b',
+        subnetType: subnetType.PRIVATE_ISOLATED,
+      },
+    ];
+ * new PopulateWithConfig(this, "vpcPopulater", {
+ *   vpcId: 'vpc-abcdefg1234567',
+ *   privateRouteTableId: 'rt-abcdefg123456',
+ *   localRouteTableId: 'rt-123456abcdefg',
+ *   subnetConfig: mySubnetConfig,
+ * })
+ */
+export class PopulateWithConfig extends Construct {
+  private readonly _vpcId: string;
+  private readonly _privateRouteTableId: string;
+  private readonly _localRouteTableId: string;
+  private readonly _subnetConfig: SubnetConfig[];
+
+  constructor(scope: Construct, id: string, props: PopulateWithConfigProps) {
+    super(scope, id);
+
+    this._vpcId = props.vpcId;
+    this._privateRouteTableId = props.privateRouteTableId;
+    this._localRouteTableId = props.localRouteTableId;
+    this._subnetConfig = props.subnetConfig;
+
+    for (const key in this._subnetConfig) {
+      if (Object.prototype.hasOwnProperty.call(this._subnetConfig, key)) {
+        const subnet = this._subnetConfig[key];
+
+        const sub = new CfnSubnet(
+          this,
+          `Subnet${subnet.groupName}-${subnet.availabilityZone}`,
+          {
+            availabilityZone: `${Stack.of(this).region}${
+              subnet.availabilityZone
+            }`,
+            vpcId: this._vpcId,
+            cidrBlock: subnet.cidrRange,
+          }
+        );
+        Tags.of(sub).add('aws-cdk:subnet-type', subnet.subnetType);
+        Tags.of(sub).add(
+          'Name',
+          `subnet-${subnet.groupName}-${subnet.availabilityZone}`
+        );
+
+        new CfnSubnetRouteTableAssociation(
+          this,
+          `SubnetReAssoc${subnet.groupName}-${subnet.availabilityZone}`,
+          {
+            routeTableId:
+              subnet.subnetType == 'Isolated'
+                ? this._privateRouteTableId
+                : this._localRouteTableId,
+            subnetId: sub.ref,
+          }
+        );
+      }
+    }
+  }
+}
+
+export interface SplitVpcEvenlyProps {
+  /**
+   * ID of the existing VPC you're trying to populate
+   */
+  readonly vpcId: string;
+  /**
+   * CIDR range of the VPC you're populating
+   */
+  readonly vpcCidr: string;
+  /**
+   * Route Table ID that will be attached to each subnet created
+   */
+  readonly routeTableId: string;
+  /**
+   * `cidrBits` argument for the [`Fn::Cidr`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html) Cloudformation intrinsic function
+   *
+   * @default '6'
+   */
+  readonly cidrBits?: string;
+  /**
+   * Number of AZs to evenly split into
+   *
+   * @default 3
+   */
+  readonly numberOfAzs?: number;
+  /**
+   * @default subnetType.PRIVATE
+   */
+  readonly subnetType?: SubnetType;
+}
+/**
+ * Splits a VPC evenly between a provided number of AZs (3 if not defined), and attaches a provided route table to each, and labels
+ *
+ * @example
+ * new SplitVpcEvenly(this, 'evenSplitVpc', {
+ *   vpcId: 'vpc-abcdefg123456',
+ *   vpcCidr: '172.16.0.0/24',
+ *   routeTableId: 'rt-abcdefgh123456',
+ * });
+ * @example
+ * // with more specific properties
+ * new SplitVpcEvenly(this, 'evenSplitVpc', {
+ *   vpcId: 'vpc-abcdefg123456',
+ *   vpcCidr: '172.16.0.0/16',
+ *   routeTableId: 'rt-abcdefgh123456',
+ *   cidrBits: '10',
+ *   numberOfAzs: 4,
+ *   subnetType: subnetType.PRIVATE_ISOLATED,
+ * });
+ */
+export class SplitVpcEvenly extends Construct {
+  private readonly _vpcId: string;
+  private readonly _vpcCidr: string;
+  private readonly _routeTableId: string;
+  private readonly _cidrBits?: string;
+  private readonly _numberOfAzs?: number;
+  private readonly _subnetType?: SubnetType;
+
+  constructor(scope: Construct, id: string, props: SplitVpcEvenlyProps) {
+    super(scope, id);
+
+    this._vpcId = props.vpcId;
+    this._vpcCidr = props.vpcCidr;
+    this._routeTableId = props.routeTableId;
+    this._cidrBits = props.cidrBits || '6';
+    this._numberOfAzs = props.numberOfAzs || 3;
+    this._subnetType = props.subnetType || SubnetType.PRIVATE_WITH_EGRESS;
+
+    // Throw error if > 6 AZs
+    if (this._numberOfAzs < 2 || this._numberOfAzs > 6) {
+      throw new Error('numberOfAzs must be between 2 and 6');
+    }
+
+    // based on number of az, create an array of az strings
+    const azs: string[] = [];
+    for (let i = 0; i < this._numberOfAzs; i++) {
+      azs.push(String.fromCharCode(97 + i));
+    }
+
+    azs.forEach((val, i) => {
+      const sub = new CfnSubnet(this, `TgwSubnet${val}`, {
+        availabilityZone: Fn.select(i, Fn.getAzs()),
+        vpcId: this._vpcId,
+        cidrBlock: Fn.select(
+          i,
+          Fn.cidr(this._vpcCidr, this._numberOfAzs!, this._cidrBits)
+        ),
+      });
+      Tags.of(sub).add('aws-cdk:subnet-type', this._subnetType!);
+      Tags.of(sub).add('Name', `subnet-${val}`);
+
+      new CfnSubnetRouteTableAssociation(this, `SubnetRtAssoc${val}`, {
+        routeTableId: this._routeTableId,
+        subnetId: sub.ref,
+      });
+    });
+  }
+}
diff --git a/src/index.ts b/src/index.ts
index a584251..76334fa 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,4 +7,5 @@ export * from './patches/removeTags';
 export * from './patches/addLambdaEnvironmentVariables';
 export * from './patches/removePublicAccessBlockConfiguration';
 export * from './patches/setApiGatewayEndpointConfiguration';
+export * from './constructs/populateProvidedVpc/populateProvidedVpc';
 export * from './constructs/ecsIsoServiceAutoscaler/ecsIsoServiceAutoscaler';


Test patch:
diff --git a/test/constructs/populateProvidedVpc/populateProvidedVpc.test.ts b/test/constructs/populateProvidedVpc/populateProvidedVpc.test.ts
new file mode 100644
index 0000000..009ac7a
--- /dev/null
+++ b/test/constructs/populateProvidedVpc/populateProvidedVpc.test.ts
@@ -0,0 +1,207 @@
+/*
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: Apache-2.0
+*/
+import { Stack } from 'aws-cdk-lib';
+import { Match, Template } from 'aws-cdk-lib/assertions';
+import { SubnetType } from 'aws-cdk-lib/aws-ec2';
+import {
+  PopulateWithConfig,
+  SplitVpcEvenly,
+  SubnetConfig,
+} from '../../../src/constructs/populateProvidedVpc/populateProvidedVpc';
+
+let stack: Stack;
+const privateRouteTableId = 'tgw-123456abcdefg';
+const localRouteTableId = 'rt-abcdefg123456';
+const vpcId = 'vpc-123456abcdefghijklmnop';
+
+beforeEach(() => {
+  stack = new Stack();
+});
+
+describe('CDKify a provided vpc', () => {
+  test('provided manual subnet configuration applied', () => {
+    const subnetConfig: SubnetConfig[] = [
+      {
+        groupName: 'app',
+        cidrRange: '172.16.0.0/27',
+        availabilityZone: 'a',
+        subnetType: SubnetType.PUBLIC,
+      },
+      {
+        groupName: 'app',
+        cidrRange: '172.16.0.32/27',
+        availabilityZone: 'b',
+        subnetType: SubnetType.PUBLIC,
+      },
+      {
+        groupName: 'db',
+        cidrRange: '172.16.0.64/27',
+        availabilityZone: 'a',
+        subnetType: SubnetType.PRIVATE_WITH_EGRESS,
+      },
+      {
+        groupName: 'db',
+        cidrRange: '172.16.0.96/27',
+        availabilityZone: 'b',
+        subnetType: SubnetType.PRIVATE_WITH_EGRESS,
+      },
+      {
+        groupName: 'iso',
+        cidrRange: '172.16.0.128/26',
+        availabilityZone: 'a',
+        subnetType: SubnetType.PRIVATE_ISOLATED,
+      },
+      {
+        groupName: 'iso',
+        cidrRange: '172.16.0.196/26',
+        availabilityZone: 'b',
+        subnetType: SubnetType.PRIVATE_ISOLATED,
+      },
+    ];
+    new PopulateWithConfig(stack, 'CDKifyVpc', {
+      vpcId,
+      privateRouteTableId,
+      localRouteTableId,
+      subnetConfig,
+    });
+    const template = Template.fromStack(stack);
+    template.resourceCountIs('AWS::EC2::Subnet', 6);
+
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        Tags: Match.arrayWith([
+          { Key: 'aws-cdk:subnet-type', Value: 'Public' },
+        ]),
+      },
+      2
+    );
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        Tags: Match.arrayWith([
+          { Key: 'aws-cdk:subnet-type', Value: 'Private' },
+        ]),
+      },
+      2
+    );
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        Tags: Match.arrayWith([
+          { Key: 'aws-cdk:subnet-type', Value: 'Isolated' },
+        ]),
+      },
+      2
+    );
+  });
+  test('split vpc evenly with defaults', () => {
+    new SplitVpcEvenly(stack, 'CDKifyVpc', {
+      vpcId,
+      routeTableId: privateRouteTableId,
+      vpcCidr: '172.16.0.0/24',
+    });
+    const template = Template.fromStack(stack);
+
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        Tags: Match.arrayWith([
+          { Key: 'aws-cdk:subnet-type', Value: 'Private' },
+        ]),
+      },
+      3
+    );
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::SubnetRouteTableAssociation',
+      {
+        RouteTableId: privateRouteTableId,
+      },
+      3
+    );
+  });
+  test('split vpc evenly larger subnet and set cidrBits', () => {
+    new SplitVpcEvenly(stack, 'CDKifyVpc', {
+      vpcId,
+      routeTableId: privateRouteTableId,
+      vpcCidr: '172.16.0.0/16',
+      cidrBits: '10',
+    });
+    const template = Template.fromStack(stack);
+
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        CidrBlock: {
+          'Fn::Select': Match.arrayWith([
+            { 'Fn::Cidr': ['172.16.0.0/16', 3, '10'] },
+          ]),
+        },
+      },
+      3
+    );
+  });
+
+  test('split vpc evenly reduced AZs', () => {
+    new SplitVpcEvenly(stack, 'CDKifyVpc', {
+      vpcId,
+      routeTableId: privateRouteTableId,
+      vpcCidr: '172.16.0.0/16',
+      cidrBits: '10',
+      numberOfAzs: 2,
+    });
+    const template = Template.fromStack(stack);
+
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        CidrBlock: {
+          'Fn::Select': Match.arrayWith([
+            { 'Fn::Cidr': ['172.16.0.0/16', 2, '10'] },
+          ]),
+        },
+      },
+      2
+    );
+  });
+  test('split vpc evenly and set subnet tag', () => {
+    new SplitVpcEvenly(stack, 'CDKifyVpc', {
+      vpcId,
+      routeTableId: privateRouteTableId,
+      vpcCidr: '172.16.0.0/24',
+      subnetType: SubnetType.PRIVATE_ISOLATED,
+    });
+    const template = Template.fromStack(stack);
+
+    template.resourcePropertiesCountIs(
+      'AWS::EC2::Subnet',
+      {
+        Tags: Match.arrayWith([
+          { Key: 'aws-cdk:subnet-type', Value: 'Isolated' },
+        ]),
+      },
+      3
+    );
+  });
+  test('error is thrown if invalid number of AZs is provided', () => {
+    expect(() => {
+      new SplitVpcEvenly(stack, 'CDKifyVpc7Azs', {
+        vpcId,
+        routeTableId: privateRouteTableId,
+        vpcCidr: '172.16.0.0/24',
+        numberOfAzs: 7,
+      });
+    }).toThrowError('numberOfAzs must be between 2 and 6');
+
+    expect(() => {
+      new SplitVpcEvenly(stack, 'CDKifyVpc1Az', {
+        vpcId,
+        routeTableId: privateRouteTableId,
+        vpcCidr: '172.16.0.0/24',
+        numberOfAzs: 1,
+      });
+    }).toThrowError('numberOfAzs must be between 2 and 6');
+  });
+});