You now have enough Terraform knowledge to create useful configurations, but the configuration still hardcodes the project name, zone, and other arguments. To become truly shareable and version controlled, we need to parameterize the configurations. This page introduces input variables as a way to do this.
»Defining Variables
First, convert a few of the hardcoded values into variables.
Create another file called variables.tf
with the following contents:
variable "project" { }
variable "credentials_file" { }
variable "region" {
default = "us-central1"
}
variable "zone" {
default = "us-central1-c"
}
Tip:: Terraform loads all files ending in .tf
in a directory, so it
doesn't matter to terraform where your variables are defined. We recommend
defining them in their own file to make your configuration easier to organize
and understand.
This file defines three variables within your Terraform configuration. The first
one has an empty block: { }
. The other two set defaults. If a default value is
set, the variable is optional. Otherwise, the variable is required. If you run
terraform plan
now, Terraform will prompt you for the values for project
and
credentials_file
.
»Using Variables in Configuration
Next, update the GCP provider configuration in main.tf
to use these new
variables.
terraform {
required_providers {
google = {
source = "hashicorp/google"
}
}
}
provider "google" {
version = "3.5.0"
- credentials = file("<NAME>.json")
+ credentials = file(var.credentials_file)
- project = "<PROJECT_ID>"
- region = "us-central1"
- zone = "us-central1-c"
+ project = var.project
+ region = var.region
+ zone = var.zone
}
Variables are referenced with the var.
prefix.
»Assigning Variables
There are several ways to assign variables, depending on your needs.
»Command-line flags
You can set variables directly on the command-line with the -var
flag. Any
command in Terraform that inspects the configuration accepts this flag, such as
apply
, plan
, and refresh
:
$ terraform plan -var 'project=<PROJECT_ID>'
var.credentials_file
Enter a value:
Setting variables this way will not save them, and they'll have to be passed
this way every time you run terraform. Notice that since you didn't enter all the variables on the command line, Terraform prompts you for the remaining ones. Cancel the run with Ctrl+C
.
»From a file
To persist variable values, create a file and assign variables within
this file. Create a new file named terraform.tfvars
with the following
contents:
project = "<PROJECT_ID>"
credentials_file = "<NAME>.json"
Note: Be sure to replace <PROJECT_ID>
with your project's ID, and
<NAME>
with the name of your credentials file.
Terraform automatically loads all files which match terraform.tfvars
or
*.auto.tfvars
present in the current directory to populate variables. You can
also specify a file to load with the -var-file
command line argument.
These files are the same syntax as Terraform configuration files.
For security reasons, we recommend never saving usernames and passwords to
version control. Your terraform configuration will probably need these secret
values, though. One solution is to create a local secret variables file and use
-var-file
to load it. You can also use multiple -var-file
arguments in a single command, with some
checked in to version control and others not checked in.
»From environment variables
Terraform will read environment variables in the form of TF_VAR_name
to find
the value for a variable. For example, in this configuration the TF_VAR_region
environment variable could be used to set the region
terraform variable.
»UI Input
If you execute terraform apply
with some variables unspecified, Terraform will
ask you to input their values interactively. These values are not saved, but
this provides a convenient workflow when getting started with Terraform. UI
input is not recommended for everyday use of Terraform.
Note: In Terraform versions 0.11 and earlier, UI input is only supported for string variables. List and map variables must be populated via one of the other mechanisms. Terraform 0.12 introduces the ability to populate complex variable types from the UI prompt.
»Variable Defaults
If no value is assigned to a variable via any of these methods and the variable
has a default
key in its declaration, that value will be used for the
variable.
»Variable Types
Terraform supports a number of different variable types. The most common ones are described below, and you can read the Terraform documentation for a complete list.
»Strings
If no type is specified, then Terraform assumes a variable is a string. Like most programming languages, strings are just a sequence of characters. You can also explicitly define a variable as a string.
Explicitly define project
as a string by updating variables.tf
.
variable "project" {
+ type = string
}
This usually isn't necessary, though, since the string type would otherwise be assumed.
»Numbers
A number, like a string, is pretty straightforward. Any valid integer or floating point value is allowed. When processing your configuration, Terraform will generally do the right thing when converting from a string to a number. So defining the number type is more about ensure the correct type of input is used.
variable "web_instance_count" {
type = number
default = 1
}
»Lists
Like lists or arrays found in many programming languages, a list is a sequence of values.
Add a variable to variables.tf
to define the CIDR network blocks to your
configuration as a list by either setting the default to a list, or setting the
type to list
.
variable "cidrs" { default = [] }
You can specify list values in a tfvars file as well. Add the following to
terraform.tfvars
:
cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ]
We'll use these values later on in this tutorial.
»Maps
Maps are a way to create variables that are lookup tables. Terraform maps are similar to data structures found in programming languages, sometimes referred to as maps or dictionaries.
In your configuration, the machine type is currently set to f1-micro
. You
might want different machine types for different environments. You can use a map
to accomplish this.
Add a map that defines the machine types for each environment to your
variables.tf
file.
variable "environment" {
type = string
default = "dev"
}
variable "machine_types" {
type = map
default = {
dev = "f1-micro"
test = "n1-highcpu-32"
prod = "n1-highcpu-32"
}
}
As with lists, a variable can have a map type assigned explicitly, or it can be implicitly declared as a map by specifying a default value that is a map.
Use the machine_types
map to set the machine type for the first vm_instance
in main.tf
.
resource "google_compute_instance" "vm_instance" {
name = "terraform-instance"
- machine_type = "f1-micro"
+ machine_type = var.machine_types[var.environment]
tags = ["web", "dev"]
# ...
}
The square-bracket index notation used here is how we access values inside a map type variable.
Run terraform plan
. Because of the default value we used for environment
,
you should see that there are no changes to apply.
Maps can also use a static value lookup directly. For example:
var.machine_types["dev"]
will resolve to "f1-micro"
.
»Assigning Maps
We set defaults above, but maps can also be set using the -var
and
-var-file
values. For example:
$ terraform apply -var 'machine_types={ dev = "f1-micro", test = "n1-standard-16", prod = "n1-standard-16" }'
Note: Even if every key will be assigned as input, the variable must be
established as a map in your configuration by setting its type to map
or its
default to {}
.
You can also set a map's keys from a .tfvars
file after it is defined as a
map.
Starting with these variable definitions in variables.tf
.
variable "region" {
type = string
default = "us-central1"
}
variable "machine_types" {
type = map
default = {
dev = "f1-micro"
test = "n1-highcpu-32"
prod = "n1-highcpu-32"
}
}
Specify values in your terraform.tfvars
file.
region = "us-central1"
machine_types = {
dev = "f1-micro"
test = "n1-highcpu-32"
prod = "n1-highcpu-32"
}
Once again, running terraform apply
at this point will have no effect, because
the value "f1-micro"
from the map is the same as the hard coded value used
originally.