paint-brush
Tips For Managing Terraform Variablesby@mariusz_michalowski

Tips For Managing Terraform Variables

by Mariusz MichalowskiDecember 11th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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. In this guide, we’ll go through 9 practical tips to help you handle Terraform variables.
featured image - Tips For Managing Terraform Variables
 Mariusz Michalowski HackerNoon profile picture

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.

1. Use variables.tf File for Variable Definitions

In Terraform, the variables.tf file is a standard convention for defining input variables. By centralizing variable declarations—such as their types, descriptions, and default values—it simplifies how you pass dynamic data into your infrastructure code.


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
}

2. Document Your Variables

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:

  1. Use comments: Add comments above variables in variables.tf.
  2. Use descriptions: Include the description attribute in each variable block.
  3. README: Maintain a README with variable details.
  4. Auto-Docs: Use tools like Terraform-docs to automatically generate documentation.
  5. Organize related variables logically by grouping them and clearly documenting any dependencies.

3. Use terraform.tfvars for Default Configurations

The 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"

4. Use locals for Derived Values

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"
  }
}

5. Organize Variables With Maps and Objects

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"
  }
}

6. Provide Default Values for Common Scenarios

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.


Optional arguments provide additional flexibility, ensuring resources remain simple while still supporting advanced configurations.

7. Incorporate Environment Variables

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.

8. Enforce Input Validation With Constraints

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.

9. Handle Secrets With Sensitive Variables

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 HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. Reference these secrets dynamically using providers or data sources to avoid exposing them in plaintext.


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.

Wrapping Up

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.