The terraform import command allows you to import into HashiCorp Terraform resources that already existed previously in the provider we are working with, in this case AWS. However, it only allows you to import those records one by one, with one run of terraform import at a time. This, apart from being extremely tedious, in some situations becomes impractical. This is the case for the records of a Route53 DNS zone. The task can become unmanageable if we have multiple DNS zones, each one with tens or hundreds of records. In this article I offer you a bash script that will allow you to import in Terraform all the records of a Route53 DNS zone in a matter of seconds or a few minutes.
Prerequisites
In order to perform the procedure described in this article you will need to have Amazon’s aws cli tool installed and configured and also the jq tool available in most Linux distributions.
Procedure
You can perform the import by following these simple steps. You can skip steps 1 and 2 and go directly to step 3 if you already have your Terraform project correctly configured:
1.- Create a new Terraform project folder and configure the provider through the following main.tf file:
provider "aws" { region = "eu-west-1" access_key = "XXXXXXXXXXXXXXXXXXXX" secret_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
2.- Initialize the new Terraform project:
$ terraform init Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/aws... - Installing hashicorp/aws v3.74.1... - Installed hashicorp/aws v3.74.1 (signed by HashiCorp) Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
3.- Run the following script:
Remember to set the appropriate value for the zone_name, zone_id variables beforehand, and also aws_profile if you use different profiles in the aws cli tool to manage different AWS accounts.
#! /bin/bash # This script retrieves all DNS records from AWS Route53 DNS zone and imports all of them to Terraform zone_name='example.com' zone_id='XXXXXXXXXXXXXXXXXXXXX' aws_profile='example_com' # Get zone slug from zone name zone_slug=$(echo ${zone_name} | tr '.' '-') # Get DNS zone current data from AWS zone="$(aws --profile=${aws_profile} route53 list-hosted-zones | jq '.HostedZones[] | select (.Id | contains("'${zone_id}'"))')" # Another method to get DNS zone data searching by zone name instead of zone ID #zone="$(aws --profile=${aws_profile} route53 list-hosted-zones | jq '.HostedZones[] | select (.Name=="'${zone_name}'.")')" zone_comment="$(echo ${zone} | jq '.Comment')" if [ "${zone_comment}" == 'null' ];then zone_comment="${zone_name} zone" fi # Write aws_route53_zone resource to terraform file cat << EOF > dns-zone-${zone_name}.tf resource "aws_route53_zone" "${zone_slug}" { name = "${zone_name}" comment = "${zone_comment}" } EOF # Import DNS zone and records from file to terraform terraform import "aws_route53_zone.${zone_slug}" "${zone_id}" # Retrieve all regular records (not alias) from DNS zone and write them down to terraform file IFS=$'\n' for dns_record in $(aws --profile="${aws_profile}" route53 list-resource-record-sets --hosted-zone-id "${zone_id}" | jq -c '.ResourceRecordSets[] | select(has("AliasTarget") | not)');do name="$(echo ${dns_record} | jq -r '.Name')" type="$(echo ${dns_record} | jq -r '.Type')" name_slug="$(echo ${type}-${name} | sed -E 's/[\._\ ]+/-/g' | sed -E 's/(^-|-$)//g')" ttl="$(echo ${dns_record} | jq -r '.TTL')" records="$(echo ${dns_record} | jq -cr '.ResourceRecords' | jq '.[].Value' | sed 's/$/,/')" records="$(echo ${records} | sed 's/,$//')" cat << EOF >> dns-zone-${zone_name}.tf resource "aws_route53_record" "${name_slug}" { zone_id = aws_route53_zone.${zone_slug}.zone_id name = "${name}" type = "${type}" ttl = "${ttl}" records = [${records}] } EOF # Import DNS record to Terraform terraform import "aws_route53_record.${name_slug}" "${zone_id}_${name}_${type}" done # Retrieve all alias records from DNS zone and write them down to terraform file IFS=$'\n' for dns_record in $(aws --profile="${aws_profile}" route53 list-resource-record-sets --hosted-zone-id "${zone_id}" | jq -c '.ResourceRecordSets[] | select(has("AliasTarget"))');do name="$(echo ${dns_record} | jq -r '.Name')" type="$(echo ${dns_record} | jq -r '.Type')" name_slug="$(echo ${type}-${name} | sed -E 's/[\._\ ]+/-/g' | sed -E 's/(^-|-$)//g')" alias_name="$(echo ${dns_record} | jq -cr '.AliasTarget' | jq -r '.DNSName')" cat << EOF >> dns-zone-${zone_name}.tf resource "aws_route53_record" "${name_slug}" { zone_id = aws_route53_zone.${zone_slug}.zone_id name = "${name}" type = "${type}" alias { name = "${alias_name}" zone_id = "${zone_id}" evaluate_target_health = true } } EOF # Import DNS record to Terraform terraform import "aws_route53_record.${name_slug}" "${zone_id}_${name}_${type}" done
The script generates a .tf file in the directory from which it is executed containing all the DNS records existing in Route53. At the same time it imports them into Terraform to start managing these resources with Terraform when it finishes. The execution of the script can take several minutes depending on the number of records in the zone to be imported, Fortunately it is a totally unattended process that will not require any effort on your part. I hope you find it useful!
Leave a Reply