Terraform Primer

In the science (fiction) community, terraforming is the act of taking a planet, moon, or other celestial body and making it suitable for human life. While a simplistic definition (more information can be found in this wikipedia article), the name is quite suitable.

In the context of IT, Terraform is a infrastructure management tool. It is stateful, which means it maintains it’s own record on how everything is supposed to look. This tool has been a crucial part in many technology organizations around the world.

Terraform allows you to express your infrastructure as code. While this term has become a bit of a buzz phrase over the last few years, managing your infrastructure as code severely optimizes your management of resources. You are now able to check your infrastructure definitions into source control. You can apply the same infrastructure instructions across multiple environments. And you can move your infrastructure from point A to B with ease.

An Example

Take the following example.

As a sysadmin you just got assigned a ticket requesting a new server for development purposes. The requester for this server has requested a standard spec sheet for the development servers that include CPU cores, memory, and storage. Before terraform, you would log into your vsphere environment, find your template by hand (search never seems to work right does it?), clone it, disable the network connection to avoid IP address conflicts, open the console, assign your IP address, add the DNS records, and finally log into your server. There goes your morning.

With terraform you could have a terraform definition ready to go. You would be using the first class vsphere provider to provision your servers. Your definition might look something like this (taken from the documentation in the provided link):

provider "vsphere" {
  user           = "${var.vsphere_user}"
  password       = "${var.vsphere_password}"
  vsphere_server = "${var.vsphere_server}"

  # If you have a self-signed cert
  allow_unverified_ssl = true
}

data "vsphere_datacenter" "dc" {
  name = "dc1"
}

data "vsphere_datastore" "datastore" {
  name          = "datastore1"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

data "vsphere_resource_pool" "pool" {
  name          = "cluster1/Resources"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

data "vsphere_network" "network" {
  name          = "public"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

resource "vsphere_virtual_machine" "vm" {
  name             = "terraform-test"
  resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
  datastore_id     = "${data.vsphere_datastore.datastore.id}"

  num_cpus = 2
  memory   = 1024
  guest_id = "other3xLinux64Guest"

  network_interface {
    network_id = "${data.vsphere_network.network.id}"
  }

  disk {
    label = "disk0"
    size  = 20
  }
}

This example is enough to get you a server running on your cluster. Need 2 servers instead of 1? Add a “count” parameter to your “vsphere_virtual_machine” resource. Now you will have 2 servers. But going back to our example, we were cloning from a template right? No problem. Simply add a vsphere_virtual_machine data object to hold information about your template and instruct terraform to clone off that template by changing the vsphere_virtual_machine resource:

data "vsphere_virtual_machine" "template" {
  name          = "ubuntu-16.04"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

resource "vsphere_virtual_machine" "vm" {
  name             = "terraform-test"
  resource_pool_id = "${data.vsphere_compute_cluster.cluster.resource_pool_id}"
  datastore_id     = "${data.vsphere_datastore.datastore.id}"

  num_cpus = 2
  memory   = 1024
  guest_id = "${data.vsphere_virtual_machine.template.guest_id}"

  scsi_type = "${data.vsphere_virtual_machine.template.scsi_type}"

  network_interface {
    network_id   = "${data.vsphere_network.network.id}"
    adapter_type = "${data.vsphere_virtual_machine.template.network_interface_types[0]}"
  }

  disk {
    label            = "disk0"
    size             = "${data.vsphere_virtual_machine.template.disks.0.size}"
    eagerly_scrub    = "${data.vsphere_virtual_machine.template.disks.0.eagerly_scrub}"
    thin_provisioned = "${data.vsphere_virtual_machine.template.disks.0.thin_provisioned}"
  }

  clone {
    template_uuid = "${data.vsphere_virtual_machine.template.id}"

    customize {
      linux_options {
        host_name = "terraform-test"
        domain    = "test.internal"
      }

      network_interface {
        ipv4_address = "10.0.0.10"
        ipv4_netmask = 24
      }

      ipv4_gateway = "10.0.0.1"
    }
  }
}

Note, we have added a customization section telling it what and how to clone. Now we simply run this using “terraform apply” and we have a server running. But that’s not all, we also need a DNS record so we know how to find this server on the network. Do not worry! Terraform also has a DNS provider. It can communicate with nearly any DNS server using RFC 2136 (RFC Spec and a more readable wikipedia entry).

All we need to do is add the following DNS resource and we will have our DNS setup and ready to go! (From the DNS provider documentation)

provider "dns" {
  update {
    server        = "192.168.0.1"
    key_name      = "example.com."
    key_algorithm = "hmac-md5"
    key_secret    = "3VwZXJzZWNyZXQ="
  }
}

resource "dns_a_record_set" "www" {
  zone = "example.com."
  name = "www"
  addresses = [
    "${vsphere_virtual_machine.vm.default_ip_address}",
  ]
  ttl = 300
}

The magic happens in the “addresses” list where we told it to use whatever IP address our virtual machine is using. This means if/when the IP address for the server changes, DNS will be updated automatically. Pretty neat huh?

Derek Anderson

Detroit, United States
Email me

A self proclaimed technologist, nature observer, and aspiring woodworker.