TerraformPilot

Terraform

AWS Route53 DNS with Terraform - Complete Guide

Manage AWS Route53 DNS with Terraform. Create hosted zones, DNS records, alias records, health checks, and failover routing policies.

LLuca Berton1 min read

Quick Answer

#
resource "aws_route53_zone" "main" {
  name = "example.com"
}
 
resource "aws_route53_record" "www" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.example.com"
  type    = "A"
  alias {
    name                   = aws_lb.main.dns_name
    zone_id                = aws_lb.main.zone_id
    evaluate_target_health = true
  }
}

Hosted Zone

#
resource "aws_route53_zone" "main" {
  name    = var.domain_name
  comment = "Managed by Terraform"
}
 
output "nameservers" {
  value       = aws_route53_zone.main.name_servers
  description = "Update your domain registrar with these NS records"
}

Common DNS Records

#
# A record (IP address)
resource "aws_route53_record" "app" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "app.${var.domain_name}"
  type    = "A"
  ttl     = 300
  records = ["1.2.3.4"]
}
 
# CNAME record
resource "aws_route53_record" "blog" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "blog.${var.domain_name}"
  type    = "CNAME"
  ttl     = 300
  records = ["mysite.netlify.app"]
}
 
# MX records (email)
resource "aws_route53_record" "mx" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "MX"
  ttl     = 3600
  records = [
    "1 aspmx.l.google.com",
    "5 alt1.aspmx.l.google.com",
    "5 alt2.aspmx.l.google.com",
  ]
}
 
# TXT record (SPF, DKIM, verification)
resource "aws_route53_record" "spf" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "TXT"
  ttl     = 300
  records = ["v=spf1 include:_spf.google.com ~all"]
}

Alias Records (AWS Services)

#
# ALB alias
resource "aws_route53_record" "alb" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"
  alias {
    name                   = aws_lb.main.dns_name
    zone_id                = aws_lb.main.zone_id
    evaluate_target_health = true
  }
}
 
# CloudFront alias
resource "aws_route53_record" "cdn" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"
  alias {
    name                   = aws_cloudfront_distribution.cdn.domain_name
    zone_id                = aws_cloudfront_distribution.cdn.hosted_zone_id
    evaluate_target_health = false
  }
}
 
# S3 website alias
resource "aws_route53_record" "s3" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "static.${var.domain_name}"
  type    = "A"
  alias {
    name                   = aws_s3_bucket_website_configuration.main.website_domain
    zone_id                = aws_s3_bucket.main.hosted_zone_id
    evaluate_target_health = false
  }
}

Health Checks

#
resource "aws_route53_health_check" "primary" {
  fqdn              = "primary.${var.domain_name}"
  port               = 443
  type               = "HTTPS"
  resource_path      = "/health"
  failure_threshold  = 3
  request_interval   = 30
 
  tags = { Name = "primary-health-check" }
}

Failover Routing

#
resource "aws_route53_record" "primary" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"
  set_identifier = "primary"
 
  failover_routing_policy {
    type = "PRIMARY"
  }
 
  alias {
    name                   = aws_lb.primary.dns_name
    zone_id                = aws_lb.primary.zone_id
    evaluate_target_health = true
  }
 
  health_check_id = aws_route53_health_check.primary.id
}
 
resource "aws_route53_record" "secondary" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"
  set_identifier = "secondary"
 
  failover_routing_policy {
    type = "SECONDARY"
  }
 
  alias {
    name                   = aws_lb.secondary.dns_name
    zone_id                = aws_lb.secondary.zone_id
    evaluate_target_health = true
  }
}

Weighted Routing (A/B Testing)

#
resource "aws_route53_record" "v1" {
  zone_id        = aws_route53_zone.main.zone_id
  name           = "api.${var.domain_name}"
  type           = "A"
  set_identifier = "v1"
  weighted_routing_policy { weight = 90 }
  alias {
    name    = aws_lb.v1.dns_name
    zone_id = aws_lb.v1.zone_id
    evaluate_target_health = true
  }
}
 
resource "aws_route53_record" "v2" {
  zone_id        = aws_route53_zone.main.zone_id
  name           = "api.${var.domain_name}"
  type           = "A"
  set_identifier = "v2"
  weighted_routing_policy { weight = 10 }
  alias {
    name    = aws_lb.v2.dns_name
    zone_id = aws_lb.v2.zone_id
    evaluate_target_health = true
  }
}

DNS Record Types

#
TypePurposeExample
AIPv4 address1.2.3.4
AAAAIPv6 address2001:db8::1
CNAMECanonical nameapp.example.comlb.aws.com
MXMail exchange10 mail.example.com
TXTText (SPF, DKIM)v=spf1 include:...
NSName serversDelegated zone
AliasAWS-native (free)ALB, CloudFront, S3
#

Conclusion

#

Use alias records (free, no TTL) for AWS services instead of CNAME. Add health checks with failover routing for high availability. Use weighted routing for blue-green deployments. Always output nameservers after creating a hosted zone so you can update the registrar.

#Terraform#AWS#Route53#DNS#Infrastructure as Code

Share this article