At my current company, we started development from a single AWS account. As the product grew in size, we faced a need to provision multiple AWS accounts to share the infrastructure across them.
A non-trivial problem we faced, is how to deploy resources in different AWS accounts, and let them inherit the same domain, that is managed in the original AWS account. Each AWS Account is strongly segregated from the others, the resources in each account cannot be accessed by other accounts.
Imagine you have a single AWS Account where you have your domain
AWS Route53 hosted zone is created for that domain in AWS Account #1.
You have a Cloudfront distribution pointing to
example.com that hosts your website.
Now it’s time to create an API.
You want to create a separate AWS Account for a team that works on the API.
API should live under the same domain, at
Maybe you’ll even have multiple stages, like
But if you create a Route53 hosted zone for
api.example.com in the 2nd AWS account,
you’ll see that requests don’t end up in the 2nd AWS account.
They are getting resolved by the R53 hosted zone from the first account. This is good because if anybody could create a hosted zone for any domain they wish, they could redirect any DNS query to anywhere they want, which would be a huge security problem.
So we need a way to let the DNS know that there are DNS records for
api.example.com created somewhere else and they should be trusted.
First, A Non-Elegant Solution
The first solution that came to mind, is to have a load balancer deployed in Account #1 that will proxy the requests to the API Gateway deployed in a different account.
This solution is fine for a one-time setup, or if you know the number of domains won’t grow in the future. Otherwise, it has drawbacks:
- Account #1 needs to know about each deployed API in Account #2. It becomes a sort of God Class.
- Account #1 now needs to store all the TLS certificates for all the subdomains in a single place. Since AWS ACM supports only one-level deep wildcards, you’ll need to create a new certificate for each two-level subdomain.
- If something happens to the load balancer on Account #1 — all other accounts are impacted. It becomes a single point of failure.
- Whenever a team that works in Account #2 wants to change the mapping, they need to contact the team that manages Account #1 to make the changes.
A Better Approach
A better approach would be to “link” two Route53 hosted zones via a Name Servers DNS record. Let me explain.
Step #1: Create a Route53 hosted zone in AWS Account #2
In our case, we want to create a hosted zone for the
Whenever you create a new Route53 hosted zone, it creates an NS (name servers) DNS record inside automatically. It’s a record with 4 values inside, for example:
Step #2: Create an NS record in the “parent” domain
Now we need to let DNS know, that all the DNS queries to the
records should be handled by a different AWS Route53 hosted zone.
The way to do it is to create an identical DNS NS record in the “parent” hosted zone,
the one in AWS Account #1 which owns the
Step #3: Provision infrastructure in AWS Account #2 assuming full ownership of all the DNS records
Now you can create DNS records in the 2nd AWS Account under
*.api.example.com assuming you have full control over it!
- Request an ACM certificate for TLS for your API Gateway.
- Create Alias DNS records from
beta.api.example.comthat points to your API Gateway.
The approach described allows you to share a common top-level domain across multiple AWS Accounts with loose coupling. AWS Accounts have full access to a hosted zone for a subdomain that was shared with them through a parent account.
Beware, that there is a soft limit of 10,000 records per hosted zone. But it’s a soft limit, that can be raised through a support ticket to AWS.
As stated in the AWS documentation:
There’s a small performance impact to this configuration for the first DNS query from each DNS resolver. The resolver must get information from the hosted zone for the root domain and then get information from the hosted zone for the subdomain.