Updated on 25.5.2023

How to use Terraform variables

Terraform variables

Variables in Terraform are a great way to define centrally controlled reusable values. The information in Terraform variables is saved independently from the deployment plans, which makes the values easy to read and edit from a single file.

In this guide, we’ll go over the types of available variables in Terraform, how to define them, and how to put them to use. You can use the general information about the variables as a quick cheat sheet but the examples are a direct continuation to our Terraform beginners article. If you haven’t installed Terraform yet, follow the guide here to get started.

Terraform variables

Terraform supports a few different variable formats. Depending on the usage, the variables are generally divided into inputs and outputs.

The input variables are used to define values that configure your infrastructure. These values can be used again and again without having to remember their every occurrence in the event it needs to be updated.

Output variables, in contrast, are used to get information about the infrastructure after deployment. These can be useful for passing on information such as IP addresses for connecting to the server.

Input variables

Input variables are usually defined by stating a name, type and default value. However, the type and default values are not strictly necessary. Terraform can deduct the type of the variable from the default or input value.

Variables can be predetermined in a file or included in the command-line options. As such, the simplest variable is just a name while the type and value are selected based on the input.

variable "variable_name" {}
terraform apply -var variable_name="value"

The input variables, like the one above, use a couple of different types: strings, lists, maps, and boolean. Here are some examples of how each type are defined and used.

String

Strings mark a single value per structure and are commonly used to simplify and make complicated values more user-friendly. Below is an example of a string variable definition.

variable "template" {
  type = string
  default = "01000000-0000-4000-8000-000030080200"
}

A string variable can then be used in resource plans. Surrounded by double quotes, string variables are a simple substitution such as the example underneath.

storage = var.template

List

Another type of Terraform variables lists. They work much like a numbered catalogue of values. Each value can be called by its corresponding index in the list. Here is an example of a list variable definition.

variable "users" {
  type    = list
  default = ["root", "user1", "user2"]
}

Lists can be used in the resource plans similarly to strings, but you’ll also need to denote the index of the value you are looking for.

username = var.users[0]

Map

Maps are a collection of string keys and string values. These can be useful for selecting values based on predefined parameters such as the server configuration by the monthly price.

variable "plans" {
  type = map
  default = {
    "5USD"  = "1xCPU-1GB"
    "10USD" = "1xCPU-2GB"
    "20USD" = "2xCPU-4GB"
  }
}

You can access the right value by using the matching key. For example, the variable below would set the plan to "1xCPU-1GB".

plan = var.plans["5USD"]

The values matching their keys can also be used to look up information on other maps. For example, underneath is a shortlist of plans and their corresponding storage sizes.

variable "storage_sizes" {
  type = map
  default = {
    "1xCPU-1GB"  = "25"
    "1xCPU-2GB"  = "50"
    "2xCPU-4GB"  = "80"
  }
}

These can then be used to find the right storage size based on the monthly price as defined in the previous example.

size = lookup(var.storage_sizes, var.plans["5USD"])

Boolean

The last of the available variable type is boolean. They give the option to employ simple true or false values. For example, you might wish to have a variable that decides when to generate the root user password on a new deployment.

variable "set_password" {
  default = false
}

The above example boolean can be used similarly to a string variable by simply marking down the correct variable.

create_password = var.set_password

By default, the value is set to false in this example. However, you can overwrite the variable at deployment by assigning a different value in a command-line variable.

terraform apply -var set_password="true"

Output variables

Output variables provide a convenient way to get useful information about your infrastructure. As you might have noticed, much of the server details are calculated at deployment and only become available afterwards. Using output variables you can extract any server-specific values including the calculated details.

Configuring output variables is really quite simple. All you need to do is define a name for the output and what value it should represent. For example, you could have Terraform show your server’s IP address after deployment with the output variable below.

output "public_ip" {
  value = upcloud_server.server_name.network_interface[0].ip_address
}

Note that the place of the public network interface on the list of network interfaces depends on which order the NICs are defined in the resources.

Terraform would then output the public IP address at the end of the apply command process. Alternatively, output variables can also be called on-demand using terraform output command. Next, continue on to set up a variable file for server configuration.

Defining variables in a file

You should have a Terraform project with a basic plan already set up. If not, follow our getting started guide for Terraform to begin.

Go to your Terraform project directory.

cd ~/terraform/base

Terraform variables can be defined within the infrastructure plan but are recommended to be stored in their own variables file. All files in your Terraform directory using the .tf file format will be automatically loaded during operations.

Create a variables file, for example, variables.tf and open the file for editing.

Add the below variable declarations to the variables file. Replace the SSH key private file path and the public key with our own.

variable "private_key_path" {
  type = string
  default = "/home/user/.ssh/terraform_rsa"
}

variable "public_key" {
  type = string
  default = "ssh-rsa terraform_public_key"
}

variable "zones" {
  type = map
  default = {
    "amsterdam" = "nl-ams1"
    "london"    = "uk-lon1"
    "frankfurt" = "de-fra1"
    "helsinki1" = "fi-hel1"
    "helsinki2" = "fi-hel2"
    "chicago"   = "us-chi1"
    "sanjose"   = "us-sjo1"
    "singapore" = "sg-sin1"
  }
}

variable "plans" {
  type = map
  default = {
    "5USD"  = "1xCPU-1GB"
    "10USD" = "1xCPU-2GB"
    "20USD" = "2xCPU-4GB"
  }
}

variable "storage_sizes" {
  type = map
  default = {
    "1xCPU-1GB" = "25"
    "1xCPU-2GB" = "50"
    "2xCPU-4GB" = "80"
  }
}
variable "templates" {
  type = map
  default = {
    "ubuntu18" = "01000000-0000-4000-8000-000030080200"
    "centos7"  = "01000000-0000-4000-8000-000050010300"
    "debian9"  = "01000000-0000-4000-8000-000020040100"
  }
}

variable "set_password" {
  type = bool
  default = false
}

variable "users" {
  type = list
  default = ["root", "user1", "user2"]
}

variable "plan" {
  type = string
  default = "10USD"
}

variable "template" {
  type = string
  default = "ubuntu18"
}

The above example is really just information storage. It uses the Terraform map variable for the most part which allows you to change the values to be more human-readable.

Variables set in the file can be overridden at deployment. This allows you to reuse the variables file while still customising the configuration at deployment. For example, although set_password is false in the variables file, you could enable it on the command line.

terraform apply -var set_password="true"

In the same way, you could override the other variables as well.

Loading variables automatically

The variables file as described in the previous section can easily be used across many configurations. However, if you need to make more than a couple of changes, it’s worth putting the customisation to a file too.

A variable definitions file uses the same basic syntax as Terraform language files but consists only of variable name assignments.

Terraform automatically loads a number of variable definitions files if named the following way:

Files named exactly terraform.tfvars or terraform.tfvars.json.
Any files with names ending in .auto.tfvars or .auto.tfvars.json.

Now, create a new file to define the custom variables called terraform.tfvars then add the following content.

set_password = "true"
users = ["root", "admin"]
plan = "20USD"
templates = {"ubuntu20":"01000000-0000-4000-8000-000030080200", "centos8":"01000000-0000-4000-8000-000050010300"}
template = "ubuntu20"

If you want to use JSON formatting instead, files with .tfvars.json ending are parsed as JSON objects. The root object properties correspond to variable names.

{
  "set_password": "true",
  "users": ["root", "admin"],
  "plan": "20USD"
  "templates": {"ubuntu20":"01000000-0000-4000-8000-000030200200", "centos8":"01000000-0000-4000-8000-000050010400"},
  "template": "ubuntu20"
}

Next, continue with the section below on how to put the variables in use.

Using variables in resources

The values defined in the variables.tf files can be used in the Terraform plans to avoid hard-coding parameters. The following example uses the highlighted variables to select the parameters for deploying a new cloud server.

Notice the two last variables set in variables.tf which are used as selectors to choose the server plan and OS template.

resource "upcloud_server" "server1" {
  # System hostname
  hostname = "terraform.example.com"

  # Availability zone
  zone = var.zones["amsterdam"]
    
  # Number of CPUs and memory in GB
  plan = var.plans[var.plan]

  template {
    # OS root disk size
    size = lookup(var.storage_sizes, var.plans[var.plan])

    # Template UUID for Ubuntu 18.04
    storage = var.templates[var.template]
  }

  network_interface { 
    type = "public" 
  }
  network_interface { 
    type = "utility" 
  }
  # Include at least one public SSH key
  login {
    user = var.users[0]
    create_password = var.set_password
    keys = [
      var.public_key
    ]
  }

  connection {
    host = self.network_interface[0].ip_address
    type = "ssh"
    user = var.users[0]
    private_key = file(var.private_key_path)
  }
}

Terraform variables are useful for defining server details without having to remember infrastructure-specific values. They are similarly handy for reusing shared parameters like public SSH keys that do not change between configurations.

It is also important that the resource plans remain clear of personal details for security reasons. Using the variables, sensitive information such as private keys and usernames won’t get shared unintentionally.

Defining output variables

Output variables provide a convenient way to get useful information about your infrastructure. As you might have noticed, much of the server details are calculated at deployment and only become available afterwards. Using output variables you can extract any server-specific values including the calculated details.

Configuring output variables is really quite simple. All you need to do is define a name for the output and what value it should correspond to. These can be included in your Terraform plan or in their own file.

Start by creating an output variables file called output.tf and open it for edit.

Add the following three variable definitions in the file to output the server’s IP addresses and hostname after deployment. Replace the server_name with the name of your Terraform host.

output "public_ip" {
  value = upcloud_server.server_name.network_interface[0].ip_address
}

output "utility_ip" {
  value = upcloud_server.server_name.network_interface[1].ip_address
}

output "hostname" {
  value = upcloud_server.server_name.hostname
}

Save the file and test the output by deploying the server with the usual commands below.

terraform plan
terraform apply
upcloud_server.server1: Creation complete after 39s (ID: 00b784aa-15c1-44dc-8252-f4bad865f853)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

hostname = terraform.example.com
private_ip = 10.5.4.82
public_ip = 94.237.45.221

The variables you defined will be shown at the end of the deployment like in the example above. However, you can also retrieve these at any time using the Terraform command. For example, to get the public IP address, you can use the example command below.

terraform output public_ip
94.237.45.221

The same way you could ask Terraform about any other output variables.

Using environmental variables

You can also set sensitive variables in your environment variables with the TF_VAR_ prefix avoiding the need to save them in a file. For example, set your password in your local environmental variables.

export TF_VAR_PASSWORD="password"

You’ll also need to declare the password variable in your variables.tf fie.

variable PASSWORD { default = "" }

The password variable is then usable in the Terraform resources.

  provisioner "remote-exec" {
    inline = [
      "useradd ${var.users[0]}",
      "echo '${var.users[0]}:${var.PASSWORD}' | chpasswd"
    ]
  }

When deployed, the remote execution provisioner will create a new user according to the users variable with the PASSWORD as set in the environmental variable.

Summary

Terraform variables provide many convenient uses for infrastructure management. Dividing your deployment plan and configuration parameters into their own files helps to keep everything in order. However, that is just the general purpose of Terraform variables. Defining your infrastructure using variables is the first step towards the advanced features in Terraform.

Janne Ruostemaa

Editor-in-Chief

  1. How do I calculate the index of list variables automatically ? Like If i need to get all the user names without giving index numbers 0,1,2

  2. Janne Ruostemaa

    Hi Raj, thanks for the question. A list can be accessed both ways, by getting a value like a username by its index, ${var.users[0]}, or by getting the index by searching for a specific user name, index(users, “root”). Alternatively, if you just want to get the total number of user names on a list, you can use length(list). You can find out more at https://www.terraform.io/docs/configuration-0-11/interpolation.html

  3. Hello Janne. How do you manage the fact that the change of a value of a variable does not generate a change in the tfstate? (so, the output plan doesnt recognize a change in the infraestructure). Thanks in advance

  4. Janne Ruostemaa

    Hi Federico, thanks for the question. Changes in variables that affect the infrastructure should be detected and generate a new plan. If the variable is only used in a provisioner, you might want to look into using null resources https://www.terraform.io/docs/providers/null/resource.html

  5. hi Janne, created VM using terraform. Now want to create second VM using same code but by changing hostname varible, Size, etc for second VM. when i plan the code says it will delete existing VM, which is expected. how do i create second VM without deleting existing one? lets say i have to create 2 VMs everyday for different team/departments.

    Second question is, how do i create multiple local users on windows server?

  6. Janne Ruostemaa

    Hi there, thanks for the question. You can deploy two servers together by adding another resource block in your current Terraform plan file. Alternatively, if you wish to deploy the two servers independently, simply make a new directory for the second server and copy in the terraform plan file and any variable files you might have, ignore the tfstate file and .terraform directory.

    As for the Windows user accounts, open the Windows Settings > Accounts > Other people and click Add someone else to this PC. You can find further instructions at Microsoft support pages.

  7. Hi Janne, is possible use a output as variable ? for example I make a VPC and I need srtorage the id vpc in varibale.

  8. Janne Ruostemaa

    Hi Martin, you can use information directly from another resource by defining dependency. The Terraform tutorial on dependencies likely uses your use-case as an example.

  9. We’are using var. variable name from the resource file, is the “var” object ? or what

  10. Janne Ruostemaa

    Hi there, thanks for the question. Most variables use simple types like string but it’s possible to define object variables as well. You can see an example of this in the Terraform input variables documentation.

  11. Hello Janne Ruostemaa,

    I am using list type variable and I have more than one value in that list as a string but I want make default one from them how can I do that?

    Thanks in Advance.

  12. Janne Ruostemaa

    Hi there, thanks for the question. The default value of a list includes all items entered in the default but you can use a selector variable which defines the “default” item in the list. For example:

    variable "users" {
        type = list
        default = ["root", "user1", "user2"]
    }
    variable "user" { default = 0 }

    Then in the resource definition, the root user is selected by default.

    login {
        user = var.users[var.user]
        create_password = var.set_password
        keys = [
            var.public_key
        ]
    }

    It can also be overwritten at deployment, e.g.

    terraform apply -var user="1"

    The same works with maps which, depending on your use case, might be easier.

  13. trying to find a way in terraform 11 to output all attributes for a variable.

  14. Janne Ruostemaa

    Hi there, thanks for the question. You could use the joker (*) to output all values from a list or map, e.g.

    output "zones" {
      value = var.zones[*]
    }
  15. How can I change /set value of a map variable’s item in main.tf file
    for example I have following in my variable list
    “`variable “appservice_as1” {
    type = map(string)
    default = {
    “mysetting1” = “something”
    “mysetting2” = “else”
    }
    }“`
    and now I want to update appservice_as1 -> mysetting2 to some dynamic value from a created resource `azurerm_resource_group.mynewresourcerg.name`

  16. Janne Ruostemaa

    Hi Balinder, thanks for the comment. While it’s possible to set map values again, for example, on the command line, the latest value overwrites the previous. Meaning, you’ll need to redefine the entire map which might not be what you are looking for. You can use values from some resources to define other resources which create a dependency that you’ll need to be mindful of.

  17. Hello,

    is it possible to share variables among different directories?
    I’m explaining the scenario I’d like to have:

    here is the folder tree:
    cloudwatch/
    vars.tf –> here i want to define all variables
    service1/
    alarms.tf –> here i want to use the variables
    …………
    service2/
    alarms.tf –> here i want to use the variables too

    With ${var.blabla} I have no reference of the variable, it should be something like ../${var.blabla} but I don’t know if it is possible, and the syntax.

    Thanks in advance,
    Leonardo

  18. Janne Ruostemaa

    Hi Leonardo, thanks for the question. You can choose the variable file during execution using the -var-file parameter. For example, while running the plan command on alarms.tf:

    terraform plan -var-file ../cloudwatch/vars.tfvars
  19. I know it’s easy to override a value at commandline, like terraform plan -var private_key_path=”/a/different/path”

    You’ve defined a list of users. How can I change that value on commandline (powershell)?

    I’ve tried terraform plan -var users=[“root”]
    fails. -var users='[“root”]’. fails. -var ‘users=[“root”]’ fails. -var=’users=[“root”]’ fails. I know I must have doublequotes around root.

    I do know setting an environment variable works. but I don’t want to use that.

    If I try overriding a list’s default value via commandline, I get variables not allowed.

  20. Janne Ruostemaa

    Hi Marc, thanks for the question. Setting list variables in the command line should use the following format -var='users=["root"]' Note that the type of quotation marks matters.

  21. Hello. Nice article.
    Got here looking for a way to define a vars file that feeds variables to a module. I’m trying to use an autovars file (specific to one of the environments say staging) to feed variables to the modules I use. But I don’t know if that is even possible. Do you know if that is possible?

  22. Janne Ruostemaa

    Hi Francisco, thanks for the question. You can have multiple variable files and load the correct one depending on your use case by selecting it on the command line. For example,

    terraform apply -var-file="staging.tfvars"
  23. weird. I cannot get it to work.
    variable “aad_allowed_tenants” {
    type = list
    // default = [“1asdf”]
    }

    terraform plan -var env_name=marcdev -var enable_azure_active_directory=”true” -var aad_client_id=myid -var aad_client_secret=mysecret -var=’aad_allowed_tenants=[“asdf”]’

    │ Error: Variables not allowed

    │ on line 1:
    │ (source code not available)

    │ Variables may not be used here.


    │ Error: No value for required variable

    │ on variables.tf line 64:
    │ 64: variable “aad_allowed_tenants” {

    │ The root module input variable “aad_allowed_tenants” is not set, and has no default value. Use a -var or
    │ -var-file command line argument to provide a value for this variable.

    those are single quotes and double quotes.

  24. Marc Towersap

    I know my problem now. THis fails for powershell. If I flip to bash, run the exact same command (invoking terraform.exe) it works.

  25. Janne Ruostemaa

    Hi Marc, thanks for the comment. Glad to hear you got it working and interesting to learn about the difference between Windows and Linux implementations.

  26. That was an excellent article on Terraform Variables!
    I want to store my password or some other sensitive information to my terraform variable file. How can I protect it from not been accessible to anyone else in my team. Do you have any other articles related to Terraform/CICD variables ?
    your help would be appreciated. Thanks in advance !

  27. Janne Ruostemaa

    Hi Keerthana, thanks for the question. You can save your password in your local environmental variables which prevents it from getting accidentally shared. We’ve added a section in the tutorial to explain how to use environmental variables in Terraform.

  28. MARC Errol TOWERSAP

    Yeah, the problem is indeed powershell. it is trying to parse the arguments, and totally mangling it by the time it realizes, I need to pass it to terraform. terraform gets some weird stuff and pukes.

    FYI for other readers, do NOT bother wasting time using –% or backtic’s (powershell escape character). The fix (if you wish to use powershell) is to use three doublequotes inside the []s. for my real argument, I am using
    -var ‘aad_allowed_tenants=[“””my azure tenant id”””]’
    weird but it works.

  29. Thank you very much for your prompt response !
    I am loading few content to APIGEE organization using Terraform via Gitlab-CICD pipeline. What is the best way I can store my Access_Token and other credentials/ sensitive information?
    your guidance would be appreciated ! Thanks in advance !

  30. Janne Ruostemaa

    You could declare the access_token in your variables.tf file, then export it to your local environmental variables with the TF_VAR_ prefix + your variable name, e.g.

    export TF_VAR_access_token="your-token"
  31. Thank you very much. Let me try this way !

  32. Do you have any article/project related to storing sensitive variables in Vault and use it inside
    gitlab-ci.yaml?

  33. Janne Ruostemaa

    While we do not have tutorials on Vault ourselves, you may find the GitLab guide useful. Note that reading access to Vault seems to require GitLab Premium.

  34. Hi @Janne Ruostemaa,

    How can i access the terraform output variables in jenkins pipeline job.

  35. Janne Ruostemaa

    Hi Sunny, thanks for the question. You can use the terraform output {variable} to get the value of any of your output variables. Jenkins Pipeline should allow for running shell commands for calling on Terraform output variables.

  36. Does export TF_VAR_testvar=”test1″ value override testvar=”test2″ defined in variables.tfvars ? as I set this based on custom variable testvar value during my pipeline runtime. If not selected I would like to use the variables.tfvars by default. Please provide your suggestion.

  37. Janne Ruostemaa

    Hi Damo, thanks for the question. Terraform load variables from different sources in a certain order where the last loaded value will be used. More specifically, the variables are loaded in the following order:
    1. Environment variables
    2. The terraform.tfvars file, if present.
    3. The terraform.tfvars.json file, if present.
    4. Any *.auto.tfvars or *.auto.tfvars.json files, processed in lexical order of their filenames.
    5. Any -var and -var-file options on the command line, in the order they are provided.
    You can use the variable loading order to your advantage to choose where to store your default values and how to override them when needed.

  38. Can I have an class- like variable that contains multiple fields. For example, instead of two strings – one each for resource_group_name and resource_group_location, I would like a resource group class resource_group that contains a name and location field.

  39. Janne Ruostemaa

    Hi Tom, thanks for the question. Structural Types allow you group together even mixed type variable.

    variable "resource_groups" {
      type = list(object({
        name = string
        location = string
      })
      default = [
        {
          name = "group1"
          location = "new york"
        },
        {
          name = "group2"
          location = "london"
        },
        {
          name = "group3"
          location = "singapore"
        }
      ]
    }

    Which you can then address in your configuration

    var.resource_group[1].location
  40. This is really nice post and very easy to use and understand.

  41. Good article refer regarding terraform variables
    I have a unique requirement that I have to assign list of strings from jenkins file to tfvars variable
    I can hardcode the list to the variable but then the requirement has changed, list of strings will be dynamically generated from the jenkins which I have to assign it to tfvars variable.

    something like this

    az_list: [“az1″,”az2″,”az3”]

    env.tfvars file

    Availability_Zones : [“${var.az_list}”]

    the above one is clearly not working, How can I go about this

  42. Janne Ruostemaa

    Hi Raos, thanks for the questions. If the values output by Jenkins are not sensitive, you could save them into an environmental variable and use that when deploying with Terraform. For example, export TF_VAR_az_list='[“az1″,”az2″,”az3”]’

  43. I believe you are essentially telling TF to run this:
    Availability_Zones: [” [“az1″,”az2″,”az3”]”]
    Which i don’t believe is valid.

    Try changing two things:

    1. Remove the brackets from Availability_Zones;
    2. Remove the “”s wrapping var.az_list, from Availability_Zones

    Example:

    env.tfvars:
    az_list: [“az1″,”az2″,”az3”]

    main.tf:
    Availability_Zones: [var.az_list]

    Hope that helps :)

  44. 1. Remove curly brackets and $ from Availability_Zones**
    2. Remove “”s

    Sorry that wasn’t clear, the example above illustrates exactly what I intended on advising :)

  45. Great article explaining the different Terraform variables. Thank you.

Leave a Reply to aditya

Your email address will not be published. Required fields are marked *

Back to top