Managing variables in Terraform doesn’t have to be complicated. With the right practices, you can make your code cleaner, easier to understand, and more adaptable to different situations.
In this guide, we’ll go through 9 practical tips to help you handle Terraform variables.
variables.tf
File for Variable DefinitionsIn Terraform, the variables.tf
file is a
Here is an example of how to define variables in variables.tf
:
# variables.tf
variable "aws_region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "instance_type" {
description = "Instance type"
type = string
default = "t2.micro"
}
And reference variables in the main.tf
file:
# main.tf
provider "aws" {
region = var.aws_region
}
resource "aws_instance" "example" {
instance_type = var.instance_type
}
Documenting Terraform variables ensures clarity, maintainability, and collaboration. It provides context for variable usage, default values, and expected inputs, helping avoid errors.
To document variables:
variables.tf
.description
attribute in each variable block.terraform.tfvars
for Default ConfigurationsThe terraform.tfvars
file is used in Terraform to define default values for variables declared in your variables.tf
file. It automatically loads variable values, reducing redundancy and avoiding manual input. terraform.tfvars
simplifies switching configurations between environments (e.g., dev, staging, prod).
Define variable values in this file using the format:
variable_name = "value"
Terraform automatically loads this file when you run terraform plan
or terraform apply
.
You can override these defaults with environment variables (TF_VAR_variable_name
) or by specifying a different .tfvars
file using the -var-file
flag:
terraform plan -var-file="custom.tfvars"
Terraform locals allow you to define reusable, calculated values within a module. They make configurations cleaner, reduce repetition, and simplify complex expressions by assigning them meaningful names.
Locals are particularly useful for derived values, such as aggregations, formatting strings, or computations, ensuring consistency and easier updates.
First, declare derived values in the locals
block in your Terraform configuration:
locals {
env_prefix = "prod" # A reusable prefix for the environment
instance_ids = concat(var.base_ids, var.extra_ids) # Combines two lists into one
}
Instead of hardcoding "prod"
in a resource, you can reference local.env_prefix
:
resource "aws_instance" "example" {
tags = {
Name = "${local.env_prefix}-instance" # Outputs "prod-instance"
}
}
Organizing Terraform variables using maps and objects is an effective way to improve the structure and readability of your configurations. Grouping related parameters, maps, and objects simplifies the management of complex inputs and reduces redundancy.
Maps are particularly useful when dealing with dynamic key-value pairs, such as tagging resources or managing variable-length inputs.
variable "tags" {
type = map(string)
}
tags = {
environment = "dev"
team = "engineering"
}
Objects, on the other hand, are ideal for defining structured data with a fixed set of attributes.
variable "instance_config" {
type = object({
instance_type = string
ami_id = string
tags = map(string)
})
}
instance_config = {
instance_type = "t2.micro"
ami_id = "ami-123456"
tags = {
environment = "prod"
}
}
Providing default values in Terraform ensures modules and resources work smoothly in common scenarios without requiring additional input, improving usability and reducing configuration errors. This is done using the default
argument in variable definitions.
For example, a variable like this:
variable "instance_type" {
default = "t2.micro"
}
Assigns a default value oft2.micro
, which is used if no explicit value is provided during module invocation or plan execution.
Defaults should balance convenience and flexibility, reflecting sensible, widely applicable settings while allowing overrides for specific requirements.
Additionally, some arguments in Terraform resources are optional, meaning they do not need to be explicitly specified if they have a sensible default or if they are not required for every use case. For example, in the aws_instance
resource, the key_name
argument is optional, allowing you to skip defining it if you’re not using an SSH key pair.
Incorporating environment variables in Terraform helps manage sensitive data, customize configurations, and avoid hardcoding values in .tf
files. Prefix environment variables with TF_VAR_
, enabling Terraform to recognize and use them as input variables.
For example, define a variable in variables.tf
:
variable "region" {
description = "AWS region"
}
variable "secret_key" {
description = "Secret key"
sensitive = true
}
Set the environment variables in your shell:
export TF_VAR_region="us-west-2"
export TF_VAR_secret_key="my-secret-key"
Note: When setting environment variables, be cautious of exposing secrets in plaintext shell history. Use secure storage or automated secrets management tools.
Enforcing input validation with constraints in Terraform ensures that variables meet specific criteria, preventing configuration errors. Terraform allows defining constraints using the validation
block within a variable definition. This block uses the condition
argument to specify validation logic and error_message
to provide feedback when the condition is not met.
Constraints help maintain predictable and secure infrastructure deployments by catching invalid input during plan or apply stages.
For example:
variable "instance_type" {
description = "EC2 instance type"
type = string
validation {
condition = contains(["t2.micro", "t2.small", "t2.medium"], var.instance_type)
error_message = "Instance type must be one of t2.micro, t2.small, or t2.medium."
}
}
This helps improve readability and debugging by ensuring invalid inputs are caught during the plan or apply stage.
Managing secrets in Terraform requires careful handling to protect sensitive data like API keys, passwords, or access tokens. Instead of hardcoding secrets in configuration files, use secure storage solutions such as
Terraform allows marking variables as sensitive to ensure they are redacted in CLI output and logs. For example:
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
resource "aws_secretsmanager_secret" "db_password" {
name = "db_password"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string = var.db_password
}
To further secure secrets, store Terraform state files in encrypted remote backends like S3 with access controls, and exclude sensitive files from version control using.gitignore
.
Environment variables (e.g.,TF_VAR_db_password
) are also a secure alternative for injecting secrets dynamically. Combining these practices minimizes the risk of leaks and ensures robust secret management.
By following these tips, you’ll make your Terraform configurations much easier to manage and way less prone to mistakes. Whether you’re working on a small project or a massive infrastructure, good variable practices save you time and headaches.