Terraform aws_route53_record Resource for AWS Route 53 DNS Management

Terraform’s aws_route53_record resource provides declarative control over DNS records in Amazon Route 53 hosted zones. The resource maps directly to the AWS Route 53 API and allows infrastructure teams to create, update, and delete records for A, CNAME, TXT, MX, and other record types from code. A minimal configuration can start with a name requirement, but production usage involves zone targeting, TTL management, alias integration, and routing policy blocks for weighted, failover, geolocation, latency, and CIDR-based traffic steering.

The resource is central to DNS as code workflows. Teams use it to bind application endpoints to load balancers, provision certificate validation records, generate multiple subdomains from a map, and import legacy records into Terraform state without recreation.

Resource Anatomy and Required Arguments

The aws_route53_record resource manages a single Route 53 record within a hosted zone.

resource "aws_route53_record" "example" { name = "my-route53-record" }

The name argument is required and defines the DNS name for the record. In practice the name is combined with the hosted zone to form the fully qualified domain name. The zone_id argument is required in all working configurations and identifies the Route 53 hosted zone that contains the record. The type argument is required and specifies the DNS record type such as A, CNAME, TXT, or MX.

For non-alias records, ttl and records are required. ttl sets the time-to-live in seconds that resolvers cache the record. records is a string list containing the target values for the record, for example IP addresses for an A record or a hostname for a CNAME record.

Alias records point to AWS resources such as an Elastic Load Balancer, Application Load Balancer, CloudFront distribution, or S3 website endpoint. For alias records the TTL is fixed at 60 seconds by Route 53 and the ttl argument must be omitted. The alias block replaces ttl and records with a reference to the AWS resource name and zone ID.

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

The impact of choosing alias versus standard records is significant for cost and health checking. Alias records incur no Route 53 query charges for the AWS resource and support health evaluation propagation from the target load balancer. Non-alias records incur query charges and require manual health checks if failover is desired.

Routing Policies and Traffic Steering

Route 53 supports multiple routing policies that are attached via nested blocks within aws_route53_record. Only one routing policy block may be supplied per record.

Weighted routing distributes traffic based on assigned weights. It is useful for blue-green deployments and canary releases.

resource "aws_route53_record" "primary" { zone_id = aws_route53_zone.primary.zone_id name = "app.example.com" type = "A" set_identifier = "primary" weighted_routing_policy { weight = 80 } alias { name = aws_lb.primary.dns_name zone_id = aws_lb.primary.zone_id evaluate_target_health = true } }

resource "aws_route53_record" "canary" { zone_id = aws_route53_zone.primary.zone_id name = "app.example.com" type = "A" set_identifier = "canary" weighted_routing_policy { weight = 20 } alias { name = aws_lb.canary.dns_name zone_id = aws_lb.canary.zone_id evaluate_target_health = true } }

The set_identifier must be unique per record name and type combination. The impact of weighted routing is the ability to shift traffic gradually without changing DNS names, reducing risk during releases.

Failover routing provides automatic primary-secondary failover with health checks.

resource "aws_route53_record" "primary_failover" { zone_id = aws_route53_zone.primary.zone_id name = "app.example.com" 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_failover" { zone_id = aws_route53_zone.primary.zone_id name = "app.example.com" 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 } }

Failover routing ensures service continuity when the primary endpoint becomes unhealthy. The health check ID ties Route 53 health monitoring to the record.

Additional routing policies supported by the module include geolocation, latency-based, and CIDR-based routing. The Terraform module boldlink/route53-records/aws advertises support for these policies with a single routing policy block allowed per record.

Module Interface and Flexible Configuration

The community module creates a Route 53 record with flexible configuration for various routing policies.

Example minimum usage:

module "minimum_example" { source = "boldlink/route53-records/aws" version = "insert_latest_version" zone_id = local.zone_id name = var.name type = var.type ttl = var.ttl records = var.records }

The module requires Terraform >= 0.14.11 and AWS provider >= 4.65.0. The module creates one resource aws_route53_record.main. Inputs include optional alias which conflicts with ttl and records. The allow_overwrite flag permits creation of a record in Terraform to overwrite an existing record if any.

The module adheres to security best practices by leveraging automated scanning with Checkov. The impact for operators is reduced configuration drift and standardized security checks across DNS records.

Certificate Validation and Dynamic Record Generation

ACME certificate validation in Route 53 requires TXT records created from domain validation options.

resource "aws_route53_record" "cert_validation" { for_each = { for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } allow_overwrite = true name = each.value.name records = [each.value.record] ttl = 60 type = each.value.type zone_id = aws_route53_zone.primary.zone_id }

resource "aws_acm_certificate_validation" "main" { certificate_arn = aws_acm_certificate.main.arn validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] }

The allow_overwrite = true setting prevents conflicts when validation records already exist. The impact is fully automated certificate issuance without manual DNS entry.

Managing Multiple Similar Records with for_each

For multiple similar records, for_each keeps configuration DRY.

variable "subdomains" { default = { "api" = "10.0.1.10" "admin" = "10.0.1.11" "staging" = "10.0.2.10" } }

resource "aws_route53_record" "subdomains" { for_each = var.subdomains zone_id = aws_route53_zone.primary.zone_id name = "${each.key}.example.com" type = "A" ttl = 300 records = [each.value] }

This pattern scales DNS management for microservices without duplicating resource blocks. Changing the map updates only affected records.

Importing Existing Records

If existing Route 53 records exist outside Terraform, they can be imported.

terraform import 'aws_route53_record.web' Z1234567890_example.com_A

terraform import 'aws_route53_record.primary' Z1234567890_app.example.com_A_primary

The import ID format is {zone_id}_{name}_{type}, with {set_identifier} appended for routing-policy records.

Importing prevents recreation and loss of DNS availability. The impact layer is preservation of production DNS while gaining state management.

Provider Configuration and Workflow

A typical Terraform workflow begins with provider configuration.

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

The provider block configures authentication details and default settings for interacting with AWS. Route 53 record creation follows provider setup.

Installation steps referenced for Amazon Linux include:

sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo sudo yum -y install terraform

The aws_route53_record resource then references the hosted zone ID, name, and type.

Example of non-alias CNAME with weighted routing:

resource "aws_route53_record" "www-dev" { zone_id = "${aws_route53_zone.primary.zone_id}" name = "www" type = "CNAME" ttl = "5" weighted_routing_policy { weight = 10 } set_identifier = "dev" records = ["dev.example.com"] }

resource "aws_route53_record" "www-live" { zone_id = "${aws_route53_zone.primary.zone_id}" name = "www" type = "CNAME" ttl = "5" weighted_routing_policy { weight = 90 } set_identifier = "live" records = ["live.example.com"] }

The low TTL of 5 seconds enables rapid testing shifts, but increases query load and cost.

Best Practices and Operational Considerations

Use variables for zone IDs. Do not hardcode zone IDs. Hardcoding creates environment-specific drift and complicates promotion between dev, staging, and prod.

Avoid mixing alias and non-alias arguments. Alias records must omit ttl and records. Violating this causes Terraform plan failures.

Set allow_overwrite carefully. Enabling overwrite can silently replace manual records, impacting teams that manage DNS outside Terraform.

Use set_identifier for all routing policy records. Without a unique identifier Route 53 rejects multiple records with the same name and type.

Prefer alias records for AWS resources to benefit from free queries and integrated health checks.

Argument Reference Summary

The resource supports:

  • zone_id - Required. The ID of the hosted zone to contain this record.
  • name - Required. The name of the record.
  • type - Required. The record type.
  • ttl - Required for non-alias records. The TTL of the record.
  • records - Required for non-alias records. A string list of values.

Additional optional arguments include allow_overwrite, set_identifier, and nested policy blocks weighted_routing_policy, failover_routing_policy, geolocation_routing_policy, latency_routing_policy, cidr_routing_policy, and alias.

Conclusion

The aws_route53_record resource is the primary interface for codifying DNS in AWS. Its power lies in the combination of basic record creation and advanced routing policies. Weighted routing enables controlled canary releases, failover routing ensures resilience, and alias records integrate natively with AWS load balancing. Dynamic patterns using for_each and allow_overwrite support certificate automation and bulk subdomain management. Import capabilities allow brownfield adoption without downtime. Adherence to best practices around variables, identifiers, and alias usage preserves stability and security at scale.

Sources

  1. awsfundamentals.com
  2. TerraformFoundation
  3. oneuptime.com
  4. koding.com
  5. geeksforgeeks.org

Related Posts