How to use Variables to customize Terraform configuration

Introduction Unlike variables in traditional programming languages, Terraform input variables remain constant throughout a Terraform run—whether it's during plan, apply, or destroy. Instead of changing dynamically, they let users safely tailor infrastructure by supplying different values ahead of execution, eliminating the need to manually modify configuration files. Requirements Latest Terraform installed locally An AWS Account A HCP Terraform account with HCP Terraform locally authenticated. A HCP Terraform variable set configured with your AWS credentials. VS Code. Create infrastructure Clone the Learn Terraform variables GitHub repository for this tutorial by running the following command: git clone https://github.com/hashicorp-education/learn-terraform-variables Change to the repository directory. cd learn-terraform-variables The configuration in main.tf sets up a web application by provisioning a VPC, a load balancer, and a set of EC2 instances. Open your terraform.tf file and uncomment the cloud block. Then, replace the placeholder organization name with your own HCP Terraform organization name. Run terraform init to initialize your configuration. Run terraform apply to apply the configuration. Introduce variables to customize your setup To use an input variable for an argument, start by defining the variable, then update your configuration to reference it instead of a hardcoded value. You will start using variable for your aws region by adding a block declaring a variable named aws_region to variables.tf variable "aws_region" { description = "AWS region" type = string default = "us-west-2" } NOTE: After defining your variable, you can reference it in the main configuration by using var.[variable_name] i.e. var.aws_regions for your aws region variables. Edit the provider block in main.tf to use the new aws_region variable. Add another declaration for the vpc_cidr_block variable to variables.tf. variable "vpc_cidr_block" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16" } Now, replace the hard-coded value for the VPC's CIDR block with a variable in main.tf. Apply the updated configuration. Because the variable defaults match the original hardcoded values, Terraform will detect no changes and apply nothing new. Set the number of instances In addition to strings, Terraform supports several other variable types. To specify the number of instances this configuration should support, use the number type by adding the following to your variables.tf file. variable "instance_count" { description = "Number of instances to provision." type = number default = 2 } Update EC2 instances to use the instance_count variable in main.tf. Enable or disable VPN gateway support Besides strings and numbers, Terraform also supports other variable types, including bool for representing true or false values. You can use a bool variable to control whether a VPN gateway should be configured for your VPC. To do this, add the following to your variables.tf file. variable "enable_vpn_gateway" { description = "Enable a VPN gateway in your VPC." type = bool default = false } Use this new variable in your VPC configuration by editing main.tf as follows. List public and private subnets So far, the variables you've used have been single values, which Terraform refers to as simple types. In addition to these, Terraform also supports collection variable types, which can hold multiple values. There are several types of collections: List: A sequence of values, all of the same type. Map: A key-value lookup table, where both keys and values are of the same type. Set: An unordered collection of unique values, all of the same type. A common scenario for using list variables is when configuring the private_subnets and public_subnets arguments for the VPC. You can make this configuration more flexible and customizable by utilizing lists and the slice() function. variable "public_subnet_count" { description = "Number of public subnets." type = number default = 2 } variable "private_subnet_count" { description = "Number of private subnets." type = number default = 2 } variable "public_subnet_cidr_blocks" { description = "Available cidr blocks for public subnets." type = list(string) default = [ "10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", ] } variable "private_subnet_cidr_blocks" { description = "Available cidr blocks for private subnets." type = list(string) default = [ "10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24", "10.0.104.0/24", "10.0.105.0/24", "10.0.106.0/24", "10.

Apr 25, 2025 - 19:54
 0
How to use Variables to customize Terraform configuration

Introduction

Unlike variables in traditional programming languages, Terraform input variables remain constant throughout a Terraform run—whether it's during plan, apply, or destroy. Instead of changing dynamically, they let users safely tailor infrastructure by supplying different values ahead of execution, eliminating the need to manually modify configuration files.

Requirements

Create infrastructure

  • Clone the Learn Terraform variables GitHub repository for this tutorial by running the following command:

git clone https://github.com/hashicorp-education/learn-terraform-variables

  • Change to the repository directory.

cd learn-terraform-variables

The configuration in main.tf sets up a web application by provisioning a VPC, a load balancer, and a set of EC2 instances.

  • Open your terraform.tf file and uncomment the cloud block. Then, replace the placeholder organization name with your own HCP Terraform organization name. Image description
  • Run terraform init to initialize your configuration. Image description
  • Run terraform apply to apply the configuration. Image description

Introduce variables to customize your setup

To use an input variable for an argument, start by defining the variable, then update your configuration to reference it instead of a hardcoded value.

  • You will start using variable for your aws region by adding a block declaring a variable named aws_region to variables.tf
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-west-2"
}

Image description
NOTE: After defining your variable, you can reference it in the main configuration by using var.[variable_name] i.e. var.aws_regions for your aws region variables.

  • Edit the provider block in main.tf to use the new aws_region variable. Image description
  • Add another declaration for the vpc_cidr_block variable to variables.tf.
variable "vpc_cidr_block" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

Image description

  • Now, replace the hard-coded value for the VPC's CIDR block with a variable in main.tf. Image description
  • Apply the updated configuration. Because the variable defaults match the original hardcoded values, Terraform will detect no changes and apply nothing new. Image description

Set the number of instances

In addition to strings, Terraform supports several other variable types.
To specify the number of instances this configuration should support, use the number type by adding the following to your variables.tf file.

variable "instance_count" {
  description = "Number of instances to provision."
  type        = number
  default     = 2
}

Image description

  • Update EC2 instances to use the instance_count variable in main.tf. Image description

Enable or disable VPN gateway support

Besides strings and numbers, Terraform also supports other variable types, including bool for representing true or false values.

You can use a bool variable to control whether a VPN gateway should be configured for your VPC. To do this, add the following to your variables.tf file.

variable "enable_vpn_gateway" {
  description = "Enable a VPN gateway in your VPC."
  type        = bool
  default     = false
}

Image description

  • Use this new variable in your VPC configuration by editing main.tf as follows. Image description

List public and private subnets

So far, the variables you've used have been single values, which Terraform refers to as simple types. In addition to these, Terraform also supports collection variable types, which can hold multiple values. There are several types of collections:

List: A sequence of values, all of the same type.

Map: A key-value lookup table, where both keys and values are of the same type.

Set: An unordered collection of unique values, all of the same type.

A common scenario for using list variables is when configuring the private_subnets and public_subnets arguments for the VPC. You can make this configuration more flexible and customizable by utilizing lists and the slice() function.

variable "public_subnet_count" {
  description = "Number of public subnets."
  type        = number
  default     = 2
}

variable "private_subnet_count" {
  description = "Number of private subnets."
  type        = number
  default     = 2
}

variable "public_subnet_cidr_blocks" {
  description = "Available cidr blocks for public subnets."
  type        = list(string)
  default     = [
    "10.0.1.0/24",
    "10.0.2.0/24",
    "10.0.3.0/24",
    "10.0.4.0/24",
    "10.0.5.0/24",
    "10.0.6.0/24",
    "10.0.7.0/24",
    "10.0.8.0/24",
  ]
}

variable "private_subnet_cidr_blocks" {
  description = "Available cidr blocks for private subnets."
  type        = list(string)
  default     = [
    "10.0.101.0/24",
    "10.0.102.0/24",
    "10.0.103.0/24",
    "10.0.104.0/24",
    "10.0.105.0/24",
    "10.0.106.0/24",
    "10.0.107.0/24",
    "10.0.108.0/24",
  ]
}

You can read more about slice() function Here

  • Next, update the VPC module configuration in main.tf to use the slice() function to extract specific subsets of the CIDR block lists for your public and private subnets. Image description

Map resource tags

Each resource and module defined in main.tf includes two tags: project_name and environment. To manage these tags efficiently, define them using a variable of type map.

  • Declare a new map variable for resource tags in variables.tf.
variable "resource_tags" {
  description = "Tags to set for all resources"
  type        = map(string)
  default     = {
    project     = "project-alpha",
    environment = "dev"
  }
}

Image description
By setting the type to map(string), you are telling Terraform to expect a map where all values are strings. Map keys in Terraform are always strings by default. Similar to dictionaries or maps in other programming languages, you can access a specific value by referencing its associated key.

  • Now, replace the hard coded tags in main.tf with references to the new variable. Image description Be sure to replace all five references to these hard-coded tags in your configuration.
  • Run terraform apply to apply the configuration. Image description

Assign values to variables

Terraform requires that every variable be assigned a value. There are several ways to provide these values, including use command line flag, assign values with a file.

Use command line flag

Up to this point, all variable definitions have included default values. Now, add a new variable to variables.tf without a default value. This will require the value to be provided explicitly when running Terraform.

variable "ec2_instance_type" {
  description = "AWS EC2 instance type."
  type        = string
}

Image description

  • Replace the reference to the EC2 instance type in main.tf. Image description
  • Now apply the configuration using the -var command-line flag to provide the variable value. Since the value you're supplying matches the existing one, Terraform will detect no changes and nothing will be applied.

terraform apply -var ec2_instance_type=t2.micro

Image description

Assign values with a file

Manually entering variable values can be time-consuming and prone to errors. A better approach is to store these values in a file, allowing for easier reuse and improved consistency.

  • Create a file named terraform.auto.tfvars with the following contents.
resource_tags = {
  project     = "project-alpha",
  environment = "dev",
  owner       = "me@example.com"
}

ec2_instance_type = "t3.micro"

instance_count = 3

Image description
Terraform automatically loads any file in the current directory named terraform.tfvars or ending with .auto.tfvars. If your variable values are stored in a file with a different name, you can explicitly load it using the -var-file flag.

  • Run terraform apply to apply the configuration. Image description

Interpolate variables in strings

Terraform configurations support string interpolation, which lets you insert the result of expressions directly into strings. This makes it possible to dynamically build strings using variables, local values, and function outputs.

Now, update the names of the security groups to include the project and environment values from the resource_tags map variable using string interpolation.

name        = "web-sg-${var.resource_tags["project"]}-${var.resource_tags["environment"]}"

name        = "lb-sg-${var.resource_tags["project"]}-${var.resource_tags["environment"]}"

name = "lb-${random_string.lb_id.result}-${var.resource_tags["project"]}-${var.resource_tags["environment"]}"

Image description
Image description

  • Run terraform apply Image description

Validate variables

This configuration has a potential issue: AWS load balancers have naming restrictions. Their names must not exceed 32 characters and can only include certain characters.

To help prevent invalid tag values from causing problems, use variable validation to enforce constraints on the project and environment tags.

Replace your existing resource_tags variable in variables.tf with the following snippet. It includes validation blocks that restrict both the character set and length for the project and environment values:

variable "resource_tags" {
  description = "Tags to set for all resources"
  type        = map(string)
  default     = {
    project     = "my-project",
    environment = "dev"
  }

  validation {
    condition     = length(var.resource_tags["project"]) <= 16 && length(regexall("[^a-zA-Z0-9-]", var.resource_tags["project"])) == 0
    error_message = "The project tag must be no more than 16 characters, and only contain letters, numbers, and hyphens."
  }

  validation {
    condition     = length(var.resource_tags["environment"]) <= 8 && length(regexall("[^a-zA-Z0-9-]", var.resource_tags["environment"])) == 0
    error_message = "The environment tag must be no more than 8 characters, and only contain letters, numbers, and hyphens."
  }
}

Image description

The regexall() function takes two arguments: a regular expression and a string to test. It returns a list of all matches found in the string. In this context, you can use a regular expression that matches any character other than a letter, number, or hyphen to detect invalid input.

  • Run terraform apply to apply the configuration. Image description
  • Now test the validation rules by specifying an environment tag that is too long. Notice that the command will fail and return the error message specified in the validation block.

terraform apply -var='resource_tags={project="my-project",environment="development"}'

Image description

Thanks for staying till the end