What I've Learned Learning Terraform: Part 7
Tags: iac, terraform, devops, tutorial
Published: 2020-09-08 14:28:46

### 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](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-3-2e32)
- [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
- [Part 8: Testing Terraform Code](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328)

-----

We continue to learn more about the basic Terraform features.

# Conditionals

We've already used some sort of conditionals in the previous parts of this series. However, it makes sense to categorize the usage of conditionals in Terraform. Just like with the loops, we use a different approach to use conditionals since Terraform is a declarative language.

## Conditional expressions

Although we don't have a procedural way of implementing if-else statements, Terraform supports the ternary syntax. Let's say you have two environments: Production and staging. You'd like to have a production environment with more computing power, but you also want to cut costs on staging and have less configuration there. How do you declare your condition?

Let's say you create a variable to determine whether the environment is production or not:

```tf
variable "is_production" {
  description = "If the environment is production or not"
  type        = bool
  default     = false
}

output "is_staging" {
  value = ! var.is_production
}
```

The code above is only a demonstration of a pair of helpers. One of them is a variable, and the other one is an output. To keep the example simple, we are not going to populate the variable programmatically, but make it `is_production=false`.

Now, let's say you are going to create one server for each environment. The production server will have a more powerful configuration.

```tf
resource "digitalocean_droplet" "server" {
  image  = "ubuntu-20-04-x64"
  name   = "example-droplet"
  region = "ams3"
  size   = var.is_production ? "s-8vcpu-32gb" : "s-1vcpu-1gb"
}
```

Notice how we used the ternary syntax to declare the size of the droplet. Now if you run `terraform plan`, you'll see that Terraform plans to create a droplet with the size of `s-1vcpu-1gb` because, by default, it's not the production environment:

```
Terraform will perform the following actions:

  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "example-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

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

## Creating resources conditionally

Resources are the backbone of IaC. We declare resources, and Terraform finds a way to create the defined resources. It's easy to make a resource when you know that it's necessary. But what if you don't know if you're going to need that resource?

Moving on from the previous example of production and staging environments, let's move one step up and create a resource conditionally. Add this resource declaration to the file:

```tf
resource "digitalocean_domain" "domain" {
  count      = var.is_production ? 1 : 0
  name       = "example-domain.com"
  ip_address = digitalocean_droplet.server.ipv4_address
}
```

Domain declaration requires name and the IP address attributes. `count` parameter is the one that allows us to create this resource conditionally. I should mention that this is a hack to simulate a conditional statement on a resource-level. If the environment is production, Terraform will create one domain with this configuration. If it's staging, then it's not going to create at all.

If you run `terraform plan`, you'll see that nothing changes in the plan because our environment is staging by default:

```
Terraform will perform the following actions:

  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "example-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

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

Now let's go ahead and change the default value for `is_production` variable to `true`:

```tf
variable "is_production" {
  description = "If the environment is production or not"
  type        = bool
  default     = true
}
```

If you run the `terraform plan` command now, you'll see two additional changes on the plan. One for the variable change, and the other for the domain resource creation:

```
Terraform will perform the following actions:

  # digitalocean_domain.domain[0] will be created
  + resource "digitalocean_domain" "domain" {
      + id         = (known after apply)
      + ip_address = (known after apply)
      + name       = "example-domain.com"
      + urn        = (known after apply)
    }

  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "example-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-8vcpu-32gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

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

Changes to Outputs:
  ~ is_staging = true -> false
```

There is a drawback with this hack, though. As you can see from the plan, the domain resource will be created as part of a list: `digitalocean_domain.domain[0]`. That's because we use the `count` parameter within the resource.

By the way, starting from Terraform 0.13, you can do this hack with modules as well.

## Setting values conditionally (Interpolation)

There is one more feature for conditionals on Terraform, which is the interpolation.

> In computer programming, string interpolation (or variable interpolation, variable substitution, or variable expansion) is the process of evaluating a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values. [1]

Interpolation in Terraform is perhaps the closest thing to a conditional in other programming languages due to its direct if-else statement usage.

Let's demonstrate this by setting the droplet name based on the environment:

```tf
resource "digitalocean_droplet" "server" {
  image  = "ubuntu-20-04-x64"
  name   = "%{if var.is_production}${"production-droplet"}%{else}${"staging-droplet"}%{endif}"
  region = "ams3"
  size   = var.is_production ? "s-8vcpu-32gb" : "s-1vcpu-1gb"
}
```

Notice how we used if-else statements inside the name attribute. You can even use variables here to define your interpolation with placeholders. Running the `terraform plan` command will give us the plan below:

```
  # digitalocean_droplet.server will be created
  + resource "digitalocean_droplet" "server" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "production-droplet"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "ams3"
      + resize_disk          = true
      + size                 = "s-8vcpu-32gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }
```

You see that the name is `production-droplet` since we set our environment to production in the last step. Now you have a conditional value setting.

[1]: https://en.wikipedia.org/wiki/String_interpolation

Cover photo by [Mario Dobelmann](https://unsplash.com/s/photos/yin-yang?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)

-----

[Part 6](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-6-id0).........................................................................................................[Part 8](https://dev.to/gzg/what-i-ve-learned-learning-terraform-part-8-1328)