Managing AWS Route 53 with Terraform

A comprehensive guide to managing DNS records and routing policies with AWS Route 53 using Terraform

Managing AWS Route 53 with Terraform

AWS Route 53 is a highly available and scalable Domain Name System (DNS) web service. This guide shows how to manage Route 53 resources using Terraform.

Prerequisites

  • AWS CLI configured
  • Terraform installed
  • Registered domain name (optional)
  • Basic understanding of DNS concepts

Project Structure

aws-route53-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfvars

Basic Route 53 Configuration

# main.tf
provider "aws" {
  region = var.aws_region
}

# Hosted Zone
resource "aws_route53_zone" "main" {
  name = var.domain_name
  
  tags = {
    Environment = var.environment
  }
}

# A Record
resource "aws_route53_record" "www" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"
  ttl     = "300"
  records = [var.ip_address]
}

# CNAME Record
resource "aws_route53_record" "subdomain" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "blog.${var.domain_name}"
  type    = "CNAME"
  ttl     = "300"
  records = ["${var.cname_target}"]
}

Alias Records for AWS Services

# Alias record for CloudFront distribution
resource "aws_route53_record" "cloudfront" {
  zone_id = aws_route53_zone.main.zone_id
  name    = var.domain_name
  type    = "A"

  alias {
    name                   = var.cloudfront_domain_name
    zone_id               = var.cloudfront_hosted_zone_id
    evaluate_target_health = false
  }
}

# Alias record for ALB
resource "aws_route53_record" "alb" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.${var.domain_name}"
  type    = "A"

  alias {
    name                   = var.alb_dns_name
    zone_id               = var.alb_zone_id
    evaluate_target_health = true
  }
}

Health Checks

resource "aws_route53_health_check" "web" {
  fqdn              = "www.${var.domain_name}"
  port              = 80
  type              = "HTTP"
  resource_path     = "/"
  failure_threshold = "3"
  request_interval  = "30"

  tags = {
    Name = "web-health-check"
  }
}

# Record with health check
resource "aws_route53_record" "www_with_health_check" {
  zone_id         = aws_route53_zone.main.zone_id
  name            = "www.${var.domain_name}"
  type            = "A"
  health_check_id = aws_route53_health_check.web.id
  
  failover_routing_policy {
    type = "PRIMARY"
  }

  set_identifier = "primary"
  records        = [var.primary_ip]
  ttl           = 60
}

Routing Policies

1. Weighted Routing

resource "aws_route53_record" "www_weighted" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  weighted_routing_policy {
    weight = 90
  }

  set_identifier = "primary"
  records        = [var.primary_ip]
  ttl           = 60
}

resource "aws_route53_record" "www_weighted_secondary" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  weighted_routing_policy {
    weight = 10
  }

  set_identifier = "secondary"
  records        = [var.secondary_ip]
  ttl           = 60
}

2. Latency-Based Routing

resource "aws_route53_record" "www_latency" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  latency_routing_policy {
    region = "us-west-2"
  }

  set_identifier = "us-west-2"
  records        = [var.us_west_2_ip]
  ttl           = 60
}

resource "aws_route53_record" "www_latency_eu" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  latency_routing_policy {
    region = "eu-west-1"
  }

  set_identifier = "eu-west-1"
  records        = [var.eu_west_1_ip]
  ttl           = 60
}

3. Geolocation Routing

resource "aws_route53_record" "www_geo" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  geolocation_routing_policy {
    country = "US"
  }

  set_identifier = "us"
  records        = [var.us_ip]
  ttl           = 60
}

resource "aws_route53_record" "www_geo_default" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  geolocation_routing_policy {
    country = "*"
  }

  set_identifier = "default"
  records        = [var.default_ip]
  ttl           = 60
}

Variables Configuration

# variables.tf
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

variable "domain_name" {
  description = "Domain name"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "production"
}

variable "ip_address" {
  description = "IP address for A record"
  type        = string
}

variable "cname_target" {
  description = "Target for CNAME record"
  type        = string
}

Best Practices

  1. DNS Management

    • Use meaningful TTL values
    • Implement proper health checks
    • Use alias records for AWS services
    • Consider using private hosted zones for internal resources
  2. Security

    • Enable DNSSEC for additional security
    • Use proper IAM policies
    • Implement logging with CloudWatch
  3. High Availability

    • Use multiple records with health checks
    • Implement failover routing when needed
    • Consider multi-region setups
  4. Cost Optimization

    • Monitor query logs
    • Use appropriate routing policies
    • Clean up unused health checks

Monitoring Configuration

resource "aws_cloudwatch_metric_alarm" "dns_queries" {
  alarm_name          = "${var.domain_name}-dns-queries"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name         = "DNSQueries"
  namespace           = "AWS/Route53"
  period             = "300"
  statistic          = "Sum"
  threshold          = "1000"
  alarm_description  = "DNS queries exceeded threshold"
  alarm_actions      = [var.sns_topic_arn]

  dimensions = {
    HostedZoneId = aws_route53_zone.main.zone_id
  }
}

Deployment Steps

  1. Initialize Terraform:
terraform init
  1. Plan the deployment:
terraform plan
  1. Apply the configuration:
terraform apply

Clean Up

Remove all resources when done:

terraform destroy

Common Use Cases

  1. Multi-Region Application
resource "aws_route53_record" "api_failover" {
  zone_id = aws_route53_zone.main.zone_id
  name    = "api.${var.domain_name}"
  type    = "A"

  failover_routing_policy {
    type = "PRIMARY"
  }

  alias {
    name                   = var.primary_alb_dns
    zone_id               = var.primary_alb_zone_id
    evaluate_target_health = true
  }

  set_identifier = "primary"
}
  1. Blue-Green Deployment
resource "aws_route53_record" "www_weighted" {
  count = 2

  zone_id = aws_route53_zone.main.zone_id
  name    = "www.${var.domain_name}"
  type    = "A"

  weighted_routing_policy {
    weight = count.index == 0 ? 100 : 0
  }

  alias {
    name                   = var.environment_alb_dns[count.index]
    zone_id               = var.environment_alb_zone_id[count.index]
    evaluate_target_health = true
  }

  set_identifier = count.index == 0 ? "blue" : "green"
}

Conclusion

This setup provides a comprehensive foundation for managing Route 53 resources using Terraform. Remember to:

  • Plan your DNS architecture carefully
  • Implement proper health checks and monitoring
  • Use appropriate routing policies for your use case
  • Keep your DNS records organized and documented
  • Version control your Terraform configurations

The complete code can be customized based on your specific requirements and use cases.