Skip to content

Commit

Permalink
adding 'disallowPrivate' flag and import of existing zones
Browse files Browse the repository at this point in the history
  • Loading branch information
IamFlowZ committed Feb 26, 2023
1 parent 94e7c31 commit 0a41420
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 10 deletions.
58 changes: 48 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,37 @@ export interface AliasTarget {
readonly ttl?: cdk.Duration;
}

/**
* @param zoneName The DNS name of the zone to create
* @param existingPublicZone An existing public zone to use instead of creating a new one
* @param existingPrivateZone An existing private zone to use instead of creating a new one
* @param disallowPrivateZone Override the default behavior of creating a private zone. Will also block adding private records.
* @param includeCertificate Whether to create an ACM certificate for the zone
* @param certAlternateNames Alternate names to include in the certificate
* @param privateZoneVpcs VPCs to associate with the private zone
* @param targets Targets to create A records for
*/
export interface ISplitHorizonDnsProps {
readonly zoneName: string;
readonly existingPublicZone?: route53.HostedZone;
readonly existingPrivateZone?: route53.HostedZone;
readonly disallowPrivateZone?: boolean;
readonly certAlternateNames?: Array<string>;
readonly privateZoneVpcs: Array<ec2.Vpc>;
readonly privateZoneVpcs?: Array<ec2.Vpc>;
readonly targets: Array<AliasTarget>;
readonly includeCertificate?: boolean;
}

/**
* Creates a public and private zone for a given domain name, and creates A records for the given targets.
* @property publicZone The public zone created
* @property privateZone The private zone created
* @property records The A records created
*/
export class SplitHorizonDns extends Construct {
public publicZone: route53.HostedZone;

public privateZone: route53.HostedZone;
public privateZone?: route53.HostedZone;

public records: Array<ARecordArray>;

Expand All @@ -37,15 +56,22 @@ export class SplitHorizonDns extends Construct {

const {
zoneName,
existingPublicZone,
existingPrivateZone,
disallowPrivateZone,
includeCertificate,
certAlternateNames,
privateZoneVpcs,
targets,
} = props;

this.publicZone = new route53.HostedZone(this, 'PublicZone', {
zoneName: zoneName,
});
if (existingPublicZone) {
this.publicZone = existingPublicZone;
} else {
this.publicZone = new route53.HostedZone(this, 'PublicZone', {
zoneName: zoneName,
});
}

if (includeCertificate) {
new acm.Certificate(this, 'Certificate', {
Expand All @@ -54,10 +80,18 @@ export class SplitHorizonDns extends Construct {
});
}

this.privateZone = new route53.HostedZone(this, 'PrivateZone', {
zoneName: zoneName,
vpcs: privateZoneVpcs,
});
if (disallowPrivateZone) {
console.log('Private zone creation is disallowed. Skipping...');
} else if (disallowPrivateZone && existingPrivateZone) {
console.error('Private zone creation is disallowed, but an existing private zone was provided. Skipping...');
} else if (existingPrivateZone) {
this.privateZone = existingPrivateZone;
} else {
this.privateZone = new route53.HostedZone(this, 'PrivateZone', {
zoneName: zoneName,
vpcs: privateZoneVpcs,
});
}

this.records = targets.reduce((accu: Array<ARecordArray>, curr: AliasTarget) => {
let target;
Expand All @@ -83,12 +117,16 @@ export class SplitHorizonDns extends Construct {
records.push(publicARecord);
}

if (curr.private) {
if (disallowPrivateZone) {
console.log('Private zone creation is disallowed. Skipping...');
} else if (curr.private && this.privateZone) {
const privateARecord = new route53.ARecord(this, `${curr.target.toString()}PrivateARecord`, {
zone: this.privateZone,
target: target,
});
records.push(privateARecord);
} else if (curr.private && !this.privateZone) {
console.error(`Private zone was specified for ${curr}, but private zone was not created. Omitting...`);
}
accu.push(records);
return accu;
Expand Down
108 changes: 108 additions & 0 deletions test/split-horizon-dns.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as cdk from 'aws-cdk-lib';
import { Duration } from 'aws-cdk-lib';
import { Match, Template } from 'aws-cdk-lib/assertions';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as targets from 'aws-cdk-lib/aws-route53-targets';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { SplitHorizonDns, AliasTarget } from '../src/index';
Expand Down Expand Up @@ -341,4 +342,111 @@ describe('split horizon', () => {
TTL: '3600',
});
});

it('omits the private zone if disallowed', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'TestStack');
new SplitHorizonDns(stack, 'MostBasicTestConstruct', {
zoneName: 'example.com',
disallowPrivateZone: true,
targets: [],
});

const template = Template.fromStack(stack);

template.hasResourceProperties('AWS::Route53::HostedZone', Match.not({
Name: Match.anyValue(),
VPCs: Match.arrayWith([
Match.objectLike({
VPCId: Match.anyValue(),
}),
]),
}));

template.hasResourceProperties('AWS::Route53::HostedZone', {
Name: Match.anyValue(),
});
});

// this test is tricky. as there is no observable property of the record
it.skip('doesnt create private records if disallowed', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'TestStack');

const firstTarget: AliasTarget = {
target: [googleDns],
private: true,
public: true,
};

new SplitHorizonDns(stack, 'MostBasicTestConstruct', {
zoneName: 'example.com',
disallowPrivateZone: true,
targets: [firstTarget],
});

// thing.records.forEach((record) => {
// console.log(record);
// });
});

it('can receive an existing public zone', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'TestStack');
const existingZone = new route53.PublicHostedZone(stack, 'ExistingPublicZone', {
zoneName: 'example.com',
});

const firstTarget: AliasTarget = {
target: [googleDns],
public: true,
};

new SplitHorizonDns(stack, 'MostBasicTestConstruct', {
zoneName: 'example.com',
existingPublicZone: existingZone,
targets: [firstTarget],
});

const template = Template.fromStack(stack);

template.hasResourceProperties('AWS::Route53::RecordSet', {
Name: Match.stringLikeRegexp(`${exampleDomain}\.`),
Type: 'A',
HostedZoneId: Match.objectLike({
Ref: Match.stringLikeRegexp('ExistingPublicZone'),
}),
});
});

it('can receive an existing private zone', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'TestStack');
const vpc = new ec2.Vpc(stack, 'myvpc');
const existingZone = new route53.PrivateHostedZone(stack, 'ExistingPrivateZone', {
zoneName: 'example.com',
vpc,
});

const firstTarget: AliasTarget = {
target: [googleDns],
private: true,
};

new SplitHorizonDns(stack, 'MostBasicTestConstruct', {
zoneName: 'example.com',
existingPrivateZone: existingZone,
targets: [firstTarget],
});

const template = Template.fromStack(stack);

template.hasResourceProperties('AWS::Route53::RecordSet', {
Name: Match.stringLikeRegexp(`${exampleDomain}\.`),
Type: 'A',
HostedZoneId: Match.objectLike({
Ref: Match.stringLikeRegexp('ExistingPrivateZone'),
}),
});
});
});

0 comments on commit 0a41420

Please sign in to comment.