Updating DNS with output of another CloudFormation stack

I show the changes to my Sceptre config & CloudFormation template required to point DNS to the two CloudFront distributions I've created.

This post is part of a series where I add SSL to this blog. The www template I created in two previous posts had two outputs defined for the CloudFront distributions created, so that I could import these values in my dns template and avoid hard-coding their domain names. This post shows the changes required to do just that.

Updated Outputs

Let's dive right in. According to the Outputs documentation you need to add the optional Export object to the output, giving the fully qualified Name to be used for the variable. It is heavily implied that you should try to work the StackName into this names somehow. That's what we're doing on lines 9 and 16. I've also added a new variable, StackName, which I'll get back to in a moment.

 1: Outputs:
 2:   RedirDomain:
 3:     Description: >-
 4:       The CloudFront domain name for our Apex domain that we use to
 5:       redirect to our SiteDomain. We will use this to add a CNAME from
 6:       our DNS template.
 7:     Value: !GetAtt ApexCloudFront.DomainName
 8:     Export:
 9:       Name: !Sub "${AWS::StackName}-RedirDomain"
10:   SiteDomain:
11:     Description: >-
12:       The CloudFront domain name for our site. We will use this to add
13:       a CNAME from our DNS template.
14:     Value: !GetAtt SiteCloudFront.DomainName
15:     Export:
16:       Name: !Sub "${AWS::StackName}-SiteDomain"
17:   StackName:
18:     Description: >-
19:       The stack name to use for templates that depend on this one.
20:     Value: !Ref 'AWS::StackName'

Now, the required changes to the DNS template from a previous post. We start by removing the ApexRecords and WwwRecord, as we'll get these from the WWW template. We'll add a new DependentStackName parameter, like so:

Parameters:
  [...]
  DependentStackName:
    Type: String
    Description: >-
      Name of stack we depend on and will grab exported values from.
      Computed by Sceptre.

Config file update

As you can infer from the comment in the previous section, Sceptre provides a bit of magic to obtain the stack name from the WWW template as an input to the DNS one. To make use of that I added the below !stack_output Sceptre fragment to config/superloopy/dns.yaml. This file is so short that I'm just showing it in its entirety here:

template_path: templates/dns.yaml
parameters:
  DomainName: superloopy.io
  DependentStackName: !stack_output www::StackName

RecordSet updates

Let's start with the WwwRecordSet since that's the simplest change. The only thing we need to change here is the ResourceRecord list. Instead of !Ref WwwRecord we'll replace it with our imported value from our dependent stack. (While researching how to do this I found examples of using the !Sub function which resulted in a much nicer way to produce the fqdn from the apex domain; see 4.) The WwwRecordSet now looks like this:

1: WwwRecordSet:
2:   Type: 'AWS::Route53::RecordSet'
3:   Properties:
4:     Name: !Sub "www.${DomainName}"
5:     HostedZoneId: !Ref Zone
6:     Type: CNAME
7:     TTL: !Ref TTL
8:     ResourceRecords:
9:       - Fn::ImportValue: !Sub "${DependentStackName}-SiteDomain"

The ApexRecordSet requires a different change. You can't CNAME the Apex domain, but AWS Route 53 supports an AliasTarget extension that we can use instead. This is handy since we don't want to hard-code all of CloudFront's IP addresses like we did previously with GitHub. The new resource is in the next snippet. Note that I found the "magic" HostedZoneId on line 10 in the AliasTarget documentation1.

 1: ApexRecordSet:
 2:   Type: 'AWS::Route53::RecordSet'
 3:   Properties:
 4:     Name: !Ref DomainName
 5:     HostedZoneId: !Ref Zone
 6:     Type: A
 7:     AliasTarget:
 8:       DNSName:
 9:         Fn::ImportValue: !Sub "${DependentStackName}-RedirDomain"
10:       HostedZoneId: Z2FDTNDATAQYW2

Conclusion

This change concludes the work to add SSL to my blog. The only bit I haven't shown is how to tell Gandi to use the name servers I've configured on AWS Route 53 rather than the default ones, but I feel that is outside the scope of this blog series. It's now done, however, and if you're reading this you're getting2 the full experience of my HTTPS-enabled blog :-)

1
I didn't see much reason to make it a template property as it is used everywhere you create an AliasTarget for a CloudFront domain.
2

Or, as it happens, maybe not yet. Because it turns out lots of home routers (including my own) ignore TTLs and cache DNS servers for a long time. So for a week or so I'll leave the generated files in GitHub, and also post new content to the AWS S3 bucket.

Date: 23 July 2017

Author: Stig Brautaset

Validate