Understanding Terraform variables, local values, and outputs - AWS Prescriptive Guidance

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, or variables. Variables can be declared anywhere in a Terraform configuration file and are usually declared with the required data type or default value. All three of the following expressions are valid Terraform variable declarations.

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, as environment variables, or in the terraform.tfvars file. The following example shows how to enter variable values in the terraform.tfvars file, which is stored in the module's top-level directory.

# 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 to true prevents the variable value from being exposed in Terraform process outputs. 

  • nullable – Setting this to true 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 are like CloudFormation outputs. Both are used to expose values from within a deployment stack. However, because the Terraform module is more ingrained into the fabric of the tool, Terraform output values are also used to expose values within a module to a parent module or other child modules, even if those modules are all within the same deployment stack. If you’re building two custom modules and the first module needs to access the ID value of the second module, then you’ll need to add the following 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." } }