Blog
Sharing AWS Route 53 Resolver Endpoints Across Accounts with Terraform and RAM
Back in February, we talked about Route 53 Resolvers, an exciting new announcement from re:Invent in 2018. Since, AWS added support for using Resource Access Manager to share resolver rules across accounts, which can help offset the somewhat painful costs associated with Route 53 Resolvers.
In our original post, we demonstrated using Route 53 resolvers in CloudFormation. This post will consider a similar example in Terraform and demonstrate using RAM.
Inbound Resolvers
Inbound resolvers allow you to resolve private Route 53 zones from outside of your VPC–whether that be your office, a data center, or another VPC (perhaps in another account). Typically when using inbound resolvers, you’ll want to explicitly specify the IPs to use. This will ensure they remain constant if you need to recreate the endpoint at some point. You’ll need an inbound resolver endpoint for each VPC you want to be able to resolve private zones in.
The following Terraform code will give you an inbound resolver on two fixed IPs, and allow the 192.168.100.0/24
CIDR access to it. Please note that any CIDRs you give access will need to be able to reach the VPC, based on your routing tables, network ACLs and other VPC settings.
module "route53-inbound" { source = "git::https://github.com/rhythmictech/terraform-aws-route53-endpoint" allowed_resolvers = ["192.168.100.0/24"] direction = "inbound" vpc_id = "vpc-1234567" ip_addresses = [ { ip = "172.30.1.10" subnet_id = "subnet-1234567a" }, { ip = "172.30.2.10" subnet_id = "subnet-1234567b" } ] }
If you are going to use inbound zones extensively, it may be beneficial to centralize your private zones in a single VPC. You can use outbound resolvers to ensure other VPCs can resolve them and use cross-account IAM roles to give your other AWS accounts and VPCs the ability to update the zone.
Outbound Resolvers
Outbound resolvers allow you to direct queries for domains to the resolvers of your choice. Outbound endpoints can be shared across many VPCs in an account, unlike inbound resolvers. As with the inbound resolvers, you will need to ensure your networking and routing allow connectivity to the outbound resolvers you’ve specified. Depending on your security settings, you may wish to explicitly specify the IPs to use. This is not a requirement, though.
The following Terraform code creates an outbound resolver:
module "route53-outbound" { source = "git::https://github.com/rhythmictech/terraform-aws-route53-endpoint" allowed_resolvers = ["192.168.100.10/32", "192.168.100.11/32"] direction = "outbound" vpc_id = "vpc-1234567" ip_addresses = [ { subnet_id = "subnet-1234567a" }, { subnet_id = "subnet-1234567b" } ] }
Of course, this resolver doesn’t do much good right now, as no forwarding rules are configured. The following Terraform code creates a rule, which will catch requests for ad.mycompany.com
and forward them to the designated IP addresses. Each IP address you list must be specified as an allowed_resolver
in the outbound endpoint.
module "route53-rule-ad-corp" { source = "git::https://github.com/rhythmictech/terraform-aws-route53-resolver-rule" associated_vpcs = ["vpc-1234567"] forward_domain = "ad.mycompany.com." forward_ips = ["192.168.100.10", "192.168.100.11"] resolver_endpoint = module.route53-outbound.endpoint_id }
You can have an arbitrary number of resolver rules, though the default limit is 10.
Cross-Account Resolver Endpoints
Outbound resolvers can be shared across accounts using Resource Access Manager (RAM). Somewhat unintuitively, the rules themselves are what you share, not the endpoint itself. By adding the resource_share_accounts
variable to your rule, the appropriate RAM resources are created automatically. You don’t need to take any further action in the target accounts to accept or utilize the resolver rule. It is automatically available.
module "route53-rule-ad-corp" { source = "git::https://github.com/rhythmictech/terraform-aws-route53-resolver-rule" associated_vpcs = ["vpc-1234567"] forward_domain = "ad.mycompany.com." forward_ips = ["192.168.100.10", "192.168.100.11"] resolver_endpoint = module.route53-outbound.endpoint_id resource_share_accounts = ["123456789012"] }
The code above will fail if you do not have RAM enabled for your account. It is disabled by default.
Using Resolver Endpoints Effectively
Resolver endpoints are powerful but can also allow you to hang onto an overdependence on global DNS resolution. In many cases, constructs like service discovery and application meshes can provide an alternative way to reach services across accounts and environments. That said, DNS is still a valid and appropriate way to find things in the cloud, and we use resolver endpoints on most accounts we manage.
Because it is DNS, it becomes very difficult to extricate yourself from a botched resolver config after the fact. There’s simply no graceful way to reconfigure the endpoint since most changes require deleting and recreating it. So plan your strategy out across all accounts, before you get started.
The resolvers are also quite expensive, roughly $180/month for an inbound or outbound endpoint. If you have a number of accounts, using RAM for your forwarding rules is a must. On the inbound side, you may find yourself needing to create an inbound endpoint for every account. Don’t sweat it, but do your best to ensure you can at least avoid one for every VPC by strategically placing your private zones.