Skip to content

Instantly share code, notes, and snippets.

@SlootSantos
Created May 22, 2022 20:54
Show Gist options
  • Save SlootSantos/61e44d7b869df94bae53d1a6697477ff to your computer and use it in GitHub Desktop.
Save SlootSantos/61e44d7b869df94bae53d1a6697477ff to your computer and use it in GitHub Desktop.
import {
AwsCustomResource,
AwsCustomResourcePolicy,
PhysicalResourceId,
} from "aws-cdk-lib/custom-resources";
import { Construct } from "constructs";
import { CfnOutput, Fn } from "aws-cdk-lib";
import { IHostedZone } from "aws-cdk-lib/aws-route53";
import { domains } from "../../constants/domains";
import { accounts } from "../../constants/accounts";
import { NS_CREATION_ROLE_NAME } from "../../constants/roles";
export const buildDelegatedNameServerRecord = (
scope: Construct,
subdomain: string,
hostedZone: IHostedZone
) => {
const rootHostedZoneId = listHostedZone(scope, subdomain);
const { nsRecordsForHostedZone, nameServers } = getNameServerList(hostedZone);
createNameServerRecords(
scope,
hostedZone,
rootHostedZoneId,
nsRecordsForHostedZone
);
return nameServers;
};
// with this custom resource we can list the hosted zones from the root-domain-account
const listHostedZone = (scope: Construct, subdomain: string) => {
const getRootHostedZoneSDK = {
service: "Route53",
action: "listHostedZonesByName",
physicalResourceId: PhysicalResourceId.fromResponse("HostedZones.0.Id"),
assumedRoleArn: `arn:aws:iam::${accounts.rootDomain}:role/${NS_CREATION_ROLE_NAME}`,
parameters: {
DNSName: `${subdomain}.${domains.root}`,
},
region: "eu-central-1",
};
const rootHostedZone = new AwsCustomResource(scope, "list-root-zones", {
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
}),
onCreate: getRootHostedZoneSDK,
});
const rootHostedZoneId = rootHostedZone.getResponseField("HostedZones.0.Id");
return rootHostedZoneId;
};
// we need to build an array of name servers for the hosted zone
// the list we get from the reference is not a JS array, hence cannot access elements via map or similar
const getNameServerList = (hostedZone: IHostedZone) => {
const nsRecordsForHostedZone = [];
const nameServers = [];
// we can make a fixed assumption here, as Route53 zones always have 4 name servers
for (let idx = 0; idx < 4; idx++) {
const fullyQualifiedNameServer =
Fn.select(idx, hostedZone.hostedZoneNameServers!) + ".";
const changeRecordSet = {
// we have to resolve the nameserver token at each index
Value: fullyQualifiedNameServer,
};
nameServers.push(fullyQualifiedNameServer);
nsRecordsForHostedZone.push(changeRecordSet);
}
return { nsRecordsForHostedZone, nameServers };
};
// with this custom resource we can write the NS records to the hosted zone in the root-domain-account
const createNameServerRecords = (
scope: Construct,
hostedZone: IHostedZone,
rootHostedZoneId: string,
nsRecordsForHostedZone: { Value: string }[]
) => {
const changeSet = {
Type: "NS",
Name: hostedZone.zoneName,
TTL: 60 * 5, // 5 minutes
ResourceRecords: nsRecordsForHostedZone,
};
const baseParameters = {
region: "eu-central-1",
service: "Route53",
action: "changeResourceRecordSets",
physicalResourceId: PhysicalResourceId.of("zone-" + hostedZone.zoneName),
assumedRoleArn: `arn:aws:iam::${accounts.rootDomain}:role/${NS_CREATION_ROLE_NAME}`,
};
const getChangeBatch = (action: "UPSERT" | "DELETE") => ({
HostedZoneId: rootHostedZoneId,
ChangeBatch: {
Changes: [
{
Action: action,
ResourceRecordSet: changeSet,
},
],
},
});
new AwsCustomResource(scope, "custom-resource-create-ns-records", {
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
}),
onCreate: {
...baseParameters,
parameters: getChangeBatch("UPSERT"),
},
onUpdate: {
...baseParameters,
parameters: getChangeBatch("UPSERT"),
},
onDelete: {
...baseParameters,
parameters: getChangeBatch("DELETE"),
},
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment