-
Notifications
You must be signed in to change notification settings - Fork 5
/
dns-records.ts
95 lines (83 loc) · 3.22 KB
/
dns-records.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import type { Duration } from "aws-cdk-lib";
import { CfnResource } from "aws-cdk-lib";
import { Construct } from "constructs";
import type { GuDomainName } from "../../types";
import type { AppIdentity, GuStack } from "../core";
export enum RecordType {
CNAME = "CNAME",
}
export interface GuDnsRecordSetProps {
name: string;
recordType: RecordType;
resourceRecords: string[];
ttl: Duration;
}
/**
* This construct can be used to create DNS records in NS1.
*
* Prefer to use [[`GuCname`]] when creating a CNAME for a load balancer,
* as this requires less boilerplate.
*/
export class GuDnsRecordSet extends Construct {
constructor(scope: GuStack, id: string, props: GuDnsRecordSetProps) {
const { name, recordType, resourceRecords, ttl } = props;
const { stage } = scope;
/*
Nodes in the CDK tree must have a unique ID. This class adds two nodes to the tree, so we have two IDs.
`id`, by definition, must be unique. `name` represents a fully qualified domain name, which must also be unique.
`id` being given to the level 1 construct means it also becomes the logicalId in the template.
*/
const level2ConstructId = `${name}-DnsRecordSet`;
const level1ConstructId = id;
super(scope, level2ConstructId);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- more `RecordType`s will be added soon!
if (recordType === RecordType.CNAME) {
/*
If you try to create a CNAME with multiple records within NS1, you are greeted with:
According to RFC, a CNAME record should not return multiple answers.
Doing so may cause problems during resolution.
If you want to use multiple answers, you should ensure you have the correct filters in place (such as SELECT_FIRST_N 1) to limit them to a single answer at resolution time.
`Guardian::DNS::RecordSet` does not implement "correct filters", so fail fast by throwing.
*/
if (resourceRecords.length !== 1) {
throw new Error(
"According to RFC, a CNAME record should not return multiple answers. Doing so may cause problems during resolution.",
);
}
}
// The spec for this private resource type can be found here:
// https://github.com/guardian/cfn-private-resource-types/tree/main/dns/guardian-dns-record-set-type/docs#syntax
new CfnResource(scope, level1ConstructId, {
type: "Guardian::DNS::RecordSet",
properties: {
Name: name,
ResourceRecords: resourceRecords,
RecordType: recordType,
TTL: ttl.toSeconds(),
Stage: stage,
},
});
}
}
export interface GuCnameProps extends GuDomainName, AppIdentity {
/** The record your CNAME should point to, for example your Load Balancer DNS name */
resourceRecord: string;
/** The time to live for the DNS record */
ttl: Duration;
}
/**
* Construct for creating CNAME records in NS1.
*
* See [[`GuCnameProps`]] for configuration options.
*/
export class GuCname extends GuDnsRecordSet {
constructor(scope: GuStack, id: string, props: GuCnameProps) {
const { domainName: name, resourceRecord, ttl } = props;
super(scope, id, {
name,
recordType: RecordType.CNAME,
resourceRecords: [resourceRecord],
ttl,
});
}
}