Understanding Terraform variables, local values, and outputs
Variables enhance code flexibility by allowing for placeholders within blocks of code. Variables can represent different values whenever the code is reused. Terraform distinguishes between its variable types by their modular scope. Input variables are external values that can be injected into a module, output values are internal values that can be shared externally, and local values always stay within their original scope.
Variables
AWS CloudFormation uses parameters to represent custom values that can be set and reset from one stack
deployment to the next. Similarly, Terraform uses input
variables
variable "thing_i_made_up" { type = string } variable "random_number" { default = 5 } variable "dogs" { type = list(object({ name = string breed = string })) default = [ { name = "Sparky", breed = "poodle" } ] }
To access Sparky’s breed within the configuration, you’d use the variable
var.dogs[0].breed
. If a variable has no default and is not classified as
nullable, then the value of the variable must be set for each deployment. Otherwise,
it’s optional to set a new value for the variable. In a root module, you can set current
variable values on the command line
# terraform.tfvars dogs = [ { name = "Sparky", breed = "poodle" }, { name = "Fluffy", breed = "chihuahua" } ] random_number = 7 thing_i_made_up = "Kabibble"
The value for dogs
in this example terraform.tfvars file would override the default value in the variable
declaration. If you’re declaring variables within a child module, you can set the
variable values directly within the module declaration block, as shown in the following
example.
module "my_custom_module" { source = "modulesource/custom" version = "0.0.1" random_number = 8 }
Some of the other arguments you can use when declaring a variable include:
-
sensitive
– Setting this totrue
prevents the variable value from being exposed in Terraform process outputs. -
nullable
– Setting this totrue
allows the variable to have no value. This is convenient for variables where a default is not set. -
description
– Add a description of the variable to the metadata for the stack. -
validation
– Set validation rules for the variable.
One of the most convenient aspects of Terraform variables is the ability to add one or more validation objects within the variable declaration. You can use validation objects to add a condition that the variable must pass or else the deployment fails. You can also set a custom error message to show whenever the condition is violated.
For example, you're setting up a Terraform configuration file that members of your team will run. Before deploying the stacks, a team member needs to create a terraform.tfvars file to set an important configuration value. To remind them, you could do something like the following.
variable "important_config_setting" { type = string validation { condition = length(var.important_config_setting) > 0 error_message = "Don't forget to create the terraform.tfvars file!" } validation { condition = substr(var.important_config_setting, 0, 7) == "prefix-" error_message = "Remember that the value always needs to start with 'prefix-'" } }
As shown in this example, you can set multiple conditions inside a single variable. Terraform only shows error messages for failed conditions. In this way, you can enforce all kinds of rules on variable values. If a variable value causes a pipeline failure, you would know exactly why.
Local values
If there are any values within a module that you’d like to alias, use the
locals
keyword rather than declaring a default variable that will never be
updated. As the name suggests, a locals
block contains terms that are scoped
internally to that specific module. If you want to transform a string value, such as by adding
a prefix to a variable value for use in a resource name, using a local value might be a good
solution. A single locals
block can declare all local values for your module, as
shown in the following example.
locals { moduleName = "My Module" localConfigId = concat("prefix-", var.important_config_setting) }
Just remember that when you’re accessing the value, the locals
keyword
becomes singular, such as local.LocalConfigId
.
Output values
If Terraform input variables are like CloudFormation parameters, then you could say that
Terraform
output values output
block to the second
module.
output "module_id" { value = local.module_id } Then in the first module you could use it like this: module "first_module" { source = "path/to/first/module" } resource "example_resource" "example_resource_name" { module_id = module.first_module.module_id }
Because Terraform output values can be used within the same stack, you can also use the
sensitive
attribute in an output
block to suppress the value from
being displayed in the stack output. Additionally, an output
block can use
precondition
blocks in the same way that variables use validation
blocks: to ensure variables follow a certain set of rules. This helps make sure that all
values within a module exist as expected before proceeding with deployment.
output "important_config_setting" { value = var.important_config_setting precondition { condition = length(var.important_config_setting) > 0 error_message = "You forgot to create the terraform.tfvars file again." } }