What I've Learned Learning Terraform: Part 3

gzg

Ekin Öcalan

Posted on August 17, 2020

What I've Learned Learning Terraform: Part 3

Terraform Series


Now that we could create a server, it's time to configure it a bit. It's no use to us if it sits right there. So let's install nginx and then have one of our domains point to the server to see if it's working or not.

Provisioning the server

It's only to install nginx for now, but it's still a server provision. We are going to use remote-exec provisioner to achieve this simple install.

Within digitalocean_droplet resource, add the provisioner below:

resource "digitalocean_droplet" "web" {
  image  = "ubuntu-20-04-x64"
  name   = "terraform-sandbox"
  region = "ams3"
  size   = "s-1vcpu-1gb"

  provisioner "remote-exec" {
    inline = [
      "export PATH=$PATH:/usr/bin",
      # install nginx
      "sudo apt-get update",
      "sudo apt-get -y install nginx"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

There are better ways to provision a server, but we'll get to that later. For our simple nginx install, this would do the trick.

Still, there is one thing missing here. How is Terraform going to connect to our server to run the bash script above?

Granting Terraform the SSH access

You first need to go to your Account/Security page on DigitalOcean and add your computer's SSH key there. If you don't know how to do it, DigitalOcean helps you to create and use an SSH key on that page. You're going to need the SSH key fingerprint shown on that page.

Now we are going to create a new Terraform file and call it variables.tf.

variable "public_key" {}
variable "private_key" {}
variable "ssh_fingerprint" {}
Enter fullscreen mode Exit fullscreen mode

You see how we create variables in Terraform. Typically, you can give them default values as well. However, since they contain sensitive data, we deliberately left them empty. This way, Terraform will ask us to fill them while planning or applying.

We head back to our main.tf now, and add the ssh_keys parameter inside our droplet resource.

resource "digitalocean_droplet" "web" {
  image  = "ubuntu-20-04-x64"
  name   = "terraform-sandbox"
  region = "ams3"
  size   = "s-1vcpu-1gb"
  ssh_keys = [
    var.ssh_fingerprint
  ]

  provisioner "remote-exec" {
    inline = [
      "export PATH=$PATH:/usr/bin",
      # install nginx
      "sudo apt-get update",
      "sudo apt-get -y install nginx"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This addition will grant the SSH key holder to access our droplet. We provided a list of ssh_keys. It means that you can add more than one. Additionally, it also shows how we can access variables defined within the Terraform files. Hint: var.ssh_fingerprint, var.public_key, etc.

Lastly, we are going to define a connection within the droplet resource.

resource "digitalocean_droplet" "web" {
  image  = "ubuntu-20-04-x64"
  name   = "terraform-sandbox"
  region = "ams3"
  size   = "s-1vcpu-1gb"
  ssh_keys = [
    var.ssh_fingerprint
  ]

  connection {
    host        = self.ipv4_address
    user        = "root"
    type        = "ssh"
    private_key = file(var.private_key)
    timeout     = "2m"
  }

  provisioner "remote-exec" {
    inline = [
      "export PATH=$PATH:/usr/bin",
      # install nginx
      "sudo apt-get update",
      "sudo apt-get -y install nginx"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This connection declaration defines an SSH connection. The host is the IP address of our droplet. Notice how we used the self object there. It's going to connect with the root user, using the private key of our computer.

Our main.tf is ready to create a droplet, and then make a connection to it to install nginx. Now, instead of a plain terraform apply, we need to do it with populating the variables as well:

$ terraform apply \
  -var "public_key=$HOME/.ssh/id_rsa.pub" \
  -var "private_key=$HOME/.ssh/id_rsa" \
  -var "ssh_fingerprint=YOUR_FINGERPRINT"
Enter fullscreen mode Exit fullscreen mode

You need to use your SSH key fingerprint instead of YOUR_FINGERPRINT. And please also note that the public_key and private_key values may be different based on how you created them.

When you run the command, it's going to show you the plan and ask for your approval. You won't see the provisioner in the plan. But when you apply itthe plan, you'll see how Terraform makes the connection to the droplet, and you'll see the logs of the provisioner script.

Pointing a domain to the server

We have a server, and we have nginx running on it. You can visit the IP address of your server to see it with your eyes. However, let's move one step ahead and use a domain instead of the IP address.

Create a domain.tf file and populate it with:

resource "digitalocean_domain" "yourdomain" {
   name = "yourdomain.com"
   ip_address = digitalocean_droplet.web.ipv4_address
}

resource "digitalocean_record" "cname_www" {
  domain = digitalocean_domain.yourdomain.name
  type = "CNAME"
  name = "www"
  value = "@"
}
Enter fullscreen mode Exit fullscreen mode

First, we create a digitalocean_domain resource. You need to update the name with your domain address. Notice how we used the ipv4_address attribute to point our domain to a specific IP address. You can see the full list of attributes reference on Terraform Registry. The same way we access the IP address here, you can access any attribute from digitalocean_droplet.web.

Second, we create a digitalocean_record resource to point all www subdomain requests to the main one. Thus, both yourdomain.com and www.yourdomain.com will lead to our droplet. See how we set the domain parameter inside to digitalocean_domain.yourdomain.name. That's how Terraform will understand under which domain it should create this record.

Resource order is not important

One interesting fact with Terraform we can mention here is that Terraform doesn't care about the order of the resources. Even if you declare digitalocean_record resource before the digitalocean_domain one, it would still work. That's because Terraform is declarative, and it creates its dependency graph based on what you declared.

Back to the domain creation... Now if you apply your plan again:

$ terraform apply \
  -var "public_key=$HOME/.ssh/id_rsa.pub" \
  -var "private_key=$HOME/.ssh/id_rsa" \
  -var "ssh_fingerprint=YOUR_FINGERPRINT"
Enter fullscreen mode Exit fullscreen mode

You'll see a plan just like this:

Terraform will perform the following actions:

  # digitalocean_domain.yourdomain will be created
  + resource "digitalocean_domain" "yourdomain" {
      + id         = (known after apply)
      + ip_address = "111.111.111.111"
      + name       = "yourdomain.com"
      + urn        = (known after apply)
    }

  # digitalocean_record.cname_www will be created
  + resource "digitalocean_record" "cname_www" {
      + domain = "yourdomain.com"
      + fqdn   = (known after apply)
      + id     = (known after apply)
      + name   = "www"
      + ttl    = (known after apply)
      + type   = "CNAME"
      + value  = "@"
    }

Plan: 2 to add, 0 to change, 0 to destroy.
Enter fullscreen mode Exit fullscreen mode

Apply the plan, and that's all. Now if you visit your domain, you'll see your that nginx's default site configuration will catch your request and show you its default page:

nginx welcomes you

PS: Cover photo by tian kuan


Part 2.........................................................................................................Part 4

💖 💪 🙅 🚩
gzg
Ekin Öcalan

Posted on August 17, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related