What I've Learned Learning Terraform: Part 3
Tags: terraform, devops, tutorial, nginx
Published: 2020-08-17 10:27:03

### Terraform Series

- [Part 1: Introduction](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-1-4en4)
- [Part 2: Creating the Server](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon)
- Part 3: Provisioning the Server
- [Part 4: Managing Terraform State](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4)
- [Part 5: Cleaner Code with Terraform Modules](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-5-j4b)
- [Part 6: Loops with Terraform](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0)
- [Part 7: Conditionals with Terraform](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-7-4ae7)
- [Part 8: Testing Terraform Code](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328)

-----

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](https://www.terraform.io/docs/provisioners/remote-exec.html) to achieve this simple install.

Within `digitalocean_droplet` resource, add the provisioner below:

```tf
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"
    ]
  }
}
```

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](https://cloud.digitalocean.com/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`.

```tf
variable "public_key" {}
variable "private_key" {}
variable "ssh_fingerprint" {}
```

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.

```tf
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"
    ]
  }
}
```

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.

```tf
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"
    ]
  }
}
```

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:

```bash
$ terraform apply \
  -var "public_key=$HOME/.ssh/id_rsa.pub" \
  -var "private_key=$HOME/.ssh/id_rsa" \
  -var "ssh_fingerprint=YOUR_FINGERPRINT"
```

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:

```tf
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 = "@"
}
```

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](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/droplet#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:

```bash
$ terraform apply \
  -var "public_key=$HOME/.ssh/id_rsa.pub" \
  -var "private_key=$HOME/.ssh/id_rsa" \
  -var "ssh_fingerprint=YOUR_FINGERPRINT"
```

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.
```

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](https://dev-to-uploads.s3.amazonaws.com/i/ymohqbp7tje9yd7t6p5s.png)

PS: Cover photo by [tian kuan](https://unsplash.com/@realaxer?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)

-----

[Part 2](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-2-jon).........................................................................................................[Part 4](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-4-np4)