Why manage your zones with terraform

Infrastructure as code gives us a set of possibilities that, that otherwise difficult to accomplish, such as testing changes before committing and of course, CI/CD options.

All of those are great to have, but not every team [feels like they] need testing, before making changes to records of a zone. However, almost everyone wants to use those domains for different purposes. 

I will guide you through my favorite example of this, having an EC2 host automatically resolve to a domain we have.

We have recently released our first public terraform module terraform-aws-revdb-dns.

As our first open module, this is a tiny, but powerful resource.

Add your managed domain to Route53

I’ve just recently purchased infrastructureascode.blog. So recently, we haven’t configured properly, but that gives us a wonderful chance to show you, how to bring a brand new domain into your infrastructure [as code].

First of, on domains.google.com, after the registration, you need to allow the AWS DNS servers, to service any lookup requests to your domain. To do this, we first need a list of DNS servers to provide for google domains.

Using our first open module, all you need to add as terraform code is this.

module "infrastructureascode_blog" {
  source      = "revenants-cie/revdb-dns/aws"
  version     = "0.0.5"
  domain_name = "infrastructureascode.blog"
}
output "infrastructureascode_blog" {
    value = module.infrastructureascode_blog.zone
}

This will result in the following terraform plan output.

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.infrastructureascode_blog.aws_route53_zone.zone will be created
  + resource "aws_route53_zone" "zone" {
      + comment       = "Managed by Terraform"
      + force_destroy = false
      + id            = (known after apply)
      + name          = "infrastructureascode.blog"
      + name_servers  = (known after apply)
      + vpc_id        = (known after apply)
      + vpc_region    = (known after apply)
      + zone_id       = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Once you apply, your output will have the DNS servers, you can use at domains.google.com.

 

module.infrastructureascode_blog.aws_route53_zone.zone: Refreshing state... [id=Z10476393R1AVLY2KQTRL]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

infrastructureascode_blog = {
  "comment" = "Managed by Terraform"
  "delegation_set_id" = ""
  "force_destroy" = false
  "id" = "Z10476393R1AVLY"
  "name" = "infrastructureascode.blog."
  "name_servers" = [
    "ns-1531.awsdns-63.org",
    "ns-1634.awsdns-12.co.uk",
    "ns-411.awsdns-51.com",
    "ns-755.awsdns-30.net",
  ]
  "tags" = {}
  "vpc" = []
  "zone_id" = "Z10476393R"
}

And now, these DNS servers are ready to be used on domains.google.com.

Adding records

This is where infrastructure as code, starts to shine. Lets say we want to add the Google MX records to this domain. Nothing is more simple, just expand the code as such:

module "infrastructureascode_blog" {
  source      = "revenants-cie/revdb-dns/aws"
  domain_name = "infrastructureascode.blog"
  records = {
    MX = [
      "1 ASPMX.L.GOOGLE.COM",
      "5 ALT1.ASPMX.L.GOOGLE.COM",
      "5 ALT2.ASPMX.L.GOOGLE.COM",
      "10 ASPMX2.GOOGLEMAIL.COM",
      "10 ASPMX3.GOOGLEMAIL.COM",
    ]
  }
}

output "infrastructureascode_blog" {
    value = module.infrastructureascode_blog.zone
}

Now all you need is to run terraform apply.

module.infrastructureascode_blog.aws_route53_zone.zone: Refreshing state... [id=Z10476393R1L]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.infrastructureascode_blog.aws_route53_record.records["MX"] will be created
  + resource "aws_route53_record" "records" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + records         = [
          + "1 ASPMX.L.GOOGLE.COM",
          + "10 ASPMX2.GOOGLEMAIL.COM",
          + "10 ASPMX3.GOOGLEMAIL.COM",
          + "5 ALT1.ASPMX.L.GOOGLE.COM",
          + "5 ALT2.ASPMX.L.GOOGLE.COM",
        ]
      + ttl             = 3600
      + type            = "MX"
      + zone_id         = "Z10476393R"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.infrastructureascode_blog.aws_route53_record.records["MX"]: Creating...
module.infrastructureascode_blog.aws_route53_record.records["MX"]: Still creating... [10s elapsed]
module.infrastructureascode_blog.aws_route53_record.records["MX"]: Still creating... [20s elapsed]
module.infrastructureascode_blog.aws_route53_record.records["MX"]: Still creating... [30s elapsed]
module.infrastructureascode_blog.aws_route53_record.records["MX"]: Creation complete after 33s [id=Z1047639TRL__MX]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Dynamically generate hostnames for your EC2 instances

And this is where infrastructure as code, isn’t only shining anymore, but blinding. Lets bring up an EC2 instance that will have a hostname, using our brand new domain name.

module "infrastructureascode_blog" {
  source      = "revenants-cie/revdb-dns/aws"
  domain_name = "infrastructureascode.blog"
  records = {
    MX = [
      "1 ASPMX.L.GOOGLE.COM",
      "5 ALT1.ASPMX.L.GOOGLE.COM",
      "5 ALT2.ASPMX.L.GOOGLE.COM",
      "10 ASPMX2.GOOGLEMAIL.COM",
      "10 ASPMX3.GOOGLEMAIL.COM",
    ]
  }
}

resource "aws_instance" "example_instance" {
  ami           = "ami-0be3f0371736d5394"
  instance_type = "t3.micro"
  associate_public_ip_address = true

  tags = {
    Name = "HelloWorld"
  }
}

resource "aws_route53_record" "example_host" {
  name     = "example_host"
  type     = "A"
  zone_id  = module.infrastructureascode_blog.zone_id
  ttl      = "300"
  records = [
    aws_instance.example_instance.public_ip
  ]
}

output "infrastructureascode_blog" {
    value = module.infrastructureascode_blog.zone
}

output "example_hostname" {
  value = aws_route53_record.example_host.fqdn
}

When you run apply, this is what you will see, our example_host.infastructurecode.blog is created and registered.

aws_instance.example_instance: Creating...
aws_instance.example_instance: Still creating... [10s elapsed]
aws_instance.example_instance: Creation complete after 16s [id=i-0a20fff1401fdee9c]
aws_route53_record.example_host: Creating...
aws_route53_record.example_host: Still creating... [10s elapsed]
aws_route53_record.example_host: Still creating... [20s elapsed]
aws_route53_record.example_host: Still creating... [30s elapsed]
aws_route53_record.example_host: Creation complete after 33s [id=Z10476393R1AVLY2KQTRL_example_host_A]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Outputs:

example_hostname = example_host.infrastructureascode.blog
infrastructureascode_blog = {
  "comment" = "Managed by Terraform"
  "delegation_set_id" = ""
  "force_destroy" = false
  "id" = "Z10476RL"
  "name" = "infrastructureascode.blog."
  "name_servers" = [
    "ns-1531.awsdns-63.org",
    "ns-1634.awsdns-12.co.uk",
    "ns-411.awsdns-51.com",
    "ns-755.awsdns-30.net",
  ]
  "tags" = {}
  "vpc" = []
  "zone_id" = "Z10476393L"

Summary

It’s all about connecting different components, letting the machines decide what needs to be done to achieve certain goals. We very firmly believe, that the future of infrastructure management is writing code, such as this. Making sure, nobody is trying to manually adjust domain name servers or forget to update the A record for the jumphost and so on.

Modern infrastructures are so complicated, human mistake is one of the number one reasons for outages. So much so, that most of the companies in Silicon Valley that I have friends working for have complete code change freeze in effect for the holidays. Management is terrified of human made mistakes.


0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *