DEV Community

Cover image for What I've Learned Learning Terraform: Part 7
Ekin Öcalan
Ekin Öcalan

Posted on • Edited on

What I've Learned Learning Terraform: Part 7

Terraform Series


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:

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

output "is_staging" {
  value = ! var.is_production
}
Enter fullscreen mode Exit fullscreen mode

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.

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

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.
Enter fullscreen mode Exit fullscreen mode

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:

resource "digitalocean_domain" "domain" {
  count      = var.is_production ? 1 : 0
  name       = "example-domain.com"
  ip_address = digitalocean_droplet.server.ipv4_address
}
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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

variable "is_production" {
  description = "If the environment is production or not"
  type        = bool
  default     = true
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

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"
}
Enter fullscreen mode Exit fullscreen mode

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)
    }
Enter fullscreen mode Exit fullscreen mode

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.

Cover photo by Mario Dobelmann


Part 6.........................................................................................................Part 8

Top comments (1)

Collapse
 
yellow1912 profile image
yellow1912

Thank you for sharing. I'm starting with terraform as well and I'm learning it along the way. One question: do you know how to reuse the remote exec among resources? I have many resources sharing some part of the remote exec, would love to know a better way to reuse instead of copy and paste. Also, when you setup certain things you may have to out password in the exec command which is unsafe as I understand it. Do you find any solution for that?