All files / lib dns.ts

96.3% Statements 26/27
70% Branches 7/10
100% Functions 5/5
96.3% Lines 26/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1232x 2x 2x 2x                                                 2x       1x 1x   1x 3x 3x       3x 3x 3x                         1x       1x                   3x 3x 3x 3x             3x 3x                 3x         3x                   3x               3x       1x          
import * as cdk from "@aws-cdk/core";
import * as iam from "@aws-cdk/aws-iam";
import * as route53 from "@aws-cdk/aws-route53";
import { RecordTarget } from "@aws-cdk/aws-route53";
import {Account} from './account';
 
/**
 * Properties for RootDns
 */
export interface RootDnsProps {
  /**
   * The stages Accounts taht will need their subzone delegation
   */
  readonly stagesAccounts: Account[];
  /**
   * The top level domain name
   */
  readonly rootHostedZoneDNSName: string;
 
  /**
   * A boolean indicating if Domain name has already been registered to a third party or if you want this contruct to create it (the latter is not yet supported)
   */
  readonly thirdPartyProviderDNSUsed?: boolean;
}
 
/**
 * A class creating the main hosted zone and a role assumable by stages account to be able to set sub domain delegation
 */
export class RootDns extends cdk.Construct {
  rootHostedZone: route53.IHostedZone;
 
  constructor(scope: cdk.Construct, id: string, props: RootDnsProps) {
    super(scope, id);
    this.rootHostedZone = this.createRootHostedZone(props);
 
    for (const accountIndex in props.stagesAccounts) {
      const account = props.stagesAccounts[accountIndex];
      const stageSubZone = this.createStageSubZone(
        account,
        props.rootHostedZoneDNSName
      );
      this.createDNSAutoUpdateRole(account, stageSubZone);
      Eif (stageSubZone.hostedZoneNameServers) {
        new route53.RecordSet(
          this,
          `${account.accountName}SubZoneDelegationNSRecord`,
          {
            recordType: route53.RecordType.NS,
            target: RecordTarget.fromValues(...stageSubZone.hostedZoneNameServers?stageSubZone.hostedZoneNameServers:''),
            recordName: stageSubZone.zoneName,
            zone: this.rootHostedZone,
          }
        );
      }
    }
 
    Eif (
      props.thirdPartyProviderDNSUsed &&
      this.rootHostedZone.hostedZoneNameServers
    ) {
      new cdk.CfnOutput(this, `NS records`, {
        value: cdk.Fn.join(",", this.rootHostedZone.hostedZoneNameServers),
      });
    } else {
      throw new Error("Creation of DNS domain is not yet supported");
      // TODO: implement call to https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Route53Domains.html#registerDomain-property
    }
  }
 
  getSubdomainPrefix(account: Account){
    let prefix: string = account.accountStageName ? account.accountStageName : account.accountName;
    prefix = prefix.toLowerCase();
    prefix = prefix.replace(' ', '-');
    return prefix;
  }
 
  createStageSubZone(
    account: Account,
    rootHostedZoneDNSName: string
  ): route53.HostedZone {
    const subDomainPrefix = this.getSubdomainPrefix(account);
    return new route53.HostedZone(this, `${subDomainPrefix}StageSubZone`, {
      zoneName: `${subDomainPrefix}.${rootHostedZoneDNSName}`,
    });
  }
 
  createDNSAutoUpdateRole(
    account: Account,
    stageSubZone: route53.HostedZone
  ) {
    const dnsAutoUpdateRole = new iam.Role(this, stageSubZone.zoneName, {
      assumedBy: new iam.AccountPrincipal(account.accountId),
      roleName: `${stageSubZone.zoneName}-dns-update`
    });
 
    dnsAutoUpdateRole.addToPolicy(
      new iam.PolicyStatement({
        resources: [stageSubZone.hostedZoneArn],
        actions: [
          "route53:GetHostedZone",
          "route53:ChangeResourceRecordSets",
          "route53:TestDNSAnswer",
        ],
      })
    );
    dnsAutoUpdateRole.addToPolicy(
      new iam.PolicyStatement({
        resources: ['*'],
        actions: [
          "route53:ListHostedZonesByName"
        ],
      })
    );
    return dnsAutoUpdateRole;
  }
 
  createRootHostedZone(props: RootDnsProps) {
    return new route53.HostedZone(this, "RootHostedZone", {
      zoneName: props.rootHostedZoneDNSName,
    });
  }
}