The goal is to provision everything from datacentre to browser in one command using Terraform for infrastructure automation.

Installation

For macOS users, installation uses Homebrew:

brew install terraform

Installation instructions vary by operating system.

Provider Configuration

Before creating resources, configure the Digital Ocean provider:

variable "do_token" {}

provider "digitalocean" {
  token = "${var.do_token}"
}

Create a terraform.tfvars file with:

do_token=<YOUR_TOKEN>

Avoid version control for this sensitive file. Run terraform init to download the Digital Ocean package.

SSH Key Setup

Upload public keys to Digital Ocean:

variable "key_path" {}

resource "digitalocean_ssh_key" "markprovan_key" {
   name = "Mark Provan Key"
   public_key = "${file("${var.key_path}")}"
}

Server Creation

Create tags and a droplet (server):

resource "digitalocean_tag" "blog_tag" {
  name = "blog"
}

resource "digitalocean_tag" "personal_tag" {
  name = "personal"
}

resource "digitalocean_droplet" "blog" {
  image  = "coreos-stable"
  name   = "blog"
  region = "lon1"
  size   = "s-1vcpu-1gb"
  tags   = [
    "${digitalocean_tag.blog_tag.id}",
    "${digitalocean_tag.personal_tag.id}"
  ]
  ssh_keys = [
    "${digitalocean_ssh_key.markprovan_key.id}"
  ]

  depends_on = ["digitalocean_ssh_key.markprovan_key"]
}

The configuration demonstrates resource interpolation, accessing IDs from other resources. The depends_on attribute ensures the SSH key exists before server creation.

DNS Configuration

Configure Cloudflare DNS records:

variable "cloudflare_email" {}
variable "cloudflare_token" {}

provider "cloudflare" {
  email = "${var.cloudflare_email}"
  token = "${var.cloudflare_token}"
}

resource "cloudflare_record" "markprovan" {
  domain = "markprovan.com"
  name = "markprovan.com"
  proxied = true
  value = "${digitalocean_droplet.blog.ipv4_address}"
  type = "A"
}

The DNS record references the droplet’s IP address, demonstrating cross-provider resource dependencies.

Software Provisioning

Deploy Docker containers using provisioners:

resource "digitalocean_droplet" "blog" {
  image = "coreos-stable"
  name = "blog"
  region = "lon1"
  size = "s-1vcpu-1gb"
  tags = [
    "${digitalocean_tag.blog_tag.id}",
    "${digitalocean_tag.personal_tag.id}"
  ]
  ssh_keys = [
    "${digitalocean_ssh_key.markprovan_key.id}"
  ]

  depends_on = ["digitalocean_ssh_key.markprovan_key"]

  provisioner "file" {
    source = "docker-compose.yml"
    destination = "/home/core/docker-compose.yml"

    connection {
      type     = "ssh"
      user     = "core"
      private_key = "${file("${var.private_key_path}")}"
    }
  }

  provisioner "remote-exec" {
    inline = [
      "docker-compose up -d"
    ]

    connection {
      type     = "ssh"
      user     = "core"
      private_key = "${file("${var.private_key_path}")}"
    }
  }
}

The file provisioner uploads the Docker Compose configuration, followed by remote-exec to start services. These execute sequentially.

Deployment

Execute terraform apply to provision all resources, confirming changes when prompted.

Conclusion

Terraform is a really handy tool for infrastructure automation, and I am excited to continue exploring its capabilities.