Until recently we used the web-interface of Route 53, the AWS DNS service, to manage the DNS records for our startup, ParrotPolls.com. Making manual changes to such a critical piece of infrastructure always made me nervous. I knew there was a better way, but I had never taken the time to set this up.
The incident which got me to set up terraform
(a command-line tool for managing cloud infrastructure) was when I realized I was about to lose track of which DNS records serve which purpose. We had been trying a couple of different service providers, and in particular the email-providers usually require setting up TXT and CNAME records for DKIM and SPF. If I had used an infrastructure-as-code-approach, like terraform
, I could have just looked at the history of the git-repo. That’s what I want!
So I sat down to figure it out.
The Goal
“Importing” an existing set of DNS records from AWS Route 53 “into” terraform
, so that I can check this configuration in form of tf-files into a git-repo.
⚠️ Please be careful ⚠️
I am not responsible if you accidentally spin-up hundreds of EC2 instances and incur a huge bill for your company, when you follow this step-by-step guide.
While the steps in this tutorial should be safe (I followed them myself), I cannot guarantee that. If you are completely new to terraform
, do yourself a favor and create a new AWS account that you can use as a playground.
Prerequisites
You will need the following software packages. Please follow their respective instructions to install them:
terraform
obviously. Follow the official instructions to install terraform.terraformer
- a nifty tool that helps you to import configuration. Again, follow the instructions.
Also, as a terraform-beginner I found this list of best-practices helpful.
Step-By-Step Guide
- Configure the AWS provider by creating a
provider.tf
-file with the following content:
provider "aws" {
version = "~> 2.0"
region = "eu-west-1"
shared_credentials_file = "~/.aws/credentials"
profile = "default"
}
(Assumptions: You are running OSX or Linux. You have the AWS-CLI installed and configured with a default profile.)
- Run
terraform init
to install the AWS provider plugin. (Note: This will have installed the plugin forterraform
. You will still need to have installed this plugin forterraformer
separately. The relevant sentence from their documentation is: “Copy your Terraform provider’s plugin(s) to folder ~/.terraform.d/plugins/{darwin,linux}_amd64/, as appropriate.”)
$ terraform init
- Run
terraform import
, which will create a./generated
-folder containing a set of *.tf-files:
$ terraformer import aws --resources=route53 --connect=true --regions=eu-west-1 --profile=default
2020/04/16 22:10:29 aws importing default region
2020/04/16 22:10:29 aws importing... route53
2020/04/16 22:10:34 Refreshing state... aws_route53_record.tfer--ZK97EJ5LKJOBM_parrotpolls-002E-com-002E-_A_
2020/04/16 22:10:34 Refreshing state... aws_route53_zone.tfer--ZK97EJ5LKJOBM_parrotpolls-002E-com
...
2020/04/16 22:10:36 aws Connecting....
2020/04/16 22:10:36 aws save route53
2020/04/16 22:10:36 aws save tfstate for route53
- Verify that the exported tf-files work as expected
$ terraform plan -out=plan.txt ./generated/aws/route53/
...[lot's of output omitted]...
------------------------------------------------------------------------
This plan was saved to: plan.txt
To perform exactly these actions, run the following command to apply:
terraform apply "plan.txt"
Let’s print the content of the plan.txt
file:
terraform show plan.txt
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_route53_zone.tfer--ZK97EJ5LKJOBM_parrotpolls-002E-com will be updated in-place
~ resource "aws_route53_zone" "tfer--ZK97EJ5LKJOBM_parrotpolls-002E-com" {
+ comment = "Managed by Terraform"
force_destroy = false
id = "ZK97EJ5LKJOBM"
name = "parrotpolls.com."
name_servers = [
"ns-140.awsdns-17.com",
"ns-1426.awsdns-50.org",
"ns-1772.awsdns-29.co.uk",
"ns-761.awsdns-31.net",
]
tags = {}
zone_id = "ZK97EJ5LKJOBM"
}
Plan: 0 to add, 1 to change, 0 to destroy.
Make sure this shows something that you would expect. In particular, you should see the 0 to add, 1 to change, 0 to destroy.
- Remove unused files
Unless you want to import more settings using terraformer
, you can delete the copy of the AWS provider plugin from ~/.terraform/plugin/...
.
What I did next
I moved all files from the generated
-folder into a git-repository, including terraform.tfstate
, which contains the state, which seems to be important. (If you work in a team, you’ll want to use remote-state. See the docs for details.). I then checked everything (except the .terraform
-subfolder) into git.
After doing that I started commenting all the records in my route53_record.tf
file, one comment for each record. Here is an example:
# MX records <--- This is the comment 🤓
resource "aws_route53_record" "tfer--ZK97EJ5LKJOBM_parrotpolls-002E-com-002E-_MX_" {
name = "parrotpolls.com"
records = ["5 alt2.aspmx.l.google.com.", "5 alt1.aspmx.l.google.com.", "1 aspmx.l.google.com."]
ttl = "600"
type = "MX"
zone_id = aws_route53_zone.tfer--ZK97EJ5LKJOBM_parrotpolls-002E-com.zone_id
}
My new workflow for making changes to DNS records
- make the changes in the tf-files
- run
terraform plan -out=plan.txt
- check if the plan looks like what I would expect
- if the plan looks ok, I commit the changed tf-files to git (not the
plan.txt
) - run
terraform apply plan.txt
to apply the changes - commit the changed state (
terraform.tfstate
) to git - delete
plan.txt
If you liked this post, please, do share it:
Thanks, for reading (and sharing)! 🥳