Skip to content

Commit

Permalink
Merge pull request #879 from aligent/feature/shared-vpc-construct
Browse files Browse the repository at this point in the history
Add Shared VPC construct
  • Loading branch information
toddhainsworth authored Jan 12, 2023
2 parents aab93ce + 09491ae commit a9ad273
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 5 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ Construct | Description
[geoip-redirect](packages/geoip-redirect) | :warning: Uses CDK 2
[prerender-proxy](packages/prerender-proxy) |
[rabbitmq](packages/rabbitmq) |
[shared-vpc](packages/shared-vpc) |
[static-hosting](packages/static-hosting) |
[waf](packages/waf) |

## Making a Release

Each construct/package maintains an independent release cycle.
Once the changes have been approved, you can commit/merge the changes to the main branch and create a release.
**Note that for all the finalized releases, the source branch should be the main branch.**
Each construct/package maintains an independent release cycle.
Once the changes have been approved, you can commit/merge the changes to the main branch and create a release.
**Note that for all the finalized releases, the source branch should be the main branch.**

If the release is experimental, you may use the `main` or the feature branch.
If the release is experimental, you may use the `main` or the feature branch.

### Release Tags

When making a release (including experimental releases), the release tag should maintain following formation.
When making a release (including experimental releases), the release tag should maintain following formation.

- experimental releases: [package-name]-[version number]-[experimental tag]
- finalized releases: [package-name]-[version number]
Expand Down
41 changes: 41 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions packages/shared-vpc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
6 changes: 6 additions & 0 deletions packages/shared-vpc/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
36 changes: 36 additions & 0 deletions packages/shared-vpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# AligentShared VPC

## Overview

This repository creates a stack that provides a stable elastic IP for micro-services. Micro-services are then deployed into this VPC rather than creating their own. A hosted zone is also created to allow for private DNS configuration.

## Example

The following can be used to provision a shared VPC.

```
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { Construct, Stack, StackProps } from '@aws-cdk/core';
import { SharedVpc, SharedVpcProps, Zone } from '@aligent/cdk-shared-vpc';
const hostedZones: Zone[] = [
{ type: "A", target: "10.6.0.12", record: "subdomain" }
]
const sharedVpcProps : SharedVpcProps = {
vpcName: 'my-vpc-name',
cidr: '10.0.0.0/16',
hostedZoneDomain: 'example.com',
hostedZoneRecords: hostedZones
};
class MyStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new SharedVpc(this, 'my-shared-vpc', sharedVpcProps);
}
}
const app = new cdk.App();
new MyStack(app, 'my-stack');
```
3 changes: 3 additions & 0 deletions packages/shared-vpc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { SharedVpc, SharedVpcProps } from "./lib/shared-vpc";

export { SharedVpc, SharedVpcProps };
122 changes: 122 additions & 0 deletions packages/shared-vpc/lib/shared-vpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Construct, CfnOutput } from "@aws-cdk/core";
import { SubnetType, Vpc } from '@aws-cdk/aws-ec2';
import { ARecord, CnameRecord, PrivateHostedZone, RecordTarget } from '@aws-cdk/aws-route53';

const DEFAULT_ZONE_RECORD_SUFFIX = 'root';

export type Zone = {
type: string,
target: string,
record?: string
}

export interface SharedVpcProps {
/**
* The name we should use to create the VPC and prefix it's resources with
*/
vpcName: string
/**
* The optional CIDR address
*/
cidr?: string,
/**
* The optional domain to use for the hosted-zone
*/
hostedZoneDomain?: string,
/**
* Optional zone records
*/
hostedZoneRecords?: Zone[],
}

export class SharedVpc extends Construct {
public readonly vpc: Vpc;
public readonly privateHostedZone: PrivateHostedZone;

constructor(scope: Construct, id: string, props: SharedVpcProps) {
super(scope, id);
const { vpcName, cidr, hostedZoneDomain, hostedZoneRecords } = props;

this.vpc = new Vpc(this, vpcName, {
maxAzs: 2,
cidr: cidr || Vpc.DEFAULT_CIDR_RANGE,
enableDnsHostnames: true,
enableDnsSupport: true,
natGateways: 1,
subnetConfiguration: [
{
cidrMask: 22,
name: `${vpcName}-subnet-private`,
subnetType: SubnetType.PRIVATE_WITH_NAT
},
{
cidrMask: 22,
name: `${vpcName}-subnet-public`,
subnetType: SubnetType.PUBLIC
}
]
});

// Export the VPC ID
new CfnOutput(this, 'vpc', {
value: this.vpc.vpcId,
exportName: `${vpcName}-vpc`
});

// Export each subnet from this VPC
this.vpc.privateSubnets.forEach((subnet, index) => {
const id = index + 1;
new CfnOutput(this, `private-subnet-${id}`, {
value: subnet.subnetId,
exportName: `${vpcName}-private-subnet-${id}`
});
});
this.vpc.publicSubnets.forEach((subnet, index) => {
const id = index + 1;
new CfnOutput(this, `public-subnet-${id}`, {
value: subnet.subnetId,
exportName: `${vpcName}-public-subnet-${id}`
});
});

// Generate DNS records for each hosted zone
if (hostedZoneDomain) {
this.privateHostedZone = new PrivateHostedZone(this, `${vpcName}-hosted-zone`, {
zoneName: hostedZoneDomain,
vpc: this.vpc
});

if (hostedZoneRecords?.length) {
for (const zone of hostedZoneRecords) {
const recordId = `${vpcName}-hosted-zone-record-${zone.record || DEFAULT_ZONE_RECORD_SUFFIX}`;
switch (zone.type) {
case 'A': {
new ARecord(this, recordId, {
zone: this.privateHostedZone,
target: RecordTarget.fromIpAddresses(zone.target)
});
break;
}
case 'CNAME': {
new CnameRecord(this, recordId, {
zone: this.privateHostedZone,
domainName: zone.target,
recordName: zone.record
});
break;
}
default: {
throw Error(`${zone.type} is not supported`);
}
}
}
}

// Export the hosted zone
new CfnOutput(this, 'hosted-zone', {
value: this.privateHostedZone.hostedZoneId,
exportName: `${vpcName}-hosted-zone`
});
}
}
}
25 changes: 25 additions & 0 deletions packages/shared-vpc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@aligent/cdk-shared-vpc",
"version": "0.1.0",
"main": "index.js",
"scripts": {
"build": "tsc",
"prepublish": "tsc"
},
"devDependencies": {
"@types/jest": "^26.0.21",
"@types/node": "^10.17.60",
"jest": "^26.4.2",
"ts-jest": "^26.5.4",
"ts-node": "^10.0.0",
"typescript": "^4.3.5"
},
"dependencies": {
"@aws-cdk/aws-route53": "1.180.0",
"@aws-cdk/aws-ec2": "1.180.0",
"@aws-cdk/core": "1.180.0",
"aws-cdk": "1.180.0",
"esbuild": "0.12.15",
"source-map-support": "^0.5.16"
}
}
3 changes: 3 additions & 0 deletions packages/shared-vpc/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}

0 comments on commit a9ad273

Please sign in to comment.