Terraform Variables: A Comprehensive Guide to Dynamic Infrastructure Configuration

By Bender Bending Rodríguez on Mar 19, 2024
Terraform Variables

In this guide, we will explore Terraform variables and the part they play when creating infrastructure. You will learn how to define variables and use these variables, including looping constructs, to create multiple resources efficiently. Infrastructure engineers can write reusable Terraform code across various customizable environments to meet specific deployment needs by leveraging variables.

This guide aims to equip you with a thorough understanding of defining, using, and managing variables in Terraform, ensuring your infrastructure is scalable and maintainable—and many more aspects of dynamic infrastructure setup, including looping and sensitive data handling.

Introduction to Terraform Variables

Terraform variables are placeholders for values that can be assigned to various resources, such as the name of an S3 bucket, an EKS cluster, or even the number of machines you want to create.

Using variables, you can easily customize and reuse Terraform code without hardcoding specific values directly into your infrastructure setup.

This flexibility allows for seamless adjustments to different environments or resource requirements by modifying the assigned terraform variables value.

Terraform variables are most commonly declared using the variable block within your Terraform code, with the following syntax, although there are other ways to define them.

variable "region" {
description = "The AWS region to deploy resources"
type = string
default = "us-east-1"
}

variable_name

Identifies the variable for referencing throughout your code.

type_constraint

Specifies the allowed value types such as ‘string,’’ number’, ‘bool,’ ‘list,’ or ‘map.’

default_value

Defines a default value for the variable, ensuring smooth configuration adjustments.

Types of Terraform Variables

Input Variables

Input variables in Terraform pass values from outside the configuration or module. Input variables allow for dynamic values to be assigned to resource attributes. Input variables are the most common and widely used method to pass values from outside the configuration or module. Input variables act as inputs to resources and can be defined along with specific attributes like type, default, value, and description.

For instance, consider the input variables values in following example:

variable "instance_type" {
type = string
default = "t2.micro"
description = "The type of EC2 instance to create"
}

The above is a declaration of an input variable named instance_type, which specifies the type of EC2 instance to create. This variable declaration is a string with a default value of “t2.micro” and a description to guide the user.

Local Variables

Local variables simplify and organize configurations by allowing you to define values that are reused within a Terraform resource. These variables can store static and dynamic data, making your configuration more maintainable and readable. They are handy for reducing repetition, enhancing clarity, and managing complex expressions or values used in multiple places.

locals {
region_identifier = "us-east-1"
}

The provided code includes a local variable named region_identifier with a value of “us-east-1”. This local variable consistently names resources and their output variable values, ensuring a coherent naming convention that includes the region identifier.

Passing Terraform Variables as Input

Variable Definitions File (variables.tf)

A variables.tf is a file where you generally define all the variables in one place. This is a common practice for defining the variable definitions files.

Variable Values File (.tfvars)

Variable value files, such as terraform.tfvars or *.auto.tfvars, offer a convenient method for managing Terraform’s environment-specific configurations.

main.tf

provider "aws" {
region = var.region
}
resource "aws_instance" "example_instance" {
ami = "ami-0d7a109bf30624c99"
instance_type = var.instance_type
tags = {
Name = var.instance_name
}
}

variables.tfvars

region = "us-east-1"
instance_name = "example-instance"
instance_type = "t2.micro"

You can use the —var-file flag during the terraform apply command to run Terraform with a variable definition and value files such as .tfvars or YAML files. This allows you to choose a file containing the variable values you want to use for the run.

$ terraform apply -var-file="variables.tfvars"

Runtime Variables

Runtime variables are provided at the command line when running Terraform commands like terraform plan or terraform apply. You could use this process to pass the variable at runtime.

$ terraform apply -var "instance_type=t2.micro"

Defining Variables in Terraform Modules

We’ve learned about using variables. Now, let’s delve into Terraform modules, another key feature that enhances the reusability and maintainability of your Terraform code. A module in Terraform is a container for multiple resources used together. It’s a way to package and encapsulate related resources and variables.

We are now going to create an AWS EC2 instance using Terraform modules. This approach allows us to encapsulate the configuration into a reusable module.

First, create a directory for your module, e.g., modules, and add the following to your module.

variables.tf

variable "instance_size" {
description = "Size of the EC2 instance"
type = string
default = "t2.micro"
}
variable "ami" {
description = "The AMI to use for the instance"
type = string
}

main.tf

resource "aws_instance" "module_instance" {
ami = var.ami
instance_type = var.instance_size
}
output "instance_id" {
value = aws_instance.module_instance.id
}

In the project’s root directory, main.tf file that calls the module, which includes main.tf and variables.tf . Make sure to replace “./modules” with the correct path to your module if it’s different.

main.tf

module "ec2_instance" {
source = "./modules"
instance_size = "t3.large"
ami = "ami-0d7a109bf30624c99"
}

Run the terraform init command in your root directory to initialize Terraform.

$ terraform init
Initializing the backend...
Initializing modules...
- ec2_instance in modules
# module.ec2_instance.aws_instance.module_instance will be created

Terraform Variable vs. Terraform Output

FeatureTerraform VariableTerraform Output
PurposeUsed to pass external input values back into a Terraform configuration.Used to return information about the infrastructure built or modified to the user.
ArgumentsInput to the Terraform configuration.Output to the end user about the build of the infrastructure or modifications.
UsageAllow users to customize configurations without altering the code.Enable users to retrieve and use information from the infrastructure, such as IP addresses, keys, or IDs.
SyntaxDefined within a variable block and referenced with var.<variable_name>Defined within an output block and referenced with output.<output_name> or via CLI.
Examplevariable "instance_size" { default = "t2.micro" }output "instance_zones" { value = aws_instance.app_server.availability_zone }

Custom Validation Rules

Custom validation rules in Terraform allow you to define constraints on the values of your variables, ensuring that user-provided values meet specific criteria before Terraform applies the configuration.

For example, to enforce a naming convention for EC2 instances in Terraform and ensure that all infrastructure components adhere to a standard naming pattern, such as starting with "terrateam" you can use custom validation within the instance_name variable block.

Custom validation rules in Terraform allow you to specify conditions string values that input variables must meet. If the conditions are not met, Terraform will prevent the plan or apply from proceeding, thus ensuring compliance with your naming standards.

Here’s an example of defining the instance_name variable with a custom validation rule to enforce that every instance name starts with "terrateam" followed by a word character (letter, digit, or underscore):

main.tf

variable "instance_name" {
type = string
description = "The name of the EC2 instance, which must start with 'terrateam'."
validation {
condition = can(regex("^terrateam", var.instance_name))
error_message = "The instance name must start with 'terrateam'."
}
}
resource "aws_instance" "example_instance" {
ami = "ami-0d7a109bf30624c99"
instance_type = "t2.micro"
}

The regex function is utilized to match the beginning of the instance_name variable value against the pattern ^terrateam, where ^ denotes the start of the string, and terrateam specifies the required prefix for the instance name. The can function is then used to safely evaluate the regex function. It returns true if the pattern matches the beginning of the instance_name value and false otherwise. If the evaluation returns false, indicating that the instance name does not start with the required prefix, Terraform will display the specified error message and prevent the configuration from being applied.

Run the terraform apply command, if we chose the id terrateam-dev01:

$ terraform apply
var.instance_name
The name of the EC2 instance, which must start with 'terrateam'.
Enter a value: terrateam-dev01
aws_instance.example_instance: Refreshing state... [id=i-02f1cffe1fd6e6ead]

We should receive the following output:

aws_instance.terrateam_instance: Creating...
aws_instance.terrateam_instance: Still creating... [10s elapsed]
aws_instance.terrateam_instance: Still creating... [20s elapsed]
aws_instance.terrateam_instance: Still creating... [30s elapsed]
aws_instance.terrateam_instance: Creationg complete after 36s [id=i-03642021922fa9693]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

If we change the value of instance_name to something that is not starting with “terrateam”, like “terateam”, the terraform apply will fail:

$ terraform apply
var.instance_name
The name of the EC2 instance, which must start with 'terrateam'.
Enter a value: terateam-dev01
var.instance_name is "terateam-dev01"
The instance name must start with 'terrateam'.
This was checked by the valiation rule at main.tf:238,3-13.

Use of null Value in Terraform

A null value represents the absence of a value. It can be useful in various scenarios, such as conditionally omitting an argument in a resource or module or representing an optional attribute that has not been set. Using null can help create more flexible and dynamic Terraform configurations.

variable "additional_tags" {
type = map(string)
description = "Additional tags to apply to the EC2 instance (optional)"
default = null
}
resource "aws_instance" "terrateam_dev_instance" {
ami = "ami-0d7a109bf30624c99"
instance_type = "t2.micro"
tags = var.additional_tags ! = null ? merge({ Name = "terrateam_dev_instance" }, var.additional_tags) : { Name = "terrateam_dev_instance" }
}

The provided Terraform code snippet demonstrates how to use the null value to create optional functionality within a Terraform configuration. Specifically, it showcases an optional additional_tags variable for an AWS EC2 instance. This variable is a map of strings intended for additional tags that users might want to apply to the EC2 instance. By default, the map variable is set to null, indicating that it is optional and does not require a value to be provided by the user.

aws_instance.terrateam_dev_instance: Creating...
aws_instance.terrateam_dev_instance: Still creating... [10s elapsed]
aws_instance.terrateam_dev_instance: Still creating... [20s elapsed]
aws_instance.terrateam_dev_instance: Creation complete after 26s [id=i-0ba0c6db1e318f56d]

Sensitive Data Handling

Handling sensitive data such as passwords securely is crucial in infrastructure management. In our Terraform configuration, we define two variables: username and password. The password variable is marked as a sensitive value, ensuring its value is not exposed in plaintext in the Terraform state or console output.

variable "username" {
type = string
description = "Username for the local password"
}
variable "password" {
type = string
description = "Password for the local password"
sensitive = true
}
output "username_value" {
value = var.username
}
output "password_value" {
value = var.password
sensitive = true
}
$ terraform apply
var.password
Password for the local password
Enter a value:
var.username
Username for the local password
Enter a value: admin
Outputs:
password_value = <sensitive>
username_value = "admin"

With this configuration, Terraform securely handles the data of sensitive variables, such as passwords. After applying the configuration, the values of the variables are printed to the console, ensuring that sensitive information remains protected.

Using for_each Loops in Variables

Using for_each allows you to efficiently create multiple resources with similar configurations. This approach is particularly beneficial when managing a large number of resources that share common characteristics but require individual configuration.

When you use for_each, Terraform creates a separate instance of the resource or module for each element in the provided map or set. Each instance is managed independently, with its own creation, update, and destruction lifecycle. This is particularly useful when you want to create similar resources that need to be configured slightly differently, such as having different names or settings.

Creating Multiple S3 Buckets with for_each

Let’s create five S3 buckets using a set of strings to define their names.

First, we define a local variable with a set of the desired bucket names:

main.tf

locals {
  bucket_names = toset(["terrateam-assets", "terrateam-media", "terrateam-logs", "terrateam-data", "terrateam-backup"])
}

Next, we use the for_each in the S3 bucket resource block to create a bucket for each name in the set:

resource "aws_s3_bucket" "buckets" {
for_each = local.bucket_names
bucket = each.value
acl = "private"
tags = {
Name = each.value
}
}

for_each iterates over the local.bucket_names set, and each. value represents the current bucket name being processed. The bucket attribute is set to each. value, which creates five S3 buckets with the names specified in the set.

With the configuration defined, run terraform init and terraform apply:

aws_s3_bucket.buckets["terrateam-media"]: Creating
aws_s3_bucket.buckets["terrateam-assets"]: Creating
aws_s3_bucket.buckets["terrateam-data"]: Creating
aws_s3_bucket.buckets["terrateam-backup"]: Creating
aws_s3_bucket.buckets["terrateam-logs"]: Creating
aws_s3_bucket.buckets["terrateam-data"]: Creation complete after 7s [id=terrateam-data]
aws_s3_bucket.buckets["terrateam-logs"]: Creation complete after 7s [id=terrateam-logs]
aws_s3_bucket.buckets["terrateam-media"]: Creation complete after 7s [id=terrateam-media]
aws_s3_bucket.buckets["terrateam-assets"]: Creation complete after 7s [id=terrateam-assets]
aws_s3_bucket.buckets["terrateam-backup"]: Creation complete after 7s [id=terrateam-backup]
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Integration with Terrateam

Terrateam is Terraform GitOps CI/CD. It automates Terraform operations by responding to GitHub events, making infrastructure management as seamless as code commits, pull request comments, and merges.

Key Features

Infrastructure as Code (IaC): Terrateam facilitates the use of Terraform, an IaC tool, allowing teams to define and provision infrastructure using simple, declarative configuration files.

Automated Terraform Workflows: Terrateam automates the execution of Terraform plans and applies operations in response to GitHub events, ensuring consistent infrastructure updates.

Full Example

To set up Terrateam in your GitHub repository and ensure it works seamlessly with your Terraform configurations, including main.tf for resource definitions, provider.tf for provider configurations, and variables.tf for input variables, you must carefully create and include three critical files:

  • config.yml
  • terrateam.yml
  • trustpolicy.json

The config.yml provides the essential configuration needed to run Terraform in the pipeline. It allows you to define how Terrateam interacts with your Terraform configurations and GitHub repository

The terrateam.yml file is a GitHub Actions workflow file that must be stored in your Terraform repository’s .github/workflows directory. This file specifies the GitHub Action Terrateam will run when changes are made to your repository, such as running a terraform plan and terraform apply.

The trustpolicy.json file is necessary for setting up AWS Authentication and Authorization with Terrateam. It defines the trust relationship policy that allows Terrateam to assume an AWS IAM role on your behalf, facilitating secure and seamless interaction with AWS resources.

For the complete code, visit here.

The first thing you need to do is update your infrastructure by modifying the Terraform files in your repository. This could involve changing resource configurations in main.tf, adjusting provider settings in provider.tf, or updating variable values in variables.tf.

Once your changes are ready, create a pull request in GitHub. This PR should include the modifications in your Terraform configurations. Opening a PR triggers Terrateam to run a Terraform plan operation, which shows the potential effects of merging these changes without applying them.

In this example we’ll create a simple S3 bucket.

GitHub Status Checks

Understanding Terrateam

Terrateam Plan

You can automate the planning and application of resource changes like S3 buckets. For instance, if your Terraform configuration includes an S3 bucket, Terrateam can help manage the lifecycle of this resource through pull requests, applying changes only after thorough review and approval, ensuring that your S3 bucket configurations are always up to date and correctly provisioned.

Terrateam Pre Hooks

Pre hooks for Terraform plan operations let you run specific commands or scripts before executing the Terraform plan. This can be useful for setting up the environment, such as configuring variables or downloading necessary dependencies. It ensures that the Terraform plan operation has everything it needs to run successfully and can accurately reflect the changes that will be applied to your infrastructure.

Terrateam Apply

Terrateam can automatically run a Terraform apply operation when a pull request is merged. This applies the changes in the pull request to your infrastructure, ensuring that your infrastructure is always in sync with your repository’s state. This ensures that your main branch always reflects the current state of your infrastructure.

Terrateam Post Hooks

Post hooks in Terrateam allow you to execute custom commands or scripts after Terraform operations. For example, after a Terraform apply operation, you might want to run a script that clears the cache or restarts services affected by the infrastructure changes. This ensures that any necessary cleanup or follow-up actions are automatically taken care of, maintaining your infrastructure’s health and performance.

After we comment terrateam apply in the pull request, our bucket is created via Terrateam.

$ aws s3 ls
2024-03-19 09:59:33 235235 terrateam090
$

Frequently Asked Questions

1. What is a variable set in Terraform?

A variable set in Terraform is a collection of variables that can be applied to multiple workspaces or configurations. It allows you to reuse the same set of variables across different Terraform projects or modules, ensuring consistency and simplifying the management of common variables. For example, in Terraform, a variable set for deploying an AWS S3 bucket across multiple environments might include variables like bucket_prefix and region, allowing for consistent bucket naming and location settings across development, staging, and production workspaces.

2. What does ${} do in Terraform?

In Terraform, the ${} syntax is used for interpolation, which allows you to insert the value of a variable or an expression into a string. For example, consider the following code:

Terrateam-${var.instance_type}-${formatdate("YYYYMMDD", timestamp())}

This uses interpolation to create a tag for the EBS snapshot that includes the instance type and the current date, providing a clear and informative identifier for the backup. This tells Terraform to replace the placeholder with the variable’s actual value when executing the configuration.

3. What are variable definition files in Terraform, and when should I use them?

Variable definition files in Terraform are variables.tf or have the file extension .tfvars or .tfvars.json and are used to define the values of input variables. These files can be loaded automatically by Terraform if they are named terraform.tfvars or *.auto.tfvars, or they can be specified on the command line using the -var-file flag. They are particularly useful for setting different sets of values for different environments, like development, staging, and production

4. How do I set default values for Terraform variables?

Using the default keyword, default values for Terraform variables can be set within the variable definition. If an input variable is declared without a default value, Terraform will prompt the user for a value when applying the configuration, unless the value is provided through a .tfvars file or directly on the command line.

Conclusion

To wrap up, Terraform variables, in conjunction with tools like Terrateam, elevate cloud infrastructure management to new heights. By enabling dynamic, secure, and efficient resource configuration, they embody the essence of modern infrastructure as code (IaC) practices. From facilitating seamless authentication with environment variables to offering granular control through custom validation and sensitive data handling, Terraform variables ensure that your infrastructure is manageable and adaptable to the evolving needs of cloud-native development.

We use cookies and similar technologies to provide certain features, enhance the user experience and deliver content that is relevant to your interests. Depending on their purpose, analysis and marketing cookies may be used in addition to technically necessary cookies. By clicking on "Agree and continue", you declare your consent to the use of the aforementioned cookies. Here you can make detailed settings or revoke your consent (in part if necessary) with effect for the future. For further information, please refer to our Privacy Policy .