The Terraform AWS Central DNS module provides a comprehensive solution for managing centralized DNS resolution across AWS Organizations. This module creates and manages Route 53 Resolver endpoints, rules, and AWS Resource Access Manager (RAM) shares to enable seamless DNS resolution between private hosted zones across multiple AWS accounts and organizational units.
In large AWS Organizations with multiple accounts and organizational units, DNS resolution becomes increasingly complex:
- Fragmented DNS Resolution: Each account maintains its own private hosted zones, creating isolated DNS namespaces
- Cross-Account DNS Challenges: Applications in different accounts cannot resolve internal domain names from other accounts
- Complex DNS Management: Managing DNS rules and forwarding across multiple accounts requires significant operational overhead
- Security and Compliance: Ensuring secure DNS resolution while maintaining proper access controls across organizational boundaries
- Scalability Issues: As organizations grow, DNS management becomes increasingly complex and error-prone
This module provides a centralized, scalable DNS solution that:
- Centralizes DNS Management: Creates a single point of DNS resolution for the entire organization
- Enables Cross-Account Resolution: Allows private hosted zones to resolve DNS queries across account boundaries
- Simplifies DNS Operations: Provides a unified interface for managing DNS rules and forwarding
- Ensures Security: Implements proper IAM controls and network security for DNS traffic
- Scales with Organization: Automatically shares DNS rules with specified organizational units via AWS RAM
- Route 53 Resolver Endpoints: Creates outbound resolver endpoints for DNS forwarding
- Cross-Account DNS Support: Enables DNS resolution between private hosted zones across accounts
- Multi-Protocol Support: Supports both Do53 (DNS over port 53) and DoH (DNS over HTTPS) protocols
- IPv4/IPv6 Support: Configurable IP address types (IPv4, IPv6, or dual-stack)
- VPC Creation or Reuse: Option to create new VPC or reuse existing network infrastructure
- Transit Gateway Integration: Seamless integration with AWS Transit Gateway for multi-VPC connectivity
- IPAM Support: Integration with AWS IP Address Manager for IP allocation
- Multi-AZ Deployment: Ensures high availability across multiple availability zones
- AWS RAM Integration: Shares DNS resolver rules with organizational units via Resource Access Manager
- Principals Management: Configurable sharing with specific OUs, accounts, or external principals
- Access Control: Granular control over which resources can access specific DNS rules
- External Principal Support: Optional support for sharing with external AWS accounts
- Rule Groups: Organize DNS rules into logical groups for better management
- Custom Targets: Configurable DNS forwarding targets (defaults to VPC DNS resolver)
- Zone Associations: Associate Route 53 private hosted zones with the resolver VPC
- Rule Types: Support for different resolver rule types (currently FORWARD)
- Security Groups: Automatically configured security groups for DNS traffic
- Network Isolation: DNS traffic restricted to private subnets and internal networks
- IAM Integration: Proper IAM roles and permissions for all resources
- Audit Trail: Comprehensive logging and monitoring capabilities
- Terraform State Management: Full Terraform state management for all resources
- Resource Tagging: Consistent tagging across all created resources
- Output Management: Comprehensive outputs for integration with other modules
- Dependency Management: Proper resource dependencies and lifecycle management
graph TB
subgraph "Central DNS Account"
A[Route 53 Resolver Endpoint]
B[DNS Security Group]
C[VPC with Private Subnets]
D[Route 53 Resolver Rules]
E[AWS RAM Resource Shares]
end
subgraph "Spoke Account 1"
F[Private Hosted Zone 1]
G[Application Workloads]
end
subgraph "Spoke Account 2"
H[Private Hosted Zone 2]
I[Application Workloads]
end
subgraph "Spoke Account N"
J[Private Hosted Zone N]
K[Application Workloads]
end
A --> D
D --> E
E --> F
E --> H
E --> J
G --> A
I --> A
K --> A
A --> B
A --> C
- DNS Query Initiation: Application in spoke account initiates DNS query
- Resolver Endpoint: Query is forwarded to central DNS resolver endpoint
- Rule Processing: Resolver applies configured rules to determine forwarding target
- Cross-Account Resolution: Query is forwarded to appropriate private hosted zone
- Response Routing: DNS response is routed back through the resolver to originating application
- Central VPC: Houses the Route 53 Resolver endpoint and associated resources
- Private Subnets: Resolver endpoints deployed across multiple AZs for high availability
- Security Groups: Restrict DNS traffic to internal networks (10.0.0.0/8)
- Transit Gateway: Optional connectivity to spoke VPCs for broader DNS resolution
module "central_dns" {
source = "appvia/dns/aws"
version = "0.1.0"
resolver_name = "central-dns-resolver"
tags = {
Environment = "production"
ManagedBy = "terraform"
Purpose = "central-dns"
}
resolver_rule_groups = [
{
ram_share_name = "internal-domains"
ram_principals = {
"Development" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-dev-123"
"Production" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-prod-456"
}
rules = [
{
name = "internal-corp"
domain = "corp.internal"
},
{
name = "development"
domain = "dev.internal"
}
]
}
]
network = {
availability_zones = 2
transit_gateway_id = var.transit_gateway_id
private_netmask = 24
vpc_cidr = "10.90.0.0/21"
}
}
# Create network separately for reuse
module "dns_network" {
source = "appvia/network/aws"
version = "0.6.10"
availability_zones = 3
name = "central-dns-network"
vpc_cidr = "10.90.0.0/21"
transit_gateway_id = var.transit_gateway_id
tags = var.tags
}
module "central_dns" {
source = "appvia/dns/aws"
version = "0.1.0"
resolver_name = "enterprise-dns-resolver"
# Advanced resolver configuration
resolver_protocols = ["Do53", "DoH"]
resolver_endpoint_type = "DUALSTACK"
resolver_rule_groups = [
{
ram_share_name = "enterprise-domains"
ram_principals = {
"Infrastructure" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-infra-789"
"Applications" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-apps-012"
"Data" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-data-345"
}
rules = [
{
name = "enterprise-corp"
domain = "enterprise.corp"
targets = ["10.1.1.1", "10.1.1.2"] # Custom DNS servers
},
{
name = "development"
domain = "dev.enterprise.corp"
targets = [] # Use default VPC resolver
},
{
name = "production"
domain = "prod.enterprise.corp"
targets = ["10.2.1.1", "10.2.1.2"]
}
]
},
{
ram_share_name = "external-domains"
ram_principals = {
"All-Accounts" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-all-678"
}
rules = [
{
name = "external-forwarding"
domain = "external.company.com"
}
]
}
]
# Reuse existing network
network = {
create = false
vpc_id = module.dns_network.vpc_id
vpc_cidr = module.dns_network.vpc_cidr
private_subnet_ids = module.dns_network.private_subnet_ids
transit_gateway_id = var.transit_gateway_id
}
# Associate additional private hosted zones
route53_zone_ids = [
"Z1234567890ABC", # Private hosted zone from another account
"Z0987654321DEF" # Another private hosted zone
]
tags = var.tags
}
# For large enterprises with complex organizational structures
module "enterprise_dns" {
source = "appvia/dns/aws"
resolver_name = "enterprise-central-dns"
resolver_rule_groups = [
{
ram_share_name = "business-domains"
ram_principals = {
"Finance" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-finance"
"HR" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-hr"
"Engineering" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-engineering"
}
rules = [
{
name = "corporate"
domain = "corp.company.com"
},
{
name = "internal"
domain = "internal.company.com"
}
]
}
]
network = {
availability_zones = 3
transit_gateway_id = var.enterprise_tgw_id
vpc_cidr = "10.100.0.0/20"
}
tags = {
Environment = "production"
BusinessUnit = "IT"
Compliance = "required"
}
}
# For development and testing environments
module "dev_dns" {
source = "appvia/dns/aws"
resolver_name = "dev-central-dns"
resolver_rule_groups = [
{
ram_share_name = "dev-domains"
ram_principals = {
"Dev-Accounts" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-dev"
}
rules = [
{
name = "dev-internal"
domain = "dev.internal"
},
{
name = "test"
domain = "test.internal"
}
]
}
]
network = {
availability_zones = 2
vpc_cidr = "10.200.0.0/22"
}
tags = {
Environment = "development"
Purpose = "dns-resolution"
}
}
# For organizations with strict compliance requirements
module "compliance_dns" {
source = "appvia/dns/aws"
resolver_name = "compliance-dns-resolver"
# Enhanced security with IPv6 support
resolver_protocols = ["Do53", "DoH"]
resolver_endpoint_type = "DUALSTACK"
resolver_rule_groups = [
{
ram_share_name = "compliance-domains"
ram_principals = {
"Compliance-Accounts" = "arn:aws:organizations::123456789012:ou/o-abc123/ou-compliance"
}
rules = [
{
name = "secure-corp"
domain = "secure.corp"
targets = ["10.1.1.1", "10.1.1.2"] # Dedicated secure DNS servers
}
]
}
]
network = {
availability_zones = 3
vpc_cidr = "10.50.0.0/20"
ipam_pool_id = var.compliance_ipam_pool_id
}
tags = {
Environment = "production"
Compliance = "required"
DataClassification = "confidential"
AuditRequired = "true"
}
}
The module creates comprehensive monitoring capabilities:
# View Route 53 Resolver logs
aws logs describe-log-groups --log-group-name-prefix "/aws/route53resolver"
# Monitor DNS query metrics
aws cloudwatch get-metric-statistics \
--namespace "AWS/Route53Resolver" \
--metric-name "Queries" \
--dimensions Name=ResolverEndpointId,Value=rslvr-out-1234567890abcdef \
--start-time 2023-01-01T00:00:00Z \
--end-time 2023-01-02T00:00:00Z \
--period 3600 \
--statistics Sum
Metric | Description | Use Case |
---|---|---|
Queries |
Number of DNS queries processed | Monitor DNS traffic volume |
QueriesBlocked |
Number of blocked queries | Security monitoring |
QueriesForwarded |
Number of forwarded queries | Performance monitoring |
QueriesProcessed |
Total queries processed | Overall health |
Error: DNS resolution timeout for domain.example.com
Solutions:
- Verify security group rules allow DNS traffic (ports 53 TCP/UDP)
- Check that resolver rules are properly configured
- Ensure target DNS servers are reachable
- Verify RAM resource shares are accepted by target accounts
Error: Resource share not found or access denied
Solutions:
- Verify principal ARNs are correct and valid
- Ensure RAM resource shares are accepted in target accounts
- Check IAM permissions for RAM operations
- Verify organization structure and OU IDs
Error: Resolver endpoint not reachable
Solutions:
- Verify VPC and subnet configuration
- Check Transit Gateway attachments and routing
- Ensure security groups allow DNS traffic
- Verify network ACLs don't block DNS traffic
# Test DNS resolution from EC2 instance
dig @10.90.0.2 domain.example.com
# Test with nslookup
nslookup domain.example.com 10.90.0.2
# Test from within VPC
nslookup domain.example.com
- Monitor DNS Performance: Set up CloudWatch alarms for query latency and error rates
- Regular Health Checks: Implement automated DNS resolution tests
- Security Auditing: Regularly review RAM shares and access permissions
- Capacity Planning: Monitor query volumes and scale accordingly
- Backup and Recovery: Document resolver configurations for disaster recovery
- AWS Organizations enabled
- Appropriate IAM permissions for Route 53, RAM, and VPC operations
- Transit Gateway (optional, for multi-VPC connectivity)
- IPAM pool (optional, for IP address management)
- Route 53 Resolver
- AWS Resource Access Manager (RAM)
- Amazon VPC
- AWS Security Groups
- AWS Transit Gateway (optional)
- AWS IPAM (optional)
- Private subnets in multiple availability zones
- Security groups allowing DNS traffic (ports 53 TCP/UDP)
- Route tables configured for DNS traffic routing
- Optional: Transit Gateway for cross-VPC connectivity
The terraform-docs
utility is used to generate this README. Follow the below steps to update:
- Make changes to the
.terraform-docs.yml
file - Fetch the
terraform-docs
binary (https://terraform-docs.io/user-guide/installation/) - Run
terraform-docs markdown table --output-file ${PWD}/README.md --output-mode inject .
Name | Version |
---|---|
aws | >= 5.0.0 |
Name | Description | Type | Default | Required |
---|---|---|---|---|
network | The network to use for the endpoints and optinal resolvers | object({ |
n/a | yes |
resolver_name | Name of the Route53 resolver endpoint | string |
n/a | yes |
tags | Map of tags to apply to resources created by this module | map(string) |
n/a | yes |
resolver_endpoint_type | The Route 53 Resolver endpoint IP address type. Valid values: IPV4, IPV6, DUALSTACK. | string |
"IPV4" |
no |
resolver_protocols | List of protocols that the Route53 Outbound Resolver should support | list(string) |
[ |
no |
resolver_rule_groups | Map of Route53 Resolver Rules by group. Every rule in each group can be shared with principals via AWS RAM. | list(object({ |
[] |
no |
route53_zone_ids | List of Route53 Zone IDs to be associated with the resolver VPC. | list(string) |
[] |
no |
Name | Description |
---|---|
all_resolver_rules | Map of all resolver rules. |
endpoint | Details of the Route53 Outbound Resolver endpoint. |
resource_shares | Map of AWS RAM Shares by group. |
rules | Map of resolver rules by group. |