Client-specific domains with CloudFormation for clients that use Google as email provider
A number of our clients want vanity domains for their experiences, which adds a laywer (or two) of operations overhead beyond just having a line item in the invoice. In the spirit of ‘infrastructure as code’-all-the-things, this is now my process for registering new domains for our clients
- Register the domain through Route 53
- Delete the hosted zone that is automatically created. (It would be nice if there was an option when getting the domain to not automatically create the hosted zone.)
- Login to Google Apps and add the domain as an alias. When prompted to verify, choose Ghandi as the provider and get the TXT record that is needed
- Create a CloudFormation stack with this template. Some interesting bits;
- Tags in the AWS Tag Editor are case sensitive so ‘client’ and ‘Client’ are not equivilent
- I think all my stacks will include the ‘CreationDateParameter’ parameter from now on which gets added as a tag to the Resource[s] that can accept them. This is part of the ‘timebombing’ of resources to make things more resilient. In theory I can also use AWS Config to find Resources that are not tagged and therefore presumably under CloudFormation control.
- Same thing for the ‘client’ tag. Though still nto keen on that name or billing_client or such.
<pre lang="json">{ "AWSTemplateFormatVersion": "2010-09-09", "Parameters": { "ClientNameParameter": { "Type": "String", "Description": "Which client this domain is for" }, "DomainNameParameter": { "Type": "String", "Description": "The domain to add a HostedZone for" }, "GoogleSiteVerificationParameter": { "Type": "String", "Description": "The Google Site Verification TXT value" }, "CreationDateParameter" : { "Description" : "Date", "Type" : "String", "Default" : "2017-08-27 00:00:00", "AllowedPattern" : "^\\d{4}(-\\d{2}){2} (\\d{2}:){2}\\d{2}$", "ConstraintDescription" : "Date and time of creation" } }, "Resources": { "clienthostedzone": { "Type": "AWS::Route53::HostedZone", "Properties": { "Name": {"Fn::Join": [".", [{"Ref": "DomainNameParameter"}]]}, "HostedZoneTags": [ { "Key": "client", "Value": {"Ref": "ClientNameParameter"} }, { "Key": "CloudFormation", "Value": { "Ref" : "CreationDateParameter" } } ] } }, "dnsclienthostedzone": { "Type": "AWS::Route53::RecordSetGroup", "Properties": { "HostedZoneId": { "Ref": "clienthostedzone" }, "RecordSets": [ { "Name": {"Fn::Join": [".", [{"Ref": "DomainNameParameter"}]]}, "Type": "TXT", "TTL": "900", "ResourceRecords": [ {"Fn::Sub": "\"google-site-verification=${GoogleSiteVerificationParameter}\""} ] }, { "Name": {"Fn::Join": [".", [{"Ref": "DomainNameParameter"}]]}, "Type": "MX", "TTL": "900", "ResourceRecords": [ "1 ASPMX.L.GOOGLE.COM", "5 ALT1.ASPMX.L.GOOGLE.COM", "5 ALT2.ASPMX.L.GOOGLE.COM", "10 ALT3.ASPMX.L.GOOGLE.COM", "10 ALT4.ASPMX.L.GOOGLE.COM" ] } ] } }, } }
- Update the domain’s nameservers for the ones in our newly created Hosted Zone. I suspect this could be done via a Lambda backed custom resource, but that’s a couple steps too complicated for me right now. If I have to do this more than once every couple weeks it’ll be work the learning time.
- Validate the domain with Google.
- Manually create a certificate for ${DomainNameParameter} and *.${DomainNameParameter}. (For reals, this should be an automatic thing for domains registered in Route 53 and hosted within Route 53.)
And then I need to create an ALB for the domain and point it at the right service. But thats getting rather yak shave-y. The ALB needs to be added to the ASG for the service but those are not under CloudFormation control so I need to get them under control.