diff --git a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts
index 0b2ddba2634f2..6129c57a2f7b9 100644
--- a/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts
+++ b/packages/@aws-cdk/aws-ecs-patterns/lib/fargate/load-balanced-fargate-service.ts
@@ -1,5 +1,5 @@
import ecs = require('@aws-cdk/aws-ecs');
-import { AliasRecord, IHostedZone } from '@aws-cdk/aws-route53';
+import { AddressRecordTarget, ARecord, IHostedZone } from '@aws-cdk/aws-route53';
import targets = require('@aws-cdk/aws-route53-targets');
import cdk = require('@aws-cdk/cdk');
import { LoadBalancedServiceBase, LoadBalancedServiceBaseProps } from '../base/load-balanced-service-base';
@@ -120,10 +120,10 @@ export class LoadBalancedFargateService extends LoadBalancedServiceBase {
throw new Error('A Route53 hosted domain zone name is required to configure the specified domain name');
}
- new AliasRecord(this, "DNS", {
+ new ARecord(this, "DNS", {
zone: props.domainZone,
recordName: props.domainName,
- target: new targets.LoadBalancerTarget(this.loadBalancer),
+ target: AddressRecordTarget.fromAlias(new targets.LoadBalancerTarget(this.loadBalancer)),
});
}
}
diff --git a/packages/@aws-cdk/aws-route53-targets/lib/cloudfront-target.ts b/packages/@aws-cdk/aws-route53-targets/lib/cloudfront-target.ts
index ceda2b8c7920e..f81b0db8cb665 100644
--- a/packages/@aws-cdk/aws-route53-targets/lib/cloudfront-target.ts
+++ b/packages/@aws-cdk/aws-route53-targets/lib/cloudfront-target.ts
@@ -14,7 +14,7 @@ export class CloudFrontTarget implements route53.IAliasRecordTarget {
constructor(private readonly distribution: cloudfront.CloudFrontWebDistribution) {
}
- public bind(_record: route53.IAliasRecord): route53.AliasRecordTargetConfig {
+ public bind(_record: route53.IRecordSet): route53.AliasRecordTargetConfig {
return {
hostedZoneId: CLOUDFRONT_ZONE_ID,
dnsName: this.distribution.domainName
diff --git a/packages/@aws-cdk/aws-route53-targets/lib/load-balancer-target.ts b/packages/@aws-cdk/aws-route53-targets/lib/load-balancer-target.ts
index 77b2d09817dc9..a621115cb5ab4 100644
--- a/packages/@aws-cdk/aws-route53-targets/lib/load-balancer-target.ts
+++ b/packages/@aws-cdk/aws-route53-targets/lib/load-balancer-target.ts
@@ -8,7 +8,7 @@ export class LoadBalancerTarget implements route53.IAliasRecordTarget {
constructor(private readonly loadBalancer: elbv2.ILoadBalancerV2) {
}
- public bind(_record: route53.IAliasRecord): route53.AliasRecordTargetConfig {
+ public bind(_record: route53.IRecordSet): route53.AliasRecordTargetConfig {
return {
hostedZoneId: this.loadBalancer.loadBalancerCanonicalHostedZoneId,
dnsName: this.loadBalancer.loadBalancerDnsName
diff --git a/packages/@aws-cdk/aws-route53-targets/test/cloudfront-target.test.ts b/packages/@aws-cdk/aws-route53-targets/test/cloudfront-target.test.ts
index d62ed2a4e1eb5..cb7b33b1d4ced 100644
--- a/packages/@aws-cdk/aws-route53-targets/test/cloudfront-target.test.ts
+++ b/packages/@aws-cdk/aws-route53-targets/test/cloudfront-target.test.ts
@@ -24,10 +24,10 @@ test('use CloudFront as record target', () => {
// WHEN
const zone = new route53.PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' });
- new route53.AliasRecord(zone, 'Alias', {
+ new route53.ARecord(zone, 'Alias', {
zone,
recordName: '_foo',
- target: new targets.CloudFrontTarget(distribution)
+ target: route53.AddressRecordTarget.fromAlias(new targets.CloudFrontTarget(distribution))
});
// THEN
@@ -37,4 +37,4 @@ test('use CloudFront as record target', () => {
HostedZoneId: "Z2FDTNDATAQYW2"
},
});
-});
\ No newline at end of file
+});
diff --git a/packages/@aws-cdk/aws-route53-targets/test/integ.alb-alias-target.ts b/packages/@aws-cdk/aws-route53-targets/test/integ.alb-alias-target.ts
index 37245fdaeba80..ceea3cfea60e9 100644
--- a/packages/@aws-cdk/aws-route53-targets/test/integ.alb-alias-target.ts
+++ b/packages/@aws-cdk/aws-route53-targets/test/integ.alb-alias-target.ts
@@ -19,10 +19,10 @@ const lb = new elbv2.ApplicationLoadBalancer(stack, 'LB', {
const zone = new route53.PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' });
-new route53.AliasRecord(zone, 'Alias', {
+new route53.ARecord(zone, 'Alias', {
zone,
recordName: '_foo',
- target: new targets.LoadBalancerTarget(lb)
+ target: route53.AddressRecordTarget.fromAlias(new targets.LoadBalancerTarget(lb))
});
app.run();
diff --git a/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.ts b/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.ts
index e384dd9558cff..0adb96e563ad4 100644
--- a/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.ts
+++ b/packages/@aws-cdk/aws-route53-targets/test/integ.cloudfront-alias-target.ts
@@ -25,10 +25,10 @@ const distribution = new cloudfront.CloudFrontWebDistribution(stack, 'MyDistribu
]
});
-new route53.AliasRecord(zone, 'Alias', {
+new route53.ARecord(zone, 'Alias', {
zone,
recordName: '_foo',
- target: new targets.CloudFrontTarget(distribution)
+ target: route53.AddressRecordTarget.fromAlias(new targets.CloudFrontTarget(distribution))
});
app.run();
diff --git a/packages/@aws-cdk/aws-route53-targets/test/load-balancer-target.test.ts b/packages/@aws-cdk/aws-route53-targets/test/load-balancer-target.test.ts
index 623cb7bbe5023..c78062120398d 100644
--- a/packages/@aws-cdk/aws-route53-targets/test/load-balancer-target.test.ts
+++ b/packages/@aws-cdk/aws-route53-targets/test/load-balancer-target.test.ts
@@ -19,10 +19,10 @@ test('use ALB as record target', () => {
const zone = new route53.PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' });
// WHEN
- new route53.AliasRecord(zone, 'Alias', {
+ new route53.ARecord(zone, 'Alias', {
zone,
recordName: '_foo',
- target: new targets.LoadBalancerTarget(lb)
+ target: route53.AddressRecordTarget.fromAlias(new targets.LoadBalancerTarget(lb))
});
// THEN
diff --git a/packages/@aws-cdk/aws-route53/README.md b/packages/@aws-cdk/aws-route53/README.md
index 336cf7c0dfb6b..6d181e441799d 100644
--- a/packages/@aws-cdk/aws-route53/README.md
+++ b/packages/@aws-cdk/aws-route53/README.md
@@ -6,7 +6,7 @@ To add a public hosted zone:
import route53 = require('@aws-cdk/aws-route53');
new route53.PublicHostedZone(this, 'HostedZone', {
- zoneName: 'fully.qualified.domain.com'
+ zoneName: 'fully.qualified.domain.com'
});
```
@@ -21,8 +21,8 @@ import route53 = require('@aws-cdk/aws-route53');
const vpc = new ec2.VpcNetwork(this, 'VPC');
const zone = new route53.PrivateHostedZone(this, 'HostedZone', {
- zoneName: 'fully.qualified.domain.com',
- vpc // At least one VPC has to be added to a Private Hosted Zone.
+ zoneName: 'fully.qualified.domain.com',
+ vpc // At least one VPC has to be added to a Private Hosted Zone.
});
```
@@ -34,15 +34,45 @@ To add a TXT record to your zone:
```ts
import route53 = require('@aws-cdk/aws-route53');
-new route53.TxtRecord(zone, 'TXTRecord', {
- recordName: '_foo', // If the name ends with a ".", it will be used as-is;
- // if it ends with a "." followed by the zone name, a trailing "." will be added automatically;
- // otherwise, a ".", the zone name, and a trailing "." will be added automatically.
- recordValue: 'Bar!', // Will be quoted for you, and " will be escaped automatically.
- ttl: 90, // Optional - default is 1800
+new route53.TxtRecord(this, 'TXTRecord', {
+ zone: myZone,
+ recordName: '_foo', // If the name ends with a ".", it will be used as-is;
+ // if it ends with a "." followed by the zone name, a trailing "." will be added automatically;
+ // otherwise, a ".", the zone name, and a trailing "." will be added automatically.
+ // Defaults to zone root if not specified.
+ values: [ // Will be quoted for you, and " will be escaped automatically.
+ 'Bar!',
+ 'Baz?'
+ ],
+ ttl: 90, // Optional - default is 1800
});
```
+To add a A record to your zone:
+```ts
+import route53 = require('@aws-cdk/aws-route53');
+
+new route53.ARecord(this, 'ARecord', {
+ zone: myZone,
+ target: route53.AddressRecordTarget.fromIpAddresses('1.2.3.4', '5.6.7.8')
+})
+```
+
+To add a AAAA record pointing to a CloudFront distribution:
+```ts
+import route53 = require('@aws-cdk/aws-route53');
+import targets = require('@aws-cdk/aws-route53-targets');
+
+new route53.AaaaRecord(this, 'Alias', {
+ zone: myZone,
+ target: route53.AddressRecordTarget.fromAlias(new targets.CloudFrontTarget(distribution))
+})
+```
+
+Constructs are available for A, AAAA, CAA, CNAME, MX, NS, SRV and TXT records.
+
+Use the `CaaAmazonRecord` construct to easily restrict certificate authorities
+allowed to issue certificates for a domain to Amazon only.
### Adding records to existing hosted zones
@@ -50,8 +80,8 @@ If you know the ID and Name of a Hosted Zone, you can import it directly:
```ts
const zone = HostedZone.import(this, 'MyZone', {
- zoneName: 'example.com',
- hostedZoneId: 'ZOJJZC49E0EPZ',
+ zoneName: 'example.com',
+ hostedZoneId: 'ZOJJZC49E0EPZ',
});
```
@@ -60,6 +90,6 @@ to discover and import it:
```ts
const zone = new HostedZoneProvider(this, {
- domainName: 'example.com'
+ domainName: 'example.com'
}).findAndImport(this, 'MyZone');
```
diff --git a/packages/@aws-cdk/aws-route53/lib/alias-record-target.ts b/packages/@aws-cdk/aws-route53/lib/alias-record-target.ts
index a0023eb038c98..e86be1a07a3c8 100644
--- a/packages/@aws-cdk/aws-route53/lib/alias-record-target.ts
+++ b/packages/@aws-cdk/aws-route53/lib/alias-record-target.ts
@@ -1,4 +1,4 @@
-import { IAliasRecord } from "./records/alias";
+import { IRecordSet } from "./record-set";
/**
* Classes that are valid alias record targets, like CloudFront distributions and load
@@ -8,7 +8,7 @@ export interface IAliasRecordTarget {
/**
* Return hosted zone ID and DNS name, usable for Route53 alias targets
*/
- bind(record: IAliasRecord): AliasRecordTargetConfig;
+ bind(record: IRecordSet): AliasRecordTargetConfig;
}
/**
diff --git a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
index af99ddb1efbb0..e89494dc02e70 100644
--- a/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
+++ b/packages/@aws-cdk/aws-route53/lib/hosted-zone.ts
@@ -1,7 +1,7 @@
import ec2 = require('@aws-cdk/aws-ec2');
import { Construct, Resource, Token } from '@aws-cdk/cdk';
import { HostedZoneAttributes, IHostedZone } from './hosted-zone-ref';
-import { ZoneDelegationRecord } from './records';
+import { CaaAmazonRecord, ZoneDelegationRecord } from './record-set';
import { CfnHostedZone } from './route53.generated';
import { validateZoneName } from './util';
@@ -107,7 +107,19 @@ export class HostedZone extends Resource implements IHostedZone {
}
}
-export interface PublicHostedZoneProps extends CommonHostedZoneProps { }
+/**
+ * Construction properties for a PublicHostedZone.
+ */
+export interface PublicHostedZoneProps extends CommonHostedZoneProps {
+ /**
+ * Whether to create a CAA record to restrict certificate authorities allowed
+ * to issue certificates for this domain to Amazon only.
+ *
+ * @default false
+ */
+ readonly caaAmazon?: boolean;
+}
+
export interface IPublicHostedZone extends IHostedZone { }
/**
@@ -127,6 +139,12 @@ export class PublicHostedZone extends HostedZone implements IPublicHostedZone {
constructor(scope: Construct, id: string, props: PublicHostedZoneProps) {
super(scope, id, props);
+
+ if (props.caaAmazon) {
+ new CaaAmazonRecord(this, 'CaaAmazon', {
+ zone: this
+ });
+ }
}
public addVpc(_vpc: ec2.IVpc) {
@@ -142,7 +160,7 @@ export class PublicHostedZone extends HostedZone implements IPublicHostedZone {
public addDelegation(delegate: IPublicHostedZone, opts: ZoneDelegationOptions = {}): void {
new ZoneDelegationRecord(this, `${this.zoneName} -> ${delegate.zoneName}`, {
zone: this,
- delegatedZoneName: delegate.zoneName,
+ recordName: delegate.zoneName,
nameServers: delegate.hostedZoneNameServers!, // PublicHostedZones always have name servers!
comment: opts.comment,
ttl: opts.ttl,
diff --git a/packages/@aws-cdk/aws-route53/lib/index.ts b/packages/@aws-cdk/aws-route53/lib/index.ts
index 1abc508d84901..9a6ed0a853679 100644
--- a/packages/@aws-cdk/aws-route53/lib/index.ts
+++ b/packages/@aws-cdk/aws-route53/lib/index.ts
@@ -1,7 +1,7 @@
export * from './hosted-zone';
export * from './hosted-zone-provider';
export * from './hosted-zone-ref';
-export * from './records';
+export * from './record-set';
export * from './alias-record-target';
// AWS::Route53 CloudFormation Resources:
diff --git a/packages/@aws-cdk/aws-route53/lib/record-set.ts b/packages/@aws-cdk/aws-route53/lib/record-set.ts
new file mode 100644
index 0000000000000..929ef4ec28192
--- /dev/null
+++ b/packages/@aws-cdk/aws-route53/lib/record-set.ts
@@ -0,0 +1,447 @@
+import { Construct, IResource, Resource, Token } from '@aws-cdk/cdk';
+import { IAliasRecordTarget } from './alias-record-target';
+import { IHostedZone } from './hosted-zone-ref';
+import { CfnRecordSet } from './route53.generated';
+import { determineFullyQualifiedDomainName } from './util';
+
+/**
+ * A record set
+ */
+export interface IRecordSet extends IResource {
+ /**
+ * The domain name of the record
+ */
+ readonly domainName: string;
+}
+
+/**
+ * The record type.
+ */
+export enum RecordType {
+ A = 'A',
+ AAAA = 'AAAA',
+ CAA = 'CAA',
+ CNAME = 'CNAME',
+ MX = 'MX',
+ NAPTR = 'NAPTR',
+ NS = 'NS',
+ PTR = 'PTR',
+ SOA = 'SOA',
+ SPF = 'SPF',
+ SRV = 'SRV',
+ TXT = 'TXT'
+}
+
+/**
+ * Options for a RecordSet.
+ */
+export interface RecordSetOptions {
+ /**
+ * The hosted zone in which to define the new record.
+ */
+ readonly zone: IHostedZone;
+
+ /**
+ * The domain name for this record.
+ *
+ * @default zone root
+ */
+ readonly recordName?: string;
+
+ /**
+ * The resource record cache time to live (TTL) in seconds.
+ *
+ * @default 1800 seconds
+ */
+ readonly ttl?: number;
+
+ /**
+ * A comment to add on the record.
+ *
+ * @default no comment
+ */
+ readonly comment?: string;
+}
+
+/**
+ * Type union for a record that accepts multiple types of target.
+ */
+export class RecordTarget {
+ /**
+ * Use string values as target.
+ */
+ public static fromValues(...values: string[]) {
+ return new RecordTarget(values);
+ }
+
+ /**
+ * Use an alias as target.
+ */
+ public static fromAlias(aliasTarget: IAliasRecordTarget) {
+ return new RecordTarget(undefined, aliasTarget);
+ }
+
+ protected constructor(public readonly values?: string[], public readonly aliasTarget?: IAliasRecordTarget) {
+ }
+}
+
+/**
+ * Construction properties for a RecordSet.
+ */
+export interface RecordSetProps extends RecordSetOptions {
+ /**
+ * The record type.
+ */
+ readonly recordType: RecordType;
+
+ /**
+ * The target for this record, either `RecordTarget.fromValues()` or
+ * `RecordTarget.fromAlias()`.
+ */
+ readonly target: RecordTarget;
+}
+
+/**
+ * A record set.
+ */
+export class RecordSet extends Resource implements IRecordSet {
+ public readonly domainName: string;
+
+ constructor(scope: Construct, id: string, props: RecordSetProps) {
+ super(scope, id);
+
+ const ttl = props.target.aliasTarget ? undefined : (props.ttl || 1800).toString();
+
+ const recordSet = new CfnRecordSet(this, 'Resource', {
+ hostedZoneId: props.zone.hostedZoneId,
+ name: determineFullyQualifiedDomainName(props.recordName || props.zone.zoneName, props.zone),
+ type: props.recordType,
+ resourceRecords: props.target.values,
+ aliasTarget: props.target.aliasTarget && props.target.aliasTarget.bind(this),
+ ttl,
+ comment: props.comment
+ });
+
+ this.domainName = recordSet.ref;
+ }
+}
+
+/**
+ *
+ */
+export class AddressRecordTarget extends RecordTarget {
+ /**
+ * Use ip adresses as target.
+ */
+ public static fromIpAddresses(...ipAddresses: string[]) {
+ return RecordTarget.fromValues(...ipAddresses);
+ }
+}
+
+/**
+ * Construction properties for a ARecord.
+ */
+export interface ARecordProps extends RecordSetOptions {
+ /**
+ * The target.
+ */
+ readonly target: AddressRecordTarget;
+}
+
+/**
+ * A DNS A record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class ARecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: ARecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.A,
+ target: props.target,
+ });
+ }
+}
+
+/**
+ * Construction properties for a AaaaRecord.
+ */
+export interface AaaaRecordProps extends RecordSetOptions, ARecordProps {}
+
+/**
+ * A DNS AAAA record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class AaaaRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: AaaaRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.AAAA,
+ target: props.target,
+ });
+ }
+}
+
+/**
+ * Construction properties for a CnameRecord.
+ */
+export interface CnameRecordProps extends RecordSetOptions {
+ /**
+ * The domain name.
+ */
+ readonly domainName: string;
+}
+
+/**
+ * A DNS CNAME record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class CnameRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: CnameRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.CNAME,
+ target: RecordTarget.fromValues(props.domainName)
+ });
+ }
+}
+
+/**
+ * Construction properties for a TxtRecord.
+ */
+export interface TxtRecordProps extends RecordSetOptions {
+ /**
+ * The text values.
+ */
+ readonly values: string[];
+}
+
+/**
+ * A DNS TXT record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class TxtRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: TxtRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.TXT,
+ target: RecordTarget.fromValues(...props.values.map(v => JSON.stringify(v))),
+ });
+ }
+}
+
+/**
+ * Properties for a SRV record value.
+ */
+export interface SrvRecordValue {
+ /**
+ * The priority.
+ */
+ readonly priority: number;
+
+ /**
+ * The weight.
+ */
+ readonly weight: number;
+
+ /**
+ * The port.
+ */
+ readonly port: number;
+
+ /**
+ * The server host name.
+ */
+ readonly hostName: string;
+}
+/**
+ * Construction properties for a SrvRecord.
+ */
+export interface SrvRecordProps extends RecordSetOptions {
+ /**
+ * The values.
+ */
+ readonly values: SrvRecordValue[];
+}
+
+/**
+ * A DNS SRV record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class SrvRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: SrvRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.SRV,
+ target: RecordTarget.fromValues(...props.values.map(v => `${v.priority} ${v.weight} ${v.port} ${v.hostName}`)),
+ });
+ }
+}
+
+/**
+ * The CAA tag.
+ */
+export enum CaaTag {
+ /**
+ * Explicity authorizes a single certificate authority to issue a
+ * certificate (any type) for the hostname.
+ */
+ ISSUE = 'issue',
+
+ /**
+ * Explicity authorizes a single certificate authority to issue a
+ * wildcard certificate (and only wildcard) for the hostname.
+ */
+ ISSUEWILD = 'issuewild',
+
+ /**
+ * Specifies a URL to which a certificate authority may report policy
+ * violations.
+ */
+ IODEF = 'iodef',
+}
+
+/**
+ * Properties for a CAA record value.
+ */
+export interface CaaRecordValue {
+ /**
+ * The flag.
+ */
+ readonly flag: number;
+
+ /**
+ * The tag.
+ */
+ readonly tag: CaaTag;
+
+ /**
+ * The value associated with the tag.
+ */
+ readonly value: string;
+}
+
+/**
+ * Construction properties for a CaaRecord.
+ */
+export interface CaaRecordProps extends RecordSetOptions {
+ /**
+ * The values.
+ */
+ readonly values: CaaRecordValue[];
+}
+
+/**
+ * A DNS CAA record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class CaaRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: CaaRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.CAA,
+ target: RecordTarget.fromValues(...props.values.map(v => `${v.flag} ${v.tag} "${v.value}"`)),
+ });
+ }
+}
+
+/**
+ * Construction properties for a CaaAmazonRecord.
+ */
+export interface CaaAmazonRecordProps extends RecordSetOptions {}
+
+/**
+ * A DNS Amazon CAA record.
+ *
+ * A CAA record to restrict certificate authorities allowed
+ * to issue certificates for a domain to Amazon only.
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class CaaAmazonRecord extends CaaRecord {
+ constructor(scope: Construct, id: string, props: CaaAmazonRecordProps) {
+ super(scope, id, {
+ ...props,
+ values: [
+ {
+ flag: 0,
+ tag: CaaTag.ISSUE,
+ value: 'amazon.com'
+ }
+ ],
+ recordName: props.zone.zoneName
+ });
+ }
+}
+
+/**
+ * Properties for a MX record value.
+ */
+export interface MxRecordValue {
+ /**
+ * The priority.
+ */
+ readonly priority: number;
+
+ /**
+ * The mail server host name.
+ */
+ readonly hostName: string;
+}
+
+/**
+ * Construction properties for a MxRecord.
+ */
+export interface MxRecordProps extends RecordSetOptions {
+ /**
+ * The values.
+ */
+ readonly values: MxRecordValue[];
+}
+
+/**
+ * A DNS MX record
+ *
+ * @resource AWS::Route53::RecordSet
+ */
+export class MxRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: MxRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.MX,
+ target: RecordTarget.fromValues(...props.values.map(v => `${v.priority} ${v.hostName}`))
+ });
+ }
+}
+
+/**
+ * Construction properties for a ZoneDelegationRecord
+ */
+export interface ZoneDelegationRecordProps extends RecordSetOptions {
+ /**
+ * The name servers to report in the delegation records.
+ */
+ readonly nameServers: string[];
+}
+
+/**
+ * A record to delegate further lookups to a different set of name servers.
+ */
+export class ZoneDelegationRecord extends RecordSet {
+ constructor(scope: Construct, id: string, props: ZoneDelegationRecordProps) {
+ super(scope, id, {
+ ...props,
+ recordType: RecordType.NS,
+ target: RecordTarget.fromValues(...Token.isToken(props.nameServers)
+ ? props.nameServers // Can't map a string-array token!
+ : props.nameServers.map(ns => (Token.isToken(ns) || ns.endsWith('.')) ? ns : `${ns}.`)
+ ),
+ ttl: props.ttl || 172_800
+ });
+ }
+}
diff --git a/packages/@aws-cdk/aws-route53/lib/records/_util.ts b/packages/@aws-cdk/aws-route53/lib/records/_util.ts
deleted file mode 100644
index ba503721f258c..0000000000000
--- a/packages/@aws-cdk/aws-route53/lib/records/_util.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { IHostedZone } from '../hosted-zone-ref';
-
-/**
- * Route53 requires the record names are specified as fully qualified names, but this
- * forces lots of redundant work on the user (repeating the zone name over and over).
- * This function allows the user to be lazier and offers a nicer experience, by
- * qualifying relative names appropriately:
- *
- * @param providedName the user-specified name of the record.
- * @param zoneName the fully-qualified name of the zone the record will be created in.
- *
- * @returns
- * - If +providedName+ ends with a +.+, use it as-is
- * - If +providedName+ ends with or equals +zoneName+, append a trailing +.+
- * - Otherwise, append +.+, +zoneName+ and a trailing +.+
- *
- */
-export function determineFullyQualifiedDomainName(providedName: string, hostedZone: IHostedZone): string {
- if (providedName.endsWith('.')) {
- return providedName;
- }
-
- const suffix = `.${hostedZone.zoneName}`;
- if (providedName.endsWith(suffix) || providedName === hostedZone.zoneName) {
- return `${providedName}.`;
- }
-
- return `${providedName}${suffix}.`;
-}
diff --git a/packages/@aws-cdk/aws-route53/lib/records/alias.ts b/packages/@aws-cdk/aws-route53/lib/records/alias.ts
deleted file mode 100644
index a933116e1fb14..0000000000000
--- a/packages/@aws-cdk/aws-route53/lib/records/alias.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Construct, IConstruct } from '@aws-cdk/cdk';
-import { IAliasRecordTarget } from '../alias-record-target';
-import { IHostedZone } from '../hosted-zone-ref';
-import { CfnRecordSet } from '../route53.generated';
-import { determineFullyQualifiedDomainName } from './_util';
-
-export interface AliasRecordProps {
- /**
- * The zone in which this alias should be defined.
- */
- readonly zone: IHostedZone;
- /**
- * Name for the record. This can be the FQDN for the record (foo.example.com) or
- * a subdomain of the parent hosted zone (foo, with example.com as the hosted zone).
- */
- readonly recordName: string;
- /**
- * Target for the alias record
- */
- readonly target: IAliasRecordTarget;
-}
-
-/**
- * An alias record
- */
-export interface IAliasRecord extends IConstruct {
- /**
- * The domain name of the record
- */
- readonly domainName: string;
-}
-
-/**
- * Define a new Route53 alias record
- */
-export class AliasRecord extends Construct implements IAliasRecord {
- public readonly domainName: string;
-
- constructor(scope: Construct, id: string, props: AliasRecordProps) {
- super(scope, id);
-
- const record = new CfnRecordSet(this, 'Resource', {
- hostedZoneId: props.zone.hostedZoneId,
- name: determineFullyQualifiedDomainName(props.recordName, props.zone),
- type: 'A', // ipv4
- aliasTarget: props.target.bind(this)
- });
-
- this.domainName = record.ref;
- }
-}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-route53/lib/records/cname.ts b/packages/@aws-cdk/aws-route53/lib/records/cname.ts
deleted file mode 100644
index 55e9a7bd3f32a..0000000000000
--- a/packages/@aws-cdk/aws-route53/lib/records/cname.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Construct } from '@aws-cdk/cdk';
-import { IHostedZone } from '../hosted-zone-ref';
-import { CfnRecordSet } from '../route53.generated';
-import { determineFullyQualifiedDomainName } from './_util';
-
-export interface CnameRecordProps {
- /**
- * The hosted zone in which to define the new TXT record.
- */
- readonly zone: IHostedZone;
-
- /**
- * The domain name for this record set.
- */
- readonly recordName: string;
-
- /**
- * The value for this record set.
- */
- readonly recordValue: string;
-
- /**
- * The resource record cache time to live (TTL) in seconds.
- *
- * @default 1800 seconds
- */
- readonly ttl?: number;
-}
-
-/**
- * A DNS CNAME record
- */
-export class CnameRecord extends Construct {
- constructor(scope: Construct, id: string, props: CnameRecordProps) {
- super(scope, id);
-
- const ttl = props.ttl === undefined ? 1800 : props.ttl;
-
- new CfnRecordSet(this, 'Resource', {
- hostedZoneId: props.zone.hostedZoneId,
- name: determineFullyQualifiedDomainName(props.recordName, props.zone),
- type: 'CNAME',
- resourceRecords: [ props.recordValue ],
- ttl: ttl.toString(),
- });
- }
-}
diff --git a/packages/@aws-cdk/aws-route53/lib/records/index.ts b/packages/@aws-cdk/aws-route53/lib/records/index.ts
deleted file mode 100644
index 1643af3bdaadb..0000000000000
--- a/packages/@aws-cdk/aws-route53/lib/records/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './alias';
-export * from './txt';
-export * from './cname';
-export * from './zone-delegation';
diff --git a/packages/@aws-cdk/aws-route53/lib/records/txt.ts b/packages/@aws-cdk/aws-route53/lib/records/txt.ts
deleted file mode 100644
index 64bd769848640..0000000000000
--- a/packages/@aws-cdk/aws-route53/lib/records/txt.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { Construct } from '@aws-cdk/cdk';
-import { IHostedZone } from '../hosted-zone-ref';
-import { CfnRecordSet } from '../route53.generated';
-import { determineFullyQualifiedDomainName } from './_util';
-
-export interface TxtRecordProps {
- /**
- * The hosted zone in which to define the new TXT record.
- */
- readonly zone: IHostedZone;
-
- /**
- * The domain name for this record set.
- */
- readonly recordName: string;
-
- /**
- * The value for this record set.
- */
- readonly recordValue: string;
-
- /**
- * The resource record cache time to live (TTL) in seconds.
- *
- * @default 1800 seconds
- */
- readonly ttl?: number;
-}
-
-/**
- * A DNS TXT record
- */
-export class TxtRecord extends Construct {
- constructor(scope: Construct, id: string, props: TxtRecordProps) {
- super(scope, id);
-
- // JSON.stringify conveniently wraps strings in " and escapes ".
- const recordValue = JSON.stringify(props.recordValue);
- const ttl = props.ttl === undefined ? 1800 : props.ttl;
-
- new CfnRecordSet(this, 'Resource', {
- hostedZoneId: props.zone.hostedZoneId,
- name: determineFullyQualifiedDomainName(props.recordName, props.zone),
- type: 'TXT',
- resourceRecords: [recordValue],
- ttl: ttl.toString()
- });
- }
-}
diff --git a/packages/@aws-cdk/aws-route53/lib/records/zone-delegation.ts b/packages/@aws-cdk/aws-route53/lib/records/zone-delegation.ts
deleted file mode 100644
index 175e1c921aceb..0000000000000
--- a/packages/@aws-cdk/aws-route53/lib/records/zone-delegation.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import cdk = require('@aws-cdk/cdk');
-import { ZoneDelegationOptions } from '../hosted-zone';
-import { IHostedZone } from '../hosted-zone-ref';
-import { CfnRecordSet } from '../route53.generated';
-import { determineFullyQualifiedDomainName } from './_util';
-
-export interface ZoneDelegationRecordProps extends ZoneDelegationOptions {
- /**
- * The zone in which this delegate is defined.
- */
- readonly zone: IHostedZone;
- /**
- * The name of the zone that delegation is made to.
- */
- readonly delegatedZoneName: string;
-
- /**
- * The name servers to report in the delegation records.
- */
- readonly nameServers: string[];
-}
-
-/**
- * A record to delegate further lookups to a different set of name servers
- */
-export class ZoneDelegationRecord extends cdk.Construct {
- constructor(scope: cdk.Construct, id: string, props: ZoneDelegationRecordProps) {
- super(scope, id);
-
- const ttl = props.ttl === undefined ? 172_800 : props.ttl;
- const resourceRecords = cdk.Token.isToken(props.nameServers)
- ? props.nameServers // Can't map a string-array token!
- : props.nameServers.map(ns => (cdk.Token.isToken(ns) || ns.endsWith('.')) ? ns : `${ns}.`);
-
- new CfnRecordSet(this, 'Resource', {
- hostedZoneId: props.zone.hostedZoneId,
- name: determineFullyQualifiedDomainName(props.delegatedZoneName, props.zone),
- type: 'NS',
- ttl: ttl.toString(),
- comment: props.comment,
- resourceRecords,
- });
- }
-}
diff --git a/packages/@aws-cdk/aws-route53/lib/util.ts b/packages/@aws-cdk/aws-route53/lib/util.ts
index 2483b74f3a4ec..358f39f2da7ce 100644
--- a/packages/@aws-cdk/aws-route53/lib/util.ts
+++ b/packages/@aws-cdk/aws-route53/lib/util.ts
@@ -1,3 +1,5 @@
+import { IHostedZone } from './hosted-zone-ref';
+
/**
* Validates a zone name is valid by Route53 specifc naming rules,
* and that there is no trailing dot in the name.
@@ -26,3 +28,31 @@ class ValidationError extends Error {
super(message);
}
}
+
+/**
+ * Route53 requires the record names are specified as fully qualified names, but this
+ * forces lots of redundant work on the user (repeating the zone name over and over).
+ * This function allows the user to be lazier and offers a nicer experience, by
+ * qualifying relative names appropriately:
+ *
+ * @param providedName the user-specified name of the record.
+ * @param zoneName the fully-qualified name of the zone the record will be created in.
+ *
+ * @returns
+ * - If +providedName+ ends with a +.+, use it as-is
+ * - If +providedName+ ends with or equals +zoneName+, append a trailing +.+
+ * - Otherwise, append +.+, +zoneName+ and a trailing +.+
+ *
+ */
+export function determineFullyQualifiedDomainName(providedName: string, hostedZone: IHostedZone): string {
+ if (providedName.endsWith('.')) {
+ return providedName;
+ }
+
+ const suffix = `.${hostedZone.zoneName}`;
+ if (providedName.endsWith(suffix) || providedName === hostedZone.zoneName) {
+ return `${providedName}.`;
+ }
+
+ return `${providedName}${suffix}.`;
+}
diff --git a/packages/@aws-cdk/aws-route53/test/integ.route53.expected.json b/packages/@aws-cdk/aws-route53/test/integ.route53.expected.json
index 9cde321023c0f..4a1797a096e06 100644
--- a/packages/@aws-cdk/aws-route53/test/integ.route53.expected.json
+++ b/packages/@aws-cdk/aws-route53/test/integ.route53.expected.json
@@ -18,7 +18,7 @@
"VPCPublicSubnet1SubnetB4246D30": {
"Type": "AWS::EC2::Subnet",
"Properties": {
- "CidrBlock": "10.0.0.0/19",
+ "CidrBlock": "10.0.0.0/17",
"VpcId": {
"Ref": "VPCB9E5F0B4"
},
@@ -106,192 +106,10 @@
]
}
},
- "VPCPublicSubnet2Subnet74179F39": {
- "Type": "AWS::EC2::Subnet",
- "Properties": {
- "CidrBlock": "10.0.32.0/19",
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "AvailabilityZone": "test-region-1b",
- "MapPublicIpOnLaunch": true,
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PublicSubnet2"
- },
- {
- "Key": "aws-cdk:subnet-name",
- "Value": "Public"
- },
- {
- "Key": "aws-cdk:subnet-type",
- "Value": "Public"
- }
- ]
- }
- },
- "VPCPublicSubnet2RouteTable6F1A15F1": {
- "Type": "AWS::EC2::RouteTable",
- "Properties": {
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PublicSubnet2"
- }
- ]
- }
- },
- "VPCPublicSubnet2RouteTableAssociation5A808732": {
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPublicSubnet2RouteTable6F1A15F1"
- },
- "SubnetId": {
- "Ref": "VPCPublicSubnet2Subnet74179F39"
- }
- }
- },
- "VPCPublicSubnet2DefaultRouteB7481BBA": {
- "Type": "AWS::EC2::Route",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPublicSubnet2RouteTable6F1A15F1"
- },
- "DestinationCidrBlock": "0.0.0.0/0",
- "GatewayId": {
- "Ref": "VPCIGWB7E252D3"
- }
- },
- "DependsOn": [
- "VPCVPCGW99B986DC"
- ]
- },
- "VPCPublicSubnet2EIP4947BC00": {
- "Type": "AWS::EC2::EIP",
- "Properties": {
- "Domain": "vpc"
- }
- },
- "VPCPublicSubnet2NATGateway3C070193": {
- "Type": "AWS::EC2::NatGateway",
- "Properties": {
- "AllocationId": {
- "Fn::GetAtt": [
- "VPCPublicSubnet2EIP4947BC00",
- "AllocationId"
- ]
- },
- "SubnetId": {
- "Ref": "VPCPublicSubnet2Subnet74179F39"
- },
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PublicSubnet2"
- }
- ]
- }
- },
- "VPCPublicSubnet3Subnet631C5E25": {
- "Type": "AWS::EC2::Subnet",
- "Properties": {
- "CidrBlock": "10.0.64.0/19",
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "AvailabilityZone": "test-region-1c",
- "MapPublicIpOnLaunch": true,
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PublicSubnet3"
- },
- {
- "Key": "aws-cdk:subnet-name",
- "Value": "Public"
- },
- {
- "Key": "aws-cdk:subnet-type",
- "Value": "Public"
- }
- ]
- }
- },
- "VPCPublicSubnet3RouteTable98AE0E14": {
- "Type": "AWS::EC2::RouteTable",
- "Properties": {
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PublicSubnet3"
- }
- ]
- }
- },
- "VPCPublicSubnet3RouteTableAssociation427FE0C6": {
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPublicSubnet3RouteTable98AE0E14"
- },
- "SubnetId": {
- "Ref": "VPCPublicSubnet3Subnet631C5E25"
- }
- }
- },
- "VPCPublicSubnet3DefaultRouteA0D29D46": {
- "Type": "AWS::EC2::Route",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPublicSubnet3RouteTable98AE0E14"
- },
- "DestinationCidrBlock": "0.0.0.0/0",
- "GatewayId": {
- "Ref": "VPCIGWB7E252D3"
- }
- },
- "DependsOn": [
- "VPCVPCGW99B986DC"
- ]
- },
- "VPCPublicSubnet3EIPAD4BC883": {
- "Type": "AWS::EC2::EIP",
- "Properties": {
- "Domain": "vpc"
- }
- },
- "VPCPublicSubnet3NATGatewayD3048F5C": {
- "Type": "AWS::EC2::NatGateway",
- "Properties": {
- "AllocationId": {
- "Fn::GetAtt": [
- "VPCPublicSubnet3EIPAD4BC883",
- "AllocationId"
- ]
- },
- "SubnetId": {
- "Ref": "VPCPublicSubnet3Subnet631C5E25"
- },
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PublicSubnet3"
- }
- ]
- }
- },
"VPCPrivateSubnet1Subnet8BCA10E0": {
"Type": "AWS::EC2::Subnet",
"Properties": {
- "CidrBlock": "10.0.96.0/19",
+ "CidrBlock": "10.0.128.0/17",
"VpcId": {
"Ref": "VPCB9E5F0B4"
},
@@ -350,130 +168,6 @@
}
}
},
- "VPCPrivateSubnet2SubnetCFCDAA7A": {
- "Type": "AWS::EC2::Subnet",
- "Properties": {
- "CidrBlock": "10.0.128.0/19",
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "AvailabilityZone": "test-region-1b",
- "MapPublicIpOnLaunch": false,
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PrivateSubnet2"
- },
- {
- "Key": "aws-cdk:subnet-name",
- "Value": "Private"
- },
- {
- "Key": "aws-cdk:subnet-type",
- "Value": "Private"
- }
- ]
- }
- },
- "VPCPrivateSubnet2RouteTable0A19E10E": {
- "Type": "AWS::EC2::RouteTable",
- "Properties": {
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PrivateSubnet2"
- }
- ]
- }
- },
- "VPCPrivateSubnet2RouteTableAssociation0C73D413": {
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPrivateSubnet2RouteTable0A19E10E"
- },
- "SubnetId": {
- "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A"
- }
- }
- },
- "VPCPrivateSubnet2DefaultRouteF4F5CFD2": {
- "Type": "AWS::EC2::Route",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPrivateSubnet2RouteTable0A19E10E"
- },
- "DestinationCidrBlock": "0.0.0.0/0",
- "NatGatewayId": {
- "Ref": "VPCPublicSubnet2NATGateway3C070193"
- }
- }
- },
- "VPCPrivateSubnet3Subnet3EDCD457": {
- "Type": "AWS::EC2::Subnet",
- "Properties": {
- "CidrBlock": "10.0.160.0/19",
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "AvailabilityZone": "test-region-1c",
- "MapPublicIpOnLaunch": false,
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PrivateSubnet3"
- },
- {
- "Key": "aws-cdk:subnet-name",
- "Value": "Private"
- },
- {
- "Key": "aws-cdk:subnet-type",
- "Value": "Private"
- }
- ]
- }
- },
- "VPCPrivateSubnet3RouteTable192186F8": {
- "Type": "AWS::EC2::RouteTable",
- "Properties": {
- "VpcId": {
- "Ref": "VPCB9E5F0B4"
- },
- "Tags": [
- {
- "Key": "Name",
- "Value": "aws-cdk-route53-integ/VPC/PrivateSubnet3"
- }
- ]
- }
- },
- "VPCPrivateSubnet3RouteTableAssociationC28D144E": {
- "Type": "AWS::EC2::SubnetRouteTableAssociation",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPrivateSubnet3RouteTable192186F8"
- },
- "SubnetId": {
- "Ref": "VPCPrivateSubnet3Subnet3EDCD457"
- }
- }
- },
- "VPCPrivateSubnet3DefaultRoute27F311AE": {
- "Type": "AWS::EC2::Route",
- "Properties": {
- "RouteTableId": {
- "Ref": "VPCPrivateSubnet3RouteTable192186F8"
- },
- "DestinationCidrBlock": "0.0.0.0/0",
- "NatGatewayId": {
- "Ref": "VPCPublicSubnet3NATGatewayD3048F5C"
- }
- }
- },
"VPCIGWB7E252D3": {
"Type": "AWS::EC2::InternetGateway",
"Properties": {
@@ -521,7 +215,8 @@
"Ref": "PrivateZone27242E85"
},
"ResourceRecords": [
- "\"Bar!\""
+ "\"Bar!\"",
+ "\"Baz?\""
],
"TTL": "60"
}
@@ -568,6 +263,35 @@
],
"TTL": "1800"
}
+ },
+ "ACCC8ACD5": {
+ "Type": "AWS::Route53::RecordSet",
+ "Properties": {
+ "Name": "test.cdk.local.",
+ "Type": "A",
+ "HostedZoneId": {
+ "Ref": "PrivateZone27242E85"
+ },
+ "ResourceRecords": [
+ "1.2.3.4",
+ "5.6.7.8"
+ ],
+ "TTL": "1800"
+ }
+ },
+ "CaaAmazon40DF725F": {
+ "Type": "AWS::Route53::RecordSet",
+ "Properties": {
+ "Name": "cdk.test.",
+ "Type": "CAA",
+ "HostedZoneId": {
+ "Ref": "PublicZone2E1C4E34"
+ },
+ "ResourceRecords": [
+ "0 issue \"amazon.com\""
+ ],
+ "TTL": "1800"
+ }
}
},
"Outputs": {
diff --git a/packages/@aws-cdk/aws-route53/test/integ.route53.ts b/packages/@aws-cdk/aws-route53/test/integ.route53.ts
index 625357e5717b6..435e660c80219 100644
--- a/packages/@aws-cdk/aws-route53/test/integ.route53.ts
+++ b/packages/@aws-cdk/aws-route53/test/integ.route53.ts
@@ -1,12 +1,12 @@
import ec2 = require('@aws-cdk/aws-ec2');
import cdk = require('@aws-cdk/cdk');
-import { CnameRecord, PrivateHostedZone, PublicHostedZone, TxtRecord } from '../lib';
+import { AddressRecordTarget, ARecord, CaaAmazonRecord, CnameRecord, PrivateHostedZone, PublicHostedZone, TxtRecord } from '../lib';
const app = new cdk.App();
const stack = new cdk.Stack(app, 'aws-cdk-route53-integ');
-const vpc = new ec2.Vpc(stack, 'VPC');
+const vpc = new ec2.Vpc(stack, 'VPC', { maxAZs: 1 });
const privateZone = new PrivateHostedZone(stack, 'PrivateZone', {
zoneName: 'cdk.local', vpc
@@ -23,14 +23,27 @@ publicZone.addDelegation(publicSubZone);
new TxtRecord(privateZone, 'TXT', {
zone: privateZone,
recordName: '_foo',
- recordValue: 'Bar!',
+ values: [
+ 'Bar!',
+ 'Baz?'
+ ],
ttl: 60
});
new CnameRecord(stack, 'CNAME', {
zone: privateZone,
recordName: 'www',
- recordValue: 'server'
+ domainName: 'server'
+});
+
+new ARecord(stack, 'A', {
+ zone: privateZone,
+ recordName: 'test',
+ target: AddressRecordTarget.fromIpAddresses('1.2.3.4', '5.6.7.8')
+});
+
+new CaaAmazonRecord(stack, 'CaaAmazon', {
+ zone: publicZone
});
new cdk.CfnOutput(stack, 'PrivateZoneId', { value: privateZone.hostedZoneId });
diff --git a/packages/@aws-cdk/aws-route53/test/test.alias-record.ts b/packages/@aws-cdk/aws-route53/test/test.alias-record.ts
deleted file mode 100644
index 412f0f271d17d..0000000000000
--- a/packages/@aws-cdk/aws-route53/test/test.alias-record.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { expect, haveResource } from '@aws-cdk/assert';
-import { Stack } from '@aws-cdk/cdk';
-import { Test } from 'nodeunit';
-import { AliasRecord, IAliasRecordTarget, PublicHostedZone } from '../lib';
-
-export = {
- 'test alias record'(test: Test) {
- // GIVEN
- const stack = new Stack();
- const zone = new PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' });
-
- const target: IAliasRecordTarget = {
- bind: () => {
- return {
- hostedZoneId: 'Z2P70J7EXAMPLE',
- dnsName: 'foo.example.com'
- };
- }
- };
-
- // WHEN
- new AliasRecord(zone, 'Alias', {
- zone,
- recordName: '_foo',
- target
- });
-
- // THEN - stack contains a record set
- expect(stack).to(haveResource('AWS::Route53::RecordSet', {
- Name: '_foo.test.public.',
- HostedZoneId: {
- Ref: 'HostedZoneDB99F866'
- },
- Type: 'A',
- AliasTarget: {
- HostedZoneId: 'Z2P70J7EXAMPLE',
- DNSName: 'foo.example.com',
- }
- }));
-
- test.done();
- },
- 'test alias record on zone root'(test: Test) {
- // GIVEN
- const stack = new Stack();
- const zone = new PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' });
-
- const target: IAliasRecordTarget = {
- bind: () => {
- return {
- hostedZoneId: 'Z2P70J7EXAMPLE',
- dnsName: 'foo.example.com'
- };
- }
- };
-
- // WHEN
- new AliasRecord(zone, 'Alias', {
- zone,
- recordName: 'test.public',
- target
- });
-
- // THEN - stack contains a record set
- expect(stack).to(haveResource('AWS::Route53::RecordSet', {
- Name: 'test.public.',
- HostedZoneId: {
- Ref: 'HostedZoneDB99F866'
- },
- Type: 'A',
- AliasTarget: {
- HostedZoneId: 'Z2P70J7EXAMPLE',
- DNSName: 'foo.example.com',
- }
- }));
-
- test.done();
- }
-};
diff --git a/packages/@aws-cdk/aws-route53/test/test.cname-record.ts b/packages/@aws-cdk/aws-route53/test/test.cname-record.ts
deleted file mode 100644
index 92d1b84b478fe..0000000000000
--- a/packages/@aws-cdk/aws-route53/test/test.cname-record.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import { expect, haveResource } from '@aws-cdk/assert';
-import { Stack } from '@aws-cdk/cdk';
-import { Test } from 'nodeunit';
-import route53 = require('../lib');
-
-export = {
- 'with default ttl'(test: Test) {
- // GIVEN
- const stack = new Stack();
-
- // WHEN
- const zone = new route53.HostedZone(stack, 'HostedZone', {
- zoneName: 'myzone'
- });
-
- new route53.CnameRecord(stack, 'MyCname', {
- zone,
- recordName: 'www',
- recordValue: 'zzz',
- });
-
- // THEN
- expect(stack).to(haveResource('AWS::Route53::RecordSet', {
- Name: "www.myzone.",
- Type: "CNAME",
- HostedZoneId: {
- Ref: "HostedZoneDB99F866"
- },
- ResourceRecords: [
- "zzz"
- ],
- TTL: "1800"
- }));
- test.done();
- },
-
- 'with custom ttl'(test: Test) {
- // GIVEN
- const stack = new Stack();
-
- // WHEN
- const zone = new route53.HostedZone(stack, 'HostedZone', {
- zoneName: 'myzone'
- });
-
- new route53.CnameRecord(stack, 'MyCname', {
- zone,
- recordName: 'aa',
- recordValue: 'bbb',
- ttl: 6077,
- });
-
- // THEN
- expect(stack).to(haveResource('AWS::Route53::RecordSet', {
- Name: "aa.myzone.",
- Type: "CNAME",
- HostedZoneId: {
- Ref: "HostedZoneDB99F866"
- },
- ResourceRecords: [
- "bbb"
- ],
- TTL: "6077"
- }));
- test.done();
- }
-};
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-route53/test/test.record-set.ts b/packages/@aws-cdk/aws-route53/test/test.record-set.ts
new file mode 100644
index 0000000000000..8dca376245ab5
--- /dev/null
+++ b/packages/@aws-cdk/aws-route53/test/test.record-set.ts
@@ -0,0 +1,458 @@
+import { expect, haveResource } from '@aws-cdk/assert';
+import { Stack } from '@aws-cdk/cdk';
+import { Test } from 'nodeunit';
+import route53 = require('../lib');
+
+export = {
+ 'with default ttl'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.RecordSet(stack, 'Basic', {
+ zone,
+ recordName: 'www',
+ recordType: route53.RecordType.CNAME,
+ target: route53.RecordTarget.fromValues('zzz')
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "CNAME",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "zzz"
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'with custom ttl'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.RecordSet(stack, 'Basic', {
+ zone,
+ recordName: 'aa',
+ recordType: route53.RecordType.CNAME,
+ target: route53.RecordTarget.fromValues('bbb'),
+ ttl: 6077
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "aa.myzone.",
+ Type: "CNAME",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "bbb"
+ ],
+ TTL: "6077"
+ }));
+ test.done();
+ },
+
+ 'defaults to zone root'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.RecordSet(stack, 'Basic', {
+ zone,
+ recordType: route53.RecordType.A,
+ target: route53.RecordTarget.fromValues('1.2.3.4'),
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "myzone.",
+ Type: "A",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "1.2.3.4"
+ ],
+ }));
+ test.done();
+ },
+
+ 'A record with ip addresses'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.ARecord(stack, 'A', {
+ zone,
+ recordName: 'www',
+ target: route53.AddressRecordTarget.fromIpAddresses('1.2.3.4', '5.6.7.8'),
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "A",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "1.2.3.4",
+ "5.6.7.8"
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'A record with alias'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ const target: route53.IAliasRecordTarget = {
+ bind: () => {
+ return {
+ hostedZoneId: 'Z2P70J7EXAMPLE',
+ dnsName: 'foo.example.com'
+ };
+ }
+ };
+
+ // WHEN
+ new route53.ARecord(zone, 'Alias', {
+ zone,
+ recordName: '_foo',
+ target: route53.RecordTarget.fromAlias(target)
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: '_foo.myzone.',
+ HostedZoneId: {
+ Ref: 'HostedZoneDB99F866'
+ },
+ Type: 'A',
+ AliasTarget: {
+ HostedZoneId: 'Z2P70J7EXAMPLE',
+ DNSName: 'foo.example.com',
+ }
+ }));
+
+ test.done();
+ },
+
+ 'AAAA record with ip addresses'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.AaaaRecord(stack, 'AAAA', {
+ zone,
+ recordName: 'www',
+ target: route53.AddressRecordTarget.fromIpAddresses('2001:0db8:85a3:0000:0000:8a2e:0370:7334'),
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "AAAA",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7334"
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'AAAA record with alias on zone root'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ const target: route53.IAliasRecordTarget = {
+ bind: () => {
+ return {
+ hostedZoneId: 'Z2P70J7EXAMPLE',
+ dnsName: 'foo.example.com'
+ };
+ }
+ };
+
+ // WHEN
+ new route53.AaaaRecord(zone, 'Alias', {
+ zone,
+ target: route53.RecordTarget.fromAlias(target)
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: 'myzone.',
+ HostedZoneId: {
+ Ref: 'HostedZoneDB99F866'
+ },
+ Type: 'AAAA',
+ AliasTarget: {
+ HostedZoneId: 'Z2P70J7EXAMPLE',
+ DNSName: 'foo.example.com',
+ }
+ }));
+
+ test.done();
+ },
+
+ 'CNAME record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.CnameRecord(stack, 'CNAME', {
+ zone,
+ recordName: 'www',
+ domainName: 'hello',
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "CNAME",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "hello"
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'TXT record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.TxtRecord(stack, 'TXT', {
+ zone,
+ recordName: 'www',
+ values: ['should be enclosed with double quotes']
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "TXT",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ '"should be enclosed with double quotes"'
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'SRV record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.SrvRecord(stack, 'SRV', {
+ zone,
+ recordName: 'www',
+ values: [{
+ hostName: 'aws.com',
+ port: 8080,
+ priority: 10,
+ weight: 5
+ }]
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "SRV",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ '10 5 8080 aws.com'
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'CAA record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.CaaRecord(stack, 'CAA', {
+ zone,
+ recordName: 'www',
+ values: [{
+ flag: 0,
+ tag: route53.CaaTag.ISSUE,
+ value: 'ssl.com'
+ }]
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "www.myzone.",
+ Type: "CAA",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ '0 issue "ssl.com"'
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'CAA Amazon record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.CaaAmazonRecord(stack, 'CAAAmazon', {
+ zone,
+ recordName: 'www', // should have no effect
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "myzone.",
+ Type: "CAA",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ '0 issue "amazon.com"'
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'MX record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.MxRecord(stack, 'MX', {
+ zone,
+ recordName: 'mail',
+ values: [{
+ hostName: 'workmail.aws',
+ priority: 10
+ }]
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "mail.myzone.",
+ Type: "MX",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ '10 workmail.aws'
+ ],
+ TTL: "1800"
+ }));
+ test.done();
+ },
+
+ 'Zone delegation record'(test: Test) {
+ // GIVEN
+ const stack = new Stack();
+
+ const zone = new route53.HostedZone(stack, 'HostedZone', {
+ zoneName: 'myzone'
+ });
+
+ // WHEN
+ new route53.ZoneDelegationRecord(stack, 'NS', {
+ zone,
+ recordName: 'foo',
+ nameServers: ['ns-1777.awsdns-30.co.uk']
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Name: "foo.myzone.",
+ Type: "NS",
+ HostedZoneId: {
+ Ref: "HostedZoneDB99F866"
+ },
+ ResourceRecords: [
+ "ns-1777.awsdns-30.co.uk."
+ ],
+ TTL: "172800"
+ }));
+ test.done();
+ }
+};
diff --git a/packages/@aws-cdk/aws-route53/test/test.route53.ts b/packages/@aws-cdk/aws-route53/test/test.route53.ts
index f2efdd0b53ad9..0daf47e07a08f 100644
--- a/packages/@aws-cdk/aws-route53/test/test.route53.ts
+++ b/packages/@aws-cdk/aws-route53/test/test.route53.ts
@@ -80,7 +80,7 @@ export = {
new TxtRecord(importedZone as any, 'Record', {
zone: importedZone,
recordName: 'lookHere',
- recordValue: 'SeeThere'
+ values: ['SeeThere']
});
expect(stack2).to(haveResource("AWS::Route53::RecordSet", {
@@ -194,6 +194,27 @@ export = {
}));
test.done();
},
+
+ 'public hosted zone wiht caaAmazon set to true'(test: Test) {
+ // GIVEN
+ const stack = new cdk.Stack();
+
+ // WHEN
+ new PublicHostedZone(stack, 'MyHostedZone', {
+ zoneName: 'protected.com',
+ caaAmazon: true
+ });
+
+ // THEN
+ expect(stack).to(haveResource('AWS::Route53::RecordSet', {
+ Type: 'CAA',
+ Name: 'protected.com.',
+ ResourceRecords: [
+ '0 issue "amazon.com"'
+ ]
+ }));
+ test.done();
+ }
};
class TestApp {
diff --git a/packages/@aws-cdk/aws-route53/test/test.txt-record.ts b/packages/@aws-cdk/aws-route53/test/test.txt-record.ts
deleted file mode 100644
index c213b798813c2..0000000000000
--- a/packages/@aws-cdk/aws-route53/test/test.txt-record.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { exactlyMatchTemplate, expect } from '@aws-cdk/assert';
-import { App, Stack } from '@aws-cdk/cdk';
-import { Test } from 'nodeunit';
-import { PublicHostedZone, TxtRecord } from '../lib';
-
-export = {
- 'TXT records': {
- TXT(test: Test) {
- const app = new TestApp();
- const zone = new PublicHostedZone(app.stack, 'HostedZone', { zoneName: 'test.public' });
- new TxtRecord(zone, 'TXT', { zone, recordName: '_foo', recordValue: 'Bar!' });
- expect(app.stack).to(exactlyMatchTemplate({
- Resources: {
- HostedZoneDB99F866: {
- Type: 'AWS::Route53::HostedZone',
- Properties: {
- Name: 'test.public.'
- }
- },
- HostedZoneTXT69C29760: {
- Type: 'AWS::Route53::RecordSet',
- Properties: {
- HostedZoneId: {
- Ref: 'HostedZoneDB99F866'
- },
- Name: '_foo.test.public.',
- ResourceRecords: ['"Bar!"'],
- Type: 'TXT',
- TTL: '1800'
- }
- }
- }
- }));
- test.done();
- }
- }
-};
-
-class TestApp {
- public readonly stack: Stack;
- private readonly app = new App();
-
- constructor() {
- const account = '123456789012';
- const region = 'bermuda-triangle';
- this.app.node.setContext(`availability-zones:${account}:${region}`,
- [`${region}-1a`]);
- this.stack = new Stack(this.app, 'MyStack', { env: { account, region } });
- }
-}
diff --git a/packages/@aws-cdk/aws-route53/test/test.zone-delegation-record.ts b/packages/@aws-cdk/aws-route53/test/test.zone-delegation-record.ts
deleted file mode 100644
index 03d05b2161c74..0000000000000
--- a/packages/@aws-cdk/aws-route53/test/test.zone-delegation-record.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { exactlyMatchTemplate, expect } from '@aws-cdk/assert';
-import { App, Stack } from '@aws-cdk/cdk';
-import { Test } from 'nodeunit';
-import { PublicHostedZone, ZoneDelegationRecord } from '../lib';
-
-export = {
- 'Zone Delegation records': {
- NS(test: Test) {
- const app = new TestApp();
- const zone = new PublicHostedZone(app.stack, 'HostedZone', { zoneName: 'test.public' });
- new ZoneDelegationRecord(zone, 'NS', {
- zone,
- delegatedZoneName: 'foo',
- nameServers: ['ns-1777.awsdns-30.co.uk']
- });
- expect(app.stack).to(exactlyMatchTemplate({
- Resources: {
- HostedZoneDB99F866: {
- Type: 'AWS::Route53::HostedZone',
- Properties: {
- Name: 'test.public.'
- }
- },
- HostedZoneNS1BB87CC3: {
- Type: 'AWS::Route53::RecordSet',
- Properties: {
- HostedZoneId: {
- Ref: 'HostedZoneDB99F866'
- },
- Name: 'foo.test.public.',
- ResourceRecords: ['ns-1777.awsdns-30.co.uk.'],
- Type: 'NS',
- TTL: '172800'
- }
- }
- }
- }));
- test.done();
- }
- }
-};
-
-class TestApp {
- public readonly stack: Stack;
- private readonly app = new App();
-
- constructor() {
- const account = '123456789012';
- const region = 'bermuda-triangle';
- this.app.node.setContext(`availability-zones:${account}:${region}`,
- [`${region}-1a`]);
- this.stack = new Stack(this.app, 'MyStack', { env: { account, region } });
- }
-}