Table of Contents
- Overview
- Declaring Variables
- Variable Attributes and Options
- Variable Types
- Supplying Variable Values
- Accessing Variable Values
- Best Practices
- Conclusion
Terraform Variables: Overview
What Are Terraform Variables?
Terraform variables are key components in HashiCorp Terraform, enabling users to make their infrastructure-as-code (IaC) scripts flexible, reusable, and easily configurable. By abstracting values—such as region names, credentials, machine types, or custom options—into named variables, you eliminate hardcoding and provide a consistent mechanism for customizing deployments across different environments.
Why You Need to Know About Terraform Variables
- Reusability: Variables allow you to write modules and infrastructure definitions that work equally well for dev, staging, or production, simply by passing in different values.
- Maintainability: By keeping configuration data separate from code logic, variables make your Terraform files easier to update and manage.
- Collaboration: Clear and descriptive variables standardize inputs for teams, lowering the learning curve and reducing the risk of misconfiguration.
- Automation: Variables are essential for integrating Terraform into continuous integration/continuous deployment (CI/CD) pipelines, enabling smooth and repeatable automation workflows.
- Security: Sensitive information, like passwords or tokens, can be managed more securely when handled as variables with restricted output.
How Terraform Variables Work
Terraform variables operate as named inputs to your configuration files. You declare variables, assign them default values or leave them required, and then supply values at runtime through various methods. The process includes:
- Declaration: In your Terraform code (usually in a file like
variables.tf
), you define a variable with optional attributes such as type, description, and default value. - Supplying Values: Variable values are provided by the user, through files (like
.tfvars
), command-line flags, or environment variables—whichever suits your workflow or automation needs. - Referencing in Configuration: Throughout your Terraform code, variables are accessed with the
var.<variable_name>
syntax, allowing resources, modules, and outputs to be dynamically controlled by these inputs. - Validation and Security: Variables can include validation rules and be marked as sensitive to enforce correct usage and protect secrets from accidental exposure.
In short, Terraform variables are the building blocks that empower you to create adaptable, robust, and secure infrastructure as code—making both individual projects and team collaboration significantly more efficient and reliable.
Declaring Variables
Terraform variables are typically declared in a dedicated file, such as variables.tf
, allowing you to reuse and simplify your configuration. These declarations provide the structure, type, and default behavior for input data.
Follow these steps to declare variables properly in Terraform:
-
Create a variable block:
Use thevariable
keyword followed by the variable name in quotes.variable "region" { }
-
Add optional attributes:
Most variable blocks include a description, a type, and optionally a default value.variable "region" { description = "AWS region where resources will be created" type = string default = "us-west-2" }
-
Use complex types as needed:
You can define variables as lists, maps, or objects for more advanced input structures.variable "tags" { type = map(string) default = { environment = "dev" owner = "network_team" } }
-
Apply validation (optional):
Restrict input values usingvalidation
blocks to prevent misconfiguration.variable "admin_email" { description = "Admin email for account notifications" type = string validation { condition = can(regex("^.+@example\\.com$", var.admin_email)) error_message = "Email must use @example.com domain." } }
-
Declare sensitive variables:
To prevent sensitive data from appearing in logs or CLI output, setsensitive = true
.variable "admin_password" { description = "Admin user password" type = string sensitive = true }
Once variables are declared, you can reference them in your configuration using var.<variable_name>
.
Variable Attributes and Options
Terraform variables support several attributes that define their behavior, enforce input constraints, and improve the readability of your configuration. Understanding these options helps ensure your infrastructure code is robust, flexible, and well-documented.
Follow these steps to configure variable attributes properly:
-
Type:
Defines the data type of the variable. This improves validation and prevents assigning unsupported values.variable "instance_count" { type = number }
Supported types include
string
,number
,bool
,list(type)
,map(type)
,object({...})
, andany
. -
Default:
Provides a fallback value if none is supplied. If no default is provided, the variable becomes required.variable "region" { default = "us-east-1" }
-
Description:
Adds context to the variable, which is especially helpful for team-based collaboration and documentation.variable "environment" { description = "Specifies the deployment environment (e.g. dev, staging, prod)" }
-
Validation:
Adds logic to restrict input based on conditions. Useful for enforcing formats or required prefixes.variable "project_prefix" { type = string validation { condition = can(regex("^net-", var.project_prefix)) error_message = "The prefix must start with 'net-'." } }
-
Sensitive:
Marks the variable to prevent its value from being displayed in logs or Terraform output.variable "db_password" { type = string sensitive = true description = "The database admin password" }
-
Nullable:
Controls whether the variable allowsnull
as a value. Defaults totrue
. Setfalse
to require a non-null assignment.variable "critical_tag" { type = string nullable = false }
These attributes can be combined to create a rich definition that ensures your variables are used consistently and safely across multiple modules.
Variable Types
Terraform variables can be declared using a wide range of types to support simple to complex inputs. Specifying a type improves validation, reduces errors, and allows better structure in your configuration.
Below is a step-by-step explanation of each supported type with examples:
-
String:
A sequence of characters.variable "region" { type = string default = "us-west-2" }
-
Number:
An integer or float value. Useful for counts or thresholds.variable "instance_count" { type = number default = 2 }
-
Boolean:
A true or false value. Useful for feature toggles and conditional logic.variable "enable_logging" { type = bool default = true }
-
List:
An ordered collection of values. Each element must match the declared inner type.variable "availability_zones" { type = list(string) default = ["us-east-1a", "us-east-1b"] }
-
Map:
A key-value pairing of elements, where keys are strings and values have a consistent type.variable "tags" { type = map(string) default = { environment = "staging" team = "network" } }
-
Object:
A complex structure with named attributes and types for each field.variable "app_config" { type = object({ name = string version = string enabled = bool }) default = { name = "nginx" version = "1.25" enabled = true } }
-
Tuple:
An ordered list of elements with different types in a fixed length.variable "coordinates" { type = tuple([number, number]) default = [29.42, -98.49] }
-
Any:
Allows any valid Terraform type. Use this for flexibility, but avoid it when possible for better control.variable "raw_input" { type = any }
Choosing the right variable type is essential for modular, secure, and scalable infrastructure code. Use complex types like object
and map
to group related inputs together, and simple types like string
and number
for individual values.
Supplying Variable Values
After declaring input variables in Terraform, you need to supply values when running Terraform commands. Terraform offers multiple flexible methods to assign values to variables, in both CLI and file-based formats.
Follow these common approaches to supply variable values:
-
terraform.tfvars
or.auto.tfvars
files:
These files are automatically loaded by Terraform and are ideal for setting default values on a per-environment basis.# terraform.tfvars region = "us-east-2" instance_count = 3 environment = "dev"
File names ending in
.auto.tfvars
are also automatically processed. For example:dev.auto.tfvars
. -
Command-line
-var
flag:
You can pass individual variable assignments directly when executing Terraform commands.terraform apply -var="region=us-east-1" -var="instance_count=2"
This is useful for quick testing or overrides in CICD pipelines.
-
Variable definition files (
-var-file
):
Pass a .tfvars or custom file explicitly using the-var-file
flag.terraform plan -var-file="prod.tfvars"
This is often used to support multiple environments such as
dev.tfvars
,stage.tfvars
, andprod.tfvars
. -
Environment variables (prefix with
TF_VAR_
):
You can export a variable as an environment variable, which Terraform will automatically detect.export TF_VAR_region="us-west-1" terraform plan
This method keeps variable values out of visible command history and is often used in automation or scripting.
-
Direct assignment in module blocks:
When using local or external modules, you can supply input variables inline during module use.module "web_server_cluster" { source = "./modules/compute" region = var.region instance_count = var.instance_count }
This promotes reuse and makes module calls self-contained and explicit.
Choose the method that best fits your workflow, whether you're using local development, CI/CD systems, or managing multiple environments. Keep in mind, explicit definitions always override defaults and environment values.
Accessing Variable Values
After variables are declared and values are supplied, you access them in your Terraform configuration using the var
namespace. This makes it possible to dynamically configure resources and modules based on external input.
Follow these steps to access and use variable values in different contexts:
-
Basic reference syntax:
Usevar.<variable_name>
to retrieve any declared variable’s value.provider "aws" { region = var.region }
This allows provider selection, resource settings, and dynamic input handling all from variable inputs.
-
Using variables in resource arguments:
You can dynamically configure resources, such as instance types or tags, using variables.resource "aws_instance" "web" { ami = var.ami_id instance_type = var.instance_type count = var.instance_count tags = { Name = var.name_tag Environment = var.environment } }
-
Referencing maps:
Use key lookups to access specific values inside amap
variable.output "team_owner" { value = var.tags["owner"] }
This is helpful when organizing metadata such as environment or contact details.
-
Accessing lists:
Use index notation to retrieve values from a list variable.output "first_subnet" { value = var.subnet_ids[0] }
Iterating or dynamically assigning resources using lists lends flexibility to your deployments.
-
Working with object variables:
Access individual fields using dot notation.output "app_version" { value = var.app_config.version }
This is ideal for passing structured inputs into modules and templates.
Using variables dynamically ensures your code is modular, clean, and easy to scale across multiple environments or use cases. Any value declared or passed into Terraform becomes instantly available via the var
namespace within your HCL configurations.
Best Practices
Adopting best practices when working with Terraform variables will make your infrastructure code more reliable, secure, and easy to understand. Below are actionable steps to maximize the benefits of variable usage in your projects:
-
Always provide clear variable descriptions:
Use thedescription
attribute for every variable so that collaborators and tools can easily understand its purpose.variable "environment" { description = "Environment name: dev, staging, or prod" type = string }
-
Enforce strong typing:
Specifytype
for each variable to prevent input errors and document expected values.variable "subnet_ids" { type = list(string) description = "List of subnet IDs for network placement" }
-
Set default values thoughtfully:
Provide defaults for common cases, but avoid defaults for sensitive or unique values to encourage explicit assignment.variable "instance_type" { description = "EC2 instance type" type = string default = "t3.micro" }
Do not set defaults for credentials or environment-specific settings.
-
Use validation for critical constraints:
Addvalidation
blocks to restrict allowed values and avoid configuration errors.variable "region" { description = "AWS region" type = string validation { condition = contains(["us-east-1", "us-west-2"], var.region) error_message = "Region must be us-east-1 or us-west-2" } }
-
Mark sensitive variables appropriately:
Prevent accidental exposure of secrets and credentials by settingsensitive = true
.variable "api_token" { description = "API access token" type = string sensitive = true }
-
Group related variables logically:
Use objects or consistent naming to group related configuration, which makes modules easier to reuse and maintain.variable "db_config" { description = "Database connection configuration" type = object({ host = string port = number user = string }) }
-
Keep variable files organized:
Declare all input variables in a dedicatedvariables.tf
file, and supply values in appropriately named*.tfvars
files for each environment.
Following these best practices ensures your Terraform codebase is clean, maintainable, and secure—making it easier for teams to collaborate and automate infrastructure safely.
Conclusion
Throughout this blog post, we’ve explored the key building blocks of how to effectively use Terraform Variables to build clean, dynamic, and reusable infrastructure code. Here's a quick recap of what you’ve learned:
🔹 Declaring Variables: You define variables using the variable
block, with optional attributes like type
, default
, and description
to configure behavior and improve readability.
🔹 Variable Attributes and Options: Terraform variables can include type enforcement, validation rules, and sensitive flagging to ensure safe and predictable deployments.
🔹 Variable Types: You can use a wide range of types — from simple strings and numbers to complex maps, lists, objects, and even tuples — depending on how structured your input data needs to be.
🔹 Supplying Variable Values: You learned several flexible ways to provide values, including .tfvars
files, -var
command-line flags, environment variables, and variable files per environment.
🔹 Accessing Variable Values: Once defined and passed in, variables are referenced through the var.<name>
syntax and can be used efficiently throughout your Terraform configuration.
🔹 Best Practices: To maintain clean and secure code, always document your variables, use type constraints, validate inputs, and group related data logically.
Terraform variables are the foundation of writing scalable, modular, and environment-agnostic infrastructure code. By structuring your approach to declarations, inputs, and access, your Terraform projects will be far more maintainable and collaborative.
Thanks for following along! I hope this guide helps bring more order to your modules and more clarity to your projects. If you liked this post, feel free to share it with fellow engineers or drop a comment on what you'd like to see covered next. Happy automating! 🌍💻🚀