diff --git a/packages/@aws-cdk/aws-route53-targets/README.md b/packages/@aws-cdk/aws-route53-targets/README.md index 5270482564ee8..ba9f1ea7e4831 100644 --- a/packages/@aws-cdk/aws-route53-targets/README.md +++ b/packages/@aws-cdk/aws-route53-targets/README.md @@ -63,6 +63,19 @@ This library contains Route53 Alias Record targets for: For example, if the Amazon-provided DNS for the load balancer is `ALB-xxxxxxx.us-west-2.elb.amazonaws.com`, CDK will create alias target in Route 53 will be `dualstack.ALB-xxxxxxx.us-west-2.elb.amazonaws.com`. +* GlobalAccelerator + + ```ts + new route53.ARecord(stack, 'AliasRecord', { + zone, + target: route53.RecordTarget.fromAlias(new targets.GlobalAcceleratorTarget(accelerator)), + // or - route53.RecordTarget.fromAlias(new targets.GlobalAcceleratorDomainTarget('xyz.awsglobalaccelerator.com')), + }); + ``` + +**Important:** If you use GlobalAcceleratorDomainTarget, passing a string rather than an instance of IAccelerator, ensure that the string is a valid domain name of an existing Global Accelerator instance. +See [the documentation on DNS addressing](https://docs.aws.amazon.com/global-accelerator/latest/dg/dns-addressing-custom-domains.dns-addressing.html) with Global Accelerator for more info. + * InterfaceVpcEndpoints **Important:** Based on the CFN docs for VPCEndpoints - [see here](attrDnsEntries) - the attributes returned for DnsEntries in CloudFormation is a combination of the hosted zone ID and the DNS name. The entries are ordered as follows: regional public DNS, zonal public DNS, private DNS, and wildcard DNS. This order is not enforced for AWS Marketplace services, and therefore this CDK construct is ONLY guaranteed to work with non-marketplace services. diff --git a/packages/@aws-cdk/aws-route53-targets/lib/global-accelerator-target.ts b/packages/@aws-cdk/aws-route53-targets/lib/global-accelerator-target.ts new file mode 100644 index 0000000000000..d80aaaa140b1b --- /dev/null +++ b/packages/@aws-cdk/aws-route53-targets/lib/global-accelerator-target.ts @@ -0,0 +1,41 @@ +import * as globalaccelerator from '@aws-cdk/aws-globalaccelerator'; +import * as route53 from '@aws-cdk/aws-route53'; + + +/** + * Use a Global Accelerator domain name as an alias record target. + */ +export class GlobalAcceleratorDomainTarget implements route53.IAliasRecordTarget { + /** + * The hosted zone Id if using an alias record in Route53. + * This value never changes. + * Ref: https://docs.aws.amazon.com/general/latest/gr/global_accelerator.html + */ + public static readonly GLOBAL_ACCELERATOR_ZONE_ID = 'Z2BJ6XQ5FK7U4H'; + + /** + * Create an Alias Target for a Global Accelerator domain name. + */ + constructor(private readonly acceleratorDomainName: string) { + } + + bind(_record: route53.IRecordSet): route53.AliasRecordTargetConfig { + return { + hostedZoneId: GlobalAcceleratorTarget.GLOBAL_ACCELERATOR_ZONE_ID, + dnsName: this.acceleratorDomainName, + }; + } +} + +/** + * Use a Global Accelerator instance domain name as an alias record target. + */ +export class GlobalAcceleratorTarget extends GlobalAcceleratorDomainTarget { + + /** + * Create an Alias Target for a Global Accelerator instance. + */ + constructor(accelerator: globalaccelerator.IAccelerator) { + super(accelerator.dnsName); + } +} diff --git a/packages/@aws-cdk/aws-route53-targets/lib/index.ts b/packages/@aws-cdk/aws-route53-targets/lib/index.ts index af574aa599519..5c8b86fb959c1 100644 --- a/packages/@aws-cdk/aws-route53-targets/lib/index.ts +++ b/packages/@aws-cdk/aws-route53-targets/lib/index.ts @@ -6,3 +6,4 @@ export * from './cloudfront-target'; export * from './load-balancer-target'; export * from './interface-vpc-endpoint-target'; export * from './userpool-domain'; +export * from './global-accelerator-target'; diff --git a/packages/@aws-cdk/aws-route53-targets/package.json b/packages/@aws-cdk/aws-route53-targets/package.json index 08ea2cb9e91f4..d98999baaab6a 100644 --- a/packages/@aws-cdk/aws-route53-targets/package.json +++ b/packages/@aws-cdk/aws-route53-targets/package.json @@ -80,6 +80,7 @@ "@aws-cdk/aws-elasticloadbalancing": "0.0.0", "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-globalaccelerator": "0.0.0", "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", @@ -96,6 +97,7 @@ "@aws-cdk/aws-elasticloadbalancing": "0.0.0", "@aws-cdk/aws-elasticloadbalancingv2": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", + "@aws-cdk/aws-globalaccelerator": "0.0.0", "@aws-cdk/aws-route53": "0.0.0", "@aws-cdk/aws-s3": "0.0.0", "@aws-cdk/core": "0.0.0", diff --git a/packages/@aws-cdk/aws-route53-targets/test/global-accelerator-target.test.ts b/packages/@aws-cdk/aws-route53-targets/test/global-accelerator-target.test.ts new file mode 100644 index 0000000000000..07db27b92940e --- /dev/null +++ b/packages/@aws-cdk/aws-route53-targets/test/global-accelerator-target.test.ts @@ -0,0 +1,59 @@ +import '@aws-cdk/assert/jest'; +import * as globalaccelerator from '@aws-cdk/aws-globalaccelerator'; +import * as route53 from '@aws-cdk/aws-route53'; +import { Stack } from '@aws-cdk/core'; +import * as targets from '../lib'; + +test('GlobalAcceleratorTarget exposes a public constant of the zone id', () => { + expect(targets.GlobalAcceleratorTarget.GLOBAL_ACCELERATOR_ZONE_ID).toStrictEqual('Z2BJ6XQ5FK7U4H'); + expect(targets.GlobalAcceleratorDomainTarget.GLOBAL_ACCELERATOR_ZONE_ID).toStrictEqual('Z2BJ6XQ5FK7U4H'); +}); + +test('GlobalAcceleratorTarget creates an alias resource with a string domain name', () => { + // GIVEN + const stack = new Stack(); + const zone = new route53.PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' }); + + // WHEN + new route53.ARecord(stack, 'GlobalAcceleratorAlias', { + target: route53.RecordTarget.fromAlias(new targets.GlobalAcceleratorDomainTarget('xyz.awsglobalaccelerator.com')), + recordName: 'test', + zone, + }); + + // THEN + expect(stack).toHaveResource('AWS::Route53::RecordSet', { + AliasTarget: { + DNSName: 'xyz.awsglobalaccelerator.com', + HostedZoneId: 'Z2BJ6XQ5FK7U4H', + }, + }); +}); + +test('GlobalAcceleratorTarget creates an alias resource with a Global Accelerator reference domain name', () => { + // GIVEN + const stack = new Stack(); + const accelerator = new globalaccelerator.Accelerator(stack, 'Accelerator'); + const logicalId = stack.getLogicalId(accelerator.node.defaultChild); + const zone = new route53.PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' }); + + // WHEN + new route53.ARecord(stack, 'GlobalAcceleratorAlias', { + target: route53.RecordTarget.fromAlias(new targets.GlobalAcceleratorTarget(accelerator)), + recordName: 'test', + zone, + }); + + // THEN + expect(stack).toHaveResource('AWS::Route53::RecordSet', { + AliasTarget: { + DNSName: { + 'Fn::GetAtt': [ + logicalId, + 'DnsName', + ], + }, + HostedZoneId: 'Z2BJ6XQ5FK7U4H', + }, + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-route53-targets/test/integ.globalaccelerator-alias-target.expected.json b/packages/@aws-cdk/aws-route53-targets/test/integ.globalaccelerator-alias-target.expected.json new file mode 100644 index 0000000000000..e11d21275bbab --- /dev/null +++ b/packages/@aws-cdk/aws-route53-targets/test/integ.globalaccelerator-alias-target.expected.json @@ -0,0 +1,52 @@ +{ + "Resources": { + "Accelerator8EB0B6B1": { + "Type": "AWS::GlobalAccelerator::Accelerator", + "Properties": { + "Name": "aws-cdk-globalaccelerator-integ", + "Enabled": true + } + }, + "HostedZoneDB99F866": { + "Type": "AWS::Route53::HostedZone", + "Properties": { + "Name": "test.public." + } + }, + "LocalGlobalAcceleratorAlias18B4A87A": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": "test-local.test.public.", + "Type": "A", + "AliasTarget": { + "DNSName": { + "Fn::GetAtt": [ + "Accelerator8EB0B6B1", + "DnsName" + ] + }, + "HostedZoneId": "Z2BJ6XQ5FK7U4H" + }, + "Comment": "Alias to the locally created Global Accelerator", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + } + } + }, + "ExistingGlobalAcceleratorAlias7ACF888C": { + "Type": "AWS::Route53::RecordSet", + "Properties": { + "Name": "test-existing.test.public.", + "Type": "A", + "AliasTarget": { + "DNSName": "someexisting.awsglobalaccelerator.com", + "HostedZoneId": "Z2BJ6XQ5FK7U4H" + }, + "Comment": "Alias to the an existing Global Accelerator", + "HostedZoneId": { + "Ref": "HostedZoneDB99F866" + } + } + } + } + } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-route53-targets/test/integ.globalaccelerator-alias-target.ts b/packages/@aws-cdk/aws-route53-targets/test/integ.globalaccelerator-alias-target.ts new file mode 100644 index 0000000000000..560e828accaa5 --- /dev/null +++ b/packages/@aws-cdk/aws-route53-targets/test/integ.globalaccelerator-alias-target.ts @@ -0,0 +1,31 @@ +#!/usr/bin/env node +import * as globalaccelerator from '@aws-cdk/aws-globalaccelerator'; +import * as route53 from '@aws-cdk/aws-route53'; +import * as cdk from '@aws-cdk/core'; +import * as targets from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-globalaccelerator-integ'); + +let accelerator = new globalaccelerator.Accelerator(stack, 'Accelerator', { + acceleratorName: `${stack.stackName}`, + enabled: true, +}); + +const zone = new route53.PublicHostedZone(stack, 'HostedZone', { zoneName: 'test.public' }); + +new route53.ARecord(stack, 'LocalGlobalAcceleratorAlias', { + comment: 'Alias to the locally created Global Accelerator', + target: route53.RecordTarget.fromAlias(new targets.GlobalAcceleratorTarget(accelerator)), + recordName: 'test-local', + zone, +}); + +new route53.ARecord(stack, 'ExistingGlobalAcceleratorAlias', { + comment: 'Alias to the an existing Global Accelerator', + target: route53.RecordTarget.fromAlias(new targets.GlobalAcceleratorDomainTarget('someexisting.awsglobalaccelerator.com')), + recordName: 'test-existing', + zone, +}); + +app.synth();