Terraform Variable Defaults
Introduction
When working with Terraform, variables are essential for creating flexible and reusable infrastructure code. One of the most powerful features of Terraform variables is the ability to assign default values. Variable defaults allow you to specify fallback values that will be used when no explicit value is provided, making your Terraform configurations more robust and user-friendly.
In this guide, we'll explore how to set default values for Terraform variables, when to use them, and best practices for implementing them effectively in your infrastructure as code.
What Are Variable Defaults?
Default values in Terraform are predefined values assigned to variables that will be used when no other value is explicitly provided. They serve as fallbacks, ensuring your configuration can run even when input values aren't specified.
Here's the basic syntax for declaring a variable with a default value:
variable "instance_type" {
description = "The type of EC2 instance to launch"
type = string
default = "t2.micro"
}
In this example, if no value is provided for instance_type
when applying the Terraform configuration, it will automatically use t2.micro
as the default value.
Why Use Default Values?
There are several compelling reasons to use default values in your Terraform configurations:
- Convenience: Users of your module don't need to specify every variable if the defaults are acceptable.
- Safety: Prevents errors that might occur when required values are missing.
- Documentation: Default values serve as examples of valid values for the variable.
- Sensible baselines: Sets reasonable starting points that work for common scenarios.
- Module reusability: Makes your modules more portable across different environments.
Types of Default Values
Terraform supports default values for all variable types. Let's look at examples for each:
String Defaults
variable "region" {
description = "AWS region to deploy resources"
type = string
default = "us-west-2"
}
Number Defaults
variable "instance_count" {
description = "Number of instances to create"
type = number
default = 1
}
Boolean Defaults
variable "enable_monitoring" {
description = "Whether to enable detailed monitoring"
type = bool
default = false
}
List Defaults
variable "availability_zones" {
description = "List of availability zones to use"
type = list(string)
default = ["us-west-2a", "us-west-2b"]
}
Map Defaults
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {
Environment = "development"
ManagedBy = "terraform"
}
}
Complex Type Defaults
variable "server_config" {
description = "Server configuration options"
type = object({
instance_type = string
disk_size_gb = number
is_public = bool
})
default = {
instance_type = "t2.micro"
disk_size_gb = 20
is_public = false
}
}
How Variables with Defaults Are Used
Let's see how variables with defaults are used in a Terraform configuration:
# Define a variable with default
variable "environment" {
type = string
default = "development"
}
# Use the variable
resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = var.environment == "production" ? "t2.medium" : "t2.micro"
tags = {
Name = "web-server-${var.environment}"
}
}
In this example, if no value is provided for environment
, it will use the default value "development", resulting in a t2.micro instance with the tag "web-server-development".
Overriding Default Values
There are several ways to override default values in Terraform:
Command Line Flags
terraform apply -var="environment=production"
Variable Files
Create a file named production.tfvars
:
environment = "production"
instance_count = 3
Then apply with:
terraform apply -var-file="production.tfvars"
Environment Variables
export TF_VAR_environment=production
terraform apply
Auto-loaded Variable Files
Terraform automatically loads variables from files named:
terraform.tfvars
terraform.tfvars.json
- Any files with names ending in
.auto.tfvars
or.auto.tfvars.json
Practical Example: Web Application Infrastructure
Let's look at a more comprehensive example that demonstrates how default values make your infrastructure code more reusable.
# Variables with defaults
variable "project_name" {
type = string
default = "web-app"
}
variable "environment" {
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
}
variable "instance_type" {
type = string
default = "t2.micro"
}
variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}
variable "public_subnet_cidrs" {
type = list(string)
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "enable_monitoring" {
type = bool
default = false
}
# Resources using those variables
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
tags = {
Name = "${var.project_name}-vpc-${var.environment}"
Environment = var.environment
}
}
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-public-subnet-${count.index}-${var.environment}"
Environment = var.environment
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = aws_subnet.public[0].id
monitoring = var.enable_monitoring
tags = {
Name = "${var.project_name}-web-${var.environment}"
Environment = var.environment
}
}
This configuration can be deployed as-is using the default values, or you can customize it for different environments by overriding the defaults.
Default Values with Expressions
Starting with Terraform 0.15, you can use expressions in default values, which adds even more flexibility:
variable "region" {
type = string
default = "us-west-2"
}
variable "azs" {
type = list(string)
default = ["${var.region}a", "${var.region}b", "${var.region}c"]
}
However, there are limitations to what expressions can be used in defaults. You can't reference resources or data sources, only other variable values and built-in functions.
Best Practices for Variable Defaults
-
Provide meaningful defaults: Choose defaults that are generally useful for most scenarios.
-
Document your variables: Always include a description that explains what the variable is for and what the default value represents.
hclvariable "backup_retention_days" {
description = "Number of days to retain backups (7 for development, 30+ recommended for production)"
type = number
default = 7
} -
Use validation rules: Add validation to ensure that provided values meet your requirements.
hclvariable "environment" {
type = string
default = "development"
validation {
condition = contains(["development", "staging", "production"], var.environment)
error_message = "Environment must be one of: development, staging, production."
}
} -
Don't default sensitive values: Credentials and other sensitive information should never have defaults.
hclvariable "database_password" {
description = "Password for the database (must be provided, no default for security reasons)"
type = string
sensitive = true
# No default!
} -
Consider environment-specific defaults: Use different variable files for different environments rather than trying to accommodate all scenarios with a single default.
When Not to Use Defaults
While defaults are powerful, they aren't always appropriate:
- Security credentials: Never set defaults for passwords, API keys, or other sensitive data.
- Environment-specific required values: Some values might not have a sensible default across all environments.
- Values that must be consciously chosen: Sometimes it's better to force the user to make an explicit choice.
Flow of Variable Resolution
Understanding how Terraform resolves variable values is important. Here's the priority order (highest to lowest):
- Command-line flags (
-var
and-var-file
) .tfvars
files specified on command lineterraform.tfvars
, if present.auto.tfvars
files (alphabetical order)- Environment variables (
TF_VAR_name
) - Default values in variable declarations
This flow is visualized in the following diagram:
Summary
Variable defaults in Terraform provide a powerful way to create flexible, reusable infrastructure code. By setting sensible defaults, you can ensure your modules work out-of-the-box while still allowing for customization when needed.
Key takeaways:
- Default values act as fallbacks when no explicit value is provided
- They can be overridden through various methods (command line, variable files, environment variables)
- Defaults improve module reusability and provide documentation through example
- Best practices include providing meaningful defaults, adding proper documentation, and using validation rules
- Avoid defaulting sensitive information like credentials
With thoughtful use of default values, your Terraform configurations will be more robust, user-friendly, and maintainable.
Exercises
- Create a Terraform configuration with variables for deploying a web server, including defaults for environment, instance type, and region.
- Write a module that uses complex type variables with defaults for configuring a database deployment.
- Create multiple
.tfvars
files for different environments (dev, staging, prod) that override the defaults in your configuration. - Practice using variable validation to enforce constraints on the values that can be provided.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)