Skip to content

Repository files navigation

AWS Route53 Terraform Module

Terraform AWS Provider License AI Ready

⭐ If you find this module useful, star it on GitHub — it helps a lot!

OverviewFeaturesUsageExamplesAPI Reference


A comprehensive Terraform module for managing AWS Route53 hosted zones and DNS records with an advanced HTTPS web redirect feature. This module simplifies DNS management while providing powerful capabilities like automatic SSL certificate provisioning and CloudFront-based redirects.

Overview

This module creates and manages AWS Route53 resources with a focus on flexibility and ease of use. It supports all common DNS record types and includes a unique records_wr (web redirect) feature that automatically provisions the complete infrastructure needed for HTTPS redirects:

  • 🔐 Automatic SSL certificates via AWS Certificate Manager
  • 🌐 CloudFront distribution for global edge delivery
  • 📦 Private S3 bucket as a CloudFront origin placeholder (no data ever stored or served)
  • DNS validation handled automatically

Features

  • Comprehensive DNS Record Support - A, AAAA, CNAME, MX, TXT, NS, and CAA records
  • HTTPS Web Redirects - Complete redirect infrastructure with a single configuration
  • Automatic SSL Certificates - ACM certificates provisioned and validated automatically
  • CloudFront Integration - Global edge delivery for web redirects
  • Flexible TTL Configuration - Customize TTL values per record type
  • Multi-Provider Architecture - Proper handling of us-east-1 requirements for CloudFront certificates

Usage

Basic Setup

First, you need to configure two AWS providers - one for your primary region and one for us-east-1 (required for CloudFront ACM certificates):

provider "aws" {
  region = "us-west-2"
}

provider "aws" {
  alias  = "acm"
  region = "us-east-1"
}

Then use the module:

module "example_com" {
  source = "locus313/route53/aws"
  
  # Pass both providers
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  records_a = {
    "example.com" = ["12.23.45.56"]
  }
  
  records_cname = {
    "www.example.com" = ["example.com"]
  }
}

Important

The aws.acm provider must point to us-east-1 for CloudFront certificate provisioning. This is an AWS requirement, not a module limitation.

Examples

Simple DNS Records

Create a basic hosted zone with A and CNAME records:

module "simple_dns" {
  source = "locus313/route53/aws"
  
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  records_a = {
    "example.com"     = ["203.0.113.10"]
    "api.example.com" = ["203.0.113.20"]
  }
  
  records_cname = {
    "www.example.com"  = ["example.com"]
    "blog.example.com" = ["example.com"]
  }
}

Email Configuration

Set up MX, SPF, and DKIM records for email:

module "email_dns" {
  source = "locus313/route53/aws"
  
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  records_mx = {
    "example.com" = [
      "10 aspmx.l.google.com",
      "20 alt1.aspmx.l.google.com",
      "30 alt2.aspmx.l.google.com"
    ]
  }
  
  records_txt = {
    "example.com" = ["v=spf1 include:_spf.google.com ~all"]
    "google._domainkey.example.com" = [
      "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQ..."
    ]
  }
  
  records_caa = {
    "example.com" = ["0 issue \"letsencrypt.org\""]
  }
}

HTTPS Web Redirects

Create HTTPS redirects with automatic SSL certificates:

module "redirects" {
  source = "locus313/route53/aws"
  
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  # Automatically creates:
  # - S3 bucket with redirect configuration
  # - CloudFront distribution
  # - ACM certificate (validated via DNS)
  # - Route53 A record (CloudFront alias)
  records_wr = {
    "support.example.com" = "https://example.atlassian.net/servicedesk"
    "docs.example.com"    = "https://documentation.example.com"
    "status.example.com"  = "https://status.example.com/incidents"
  }
}

Subdomain Delegation

Delegate subdomains to other AWS accounts or hosted zones:

module "parent_zone" {
  source = "locus313/route53/aws"
  
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  records_ns = {
    "dev.example.com" = [
      "ns-1234.awsdns-12.org",
      "ns-5678.awsdns-34.com",
      "ns-9012.awsdns-56.net",
      "ns-3456.awsdns-78.co.uk"
    ]
    "staging.example.com" = [
      "ns-4321.awsdns-21.org",
      "ns-8765.awsdns-43.com"
    ]
  }
  
  # Custom TTL for NS records (default is 172800)
  ttl_ns = 86400
}

IPv6 Support

Configure both IPv4 and IPv6 addresses:

module "dual_stack" {
  source = "locus313/route53/aws"
  
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  records_a = {
    "example.com" = ["203.0.113.10"]
  }
  
  records_aaaa = {
    "example.com" = ["2001:0db8:85a3:0000:0000:8a2e:0370:7334"]
  }
}

Complete Configuration

A comprehensive example with multiple record types:

module "complete_example" {
  source = "locus313/route53/aws"
  
  providers = {
    aws.acm = aws.acm
  }
  
  primary_domain = "example.com"
  
  # Web redirects with HTTPS
  records_wr = {
    "support.example.com" = "https://example.atlassian.net/servicedesk"
  }
  
  # IPv4 addresses
  records_a = {
    "example.com"     = ["203.0.113.10"]
    "api.example.com" = ["203.0.113.20"]
  }
  
  # IPv6 addresses
  records_aaaa = {
    "example.com" = ["2001:0db8:85a3::8a2e:0370:7334"]
  }
  
  # Certificate Authority Authorization
  records_caa = {
    "example.com" = ["0 issue \"letsencrypt.org\""]
  }
  
  # Canonical names
  records_cname = {
    "www.example.com"      = ["example.com"]
    "calendar.example.com" = ["ghs.googlehosted.com"]
    "mail.example.com"     = ["ghs.googlehosted.com"]
  }
  
  # Mail exchange
  records_mx = {
    "example.com" = [
      "10 aspmx.l.google.com",
      "20 alt1.aspmx.l.google.com",
      "30 alt2.aspmx.l.google.com"
    ]
  }
  
  # Text records (SPF, DKIM, etc.)
  records_txt = {
    "example.com" = ["v=spf1 include:_spf.google.com ~all"]
    "google._domainkey.example.com" = [
      "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
    ]
  }
  
  # Subdomain delegation
  records_ns = {
    "dev.example.com" = [
      "ns-1234.awsdns-12.org",
      "ns-5678.awsdns-34.com"
    ]
  }
  
  # Custom TTL values
  ttl     = 3600   # Default records (1 hour)
  ttl_acm = 60     # ACM validation records (1 minute)
  ttl_ns  = 172800 # NS records (48 hours)
}

Tagging

Apply a common set of tags to every taggable resource the module creates (the hosted zone, and — when records_wr is set — the ACM certificate, CloudFront distribution, and S3 origin bucket):

module "example_com" {
  source = "locus313/route53/aws"

  providers = {
    aws.acm = aws.acm
  }

  primary_domain = "example.com"

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

API Reference

Requirements

Name Version
terraform >= 1.0
aws >= 4.0

Providers

Name Version
aws >= 4.0
aws.acm >= 4.0

Modules

No modules.

Resources

Name Type
aws_acm_certificate.records_wr resource
aws_acm_certificate_validation.records_wr resource
aws_cloudfront_distribution.records_wr resource
aws_cloudfront_function.records_wr resource
aws_cloudfront_origin_access_control.records_wr resource
aws_route53_record.records_a resource
aws_route53_record.records_aaaa resource
aws_route53_record.records_caa resource
aws_route53_record.records_cname resource
aws_route53_record.records_mx resource
aws_route53_record.records_ns resource
aws_route53_record.records_txt resource
aws_route53_record.records_wr resource
aws_route53_record.records_wr_validation resource
aws_route53_zone.this resource
aws_s3_bucket.records_wr resource
aws_s3_bucket_policy.records_wr resource
aws_s3_bucket_public_access_block.records_wr resource

Inputs

Name Description Type Default Required
enabled Whether to enable Route 53 resources bool true no
primary_domain The primary domain name to manage in Route 53 string n/a yes
records_a Map of A records (IPv4 addresses) map(list(string)) {} no
records_aaaa Map of AAAA records (IPv6 addresses) map(list(string)) {} no
records_caa Map of CAA records (Certificate Authority Authorization) map(list(string)) {} no
records_cname Map of CNAME records (canonical name aliases) map(list(string)) {} no
records_mx Map of MX records (mail exchange) map(list(string)) {} no
records_ns Map of NS records (name server delegation) map(list(string)) {} no
records_txt Map of TXT records (text records) map(list(string)) {} no
records_wr Map of web redirect records (domain -> full destination URL, e.g. https://example.com/path) map(string) {} no
tags A map of tags to apply to all taggable resources created by this module map(string) {} no
ttl Default TTL for all DNS records (in seconds) number 3600 no
ttl_acm TTL for ACM validation records (in seconds) number 60 no
ttl_ns TTL for NS records (in seconds) number 172800 no

Outputs

Name Description
records_wr_certificate_arns Map of records_wr domain to validated ACM certificate ARN
records_wr_cloudfront_distribution_domain_names Map of records_wr domain to CloudFront distribution domain name
records_wr_cloudfront_distribution_ids Map of records_wr domain to CloudFront distribution ID
records_wr_s3_bucket_names Map of records_wr domain to S3 origin bucket name
this_route53_zone_name_servers Name servers of Route53 zone
this_route53_zone_zone_id Zone ID of Route53 zone

Supported Record Types

Record Type Supported Description
A IPv4 addresses
AAAA IPv6 addresses
CAA Certificate Authority Authorization
CNAME Canonical name aliases
MX Mail exchange
NS Name server delegation
TXT Text records
LOC Location information
PTR Reverse DNS lookups
SOA Start of authority (managed by AWS)
SRV Service locator
SPF Use TXT records instead

Resources Created

For Standard DNS Records

  • aws_route53_zone - Hosted zone for the domain
  • aws_route53_record - DNS records of various types

For Web Redirects (records_wr)

  • aws_s3_bucket - Private placeholder bucket used only as a CloudFront origin (no objects stored)
  • aws_s3_bucket_public_access_block - Blocks all public access to the placeholder bucket
  • aws_s3_bucket_policy - Grants read access to the specific CloudFront distribution only (via OAC aws:SourceArn), denies non-HTTPS requests
  • aws_cloudfront_origin_access_control - OAC used to authenticate CloudFront to the private S3 origin
  • aws_cloudfront_function - Issues the HTTPS 301 redirect at the viewer-request stage, before the origin is contacted
  • aws_acm_certificate - SSL certificate (in us-east-1)
  • aws_acm_certificate_validation - DNS validation
  • aws_cloudfront_distribution - CDN for HTTPS redirect
  • aws_route53_record - DNS records for domain and validation

Development

Commit Message Convention

This project uses Conventional Commits for automated releases. Commit messages must follow this format:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Types that trigger releases:

  • feat: - New feature (triggers MINOR version bump, e.g., 1.2.0 → 1.3.0)
  • fix: - Bug fix (triggers PATCH version bump, e.g., 1.2.0 → 1.2.1)
  • feat!: or BREAKING CHANGE: - Breaking change (triggers MAJOR version bump, e.g., 1.2.0 → 2.0.0)

Other types (trigger PATCH version bump):

  • docs: - Documentation changes
  • chore: - Build process or auxiliary tool changes
  • refactor: - Code refactoring without changing functionality
  • test: - Adding or modifying tests
  • style: - Code style changes (formatting, whitespace)
  • perf: - Performance improvements

Examples:

# Patch release (1.2.3 → 1.2.4)
git commit -m "fix: correct CloudFront origin protocol policy"
git commit -m "docs: update README with new examples"

# Minor release (1.2.3 → 1.3.0)
git commit -m "feat: add support for Route53 health checks"
git commit -m "feat(dns): add PTR record type support"

# Major release (1.2.3 → 2.0.0)
git commit -m "feat!: change records_wr to accept object instead of string"
# or
git commit -m "feat: redesign module structure

BREAKING CHANGE: module now requires AWS provider >= 5.0"

Note

Releases are automatically created when *.tf files are changed on the main branch. See CONTRIBUTING.md for more details.

Running Tests

This module includes both Go-based Terratest and compliance tests:

# Run fast unit tests (no AWS credentials needed)
cd test
go test -v ./...

# Run Terratest integration tests (requires AWS credentials)
cd test
go test -v -tags=integration -timeout 30m

# Run compliance tests
terraform-compliance -f compliance -p .

Local Development

# Initialize and validate
terraform init
terraform validate

# Test with the example configuration
cd example
terraform init
terraform plan

Important Notes

Note

This module creates exactly one hosted zone per invocation. All DNS records are created within that zone.

Warning

The records_wr feature creates multiple AWS resources (S3, CloudFront, ACM, Route53) which may incur costs. CloudFront distributions can take 15-20 minutes to deploy.

Tip

After creating a hosted zone, update your domain registrar's name servers to the values in this_route53_zone_name_servers output.

Contributing

Contributions are welcome! See CONTRIBUTING.md for the commit message convention, development workflow, and how to run tests.

Author

Module created and maintained by Patrick Lewis.

About

Terraform module for AWS Route53 hosted zones with advanced web redirect feature. Creates S3→CloudFront→ACM→Route53 stack for HTTPS redirects. Supports all DNS record types with configurable TTLs.

Topics

Resources

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages