Terraform Variable Types
I want to walk you through Terraform variable types. If you’ve worked with other programming languages, you’ll find Terraform’s approach familiar. Variables hold your data, and you need to know what types you can use.
Terraform gives you solid flexibility here. You can build configurations that adapt to different environments without rewriting code.
The Variable Block
You declare variables with the variable block. Here’s the simplest form:
variable "db_host" {}
No required attributes. No need to specify a type or default value either—Terraform can infer the type from context.
If you skip the default, you must provide a value when you run Terraform:
terraform apply -var db_host="db.example.com"
String
Strings are straightforward. Define a variable and set the type:
variable "web_service_protocol" {
type = string
default = "https://"
}
Double quotes around the value. The type = string annotation is optional since Terraform infers it, but I always include it for clarity.
Multiline Strings
If you need a large block of text with newlines, use heredoc syntax:
variable "policy" {
type = string
default = <<POLICY
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
}
POLICY
}
The <<POLICY marker tells Terraform to capture everything until it sees POLICY on its own line.
Number
For numeric values, use number:
variable "db_port" {
type = number
default = 5432
}
Boolean
Boolean variables hold true or false. Terraform infers the type if you don’t specify:
variable "enable_debug" {
default = false
}
Override the value at runtime:
terraform apply -var enable_debug=true
You might see people use 0 and "1" as boolean substitutes—that still works, but I prefer being explicit with true and false.
Map
Maps let you associate keys with values. A common use case is storing AMI IDs by region:
variable "images_map" {
type = map(string)
default = {
"ap-east-1" = "ami-0b59bfac6be064b78"
"us-west-1" = "ami-e7768380"
}
}
Access a value with var.images_map["us-west-1"]. Much cleaner than maintaining separate variables for each region.
List
Lists hold ordered sequences. Each element has an index, starting at zero:
variable "aws_zones" {
type = list(string)
default = ["us-east-1a", "us-west-1c"]
}
In this example, var.aws_zones[0] gives you "us-east-1a", and var.aws_zones[1] gives you "us-west-1c".
Object
Objects let you define structured data. Suppose you need to store database connection details:
variable "db_config" {
type = object({
external = number
internal = number
protocol = string
})
default = {
external = 5432
internal = 5433
protocol = "tcp"
}
}
List of Objects
You can nest these. Here’s a list of database configurations:
variable "db_ports" {
type = list(object({
external = number
internal = number
protocol = string
}))
default = [
{
external = 5432
internal = 5433
protocol = "tcp"
}
]
}
Complex Nesting
You can build more sophisticated structures. Here’s a block device mapping that contains an EBS object:
variable "block_device_mappings" {
description = "Volumes to attach to the instance"
type = list(object({
device_name = string
no_device = bool
virtual_name = string
ebs = object({
delete_on_termination = bool
encrypted = bool
iops = number
kms_key_id = string
snapshot_id = string
volume_size = number
volume_type = string
})
}))
default = []
}
The ebs attribute is itself an object. Terraform lets you compose these types to match almost any infrastructure configuration.
Wrapping Up
Terraform gives you these core types to work with: string, number, bool, map, list, and object. You can combine them—lists of objects, maps of lists, objects containing objects—to match your infrastructure’s shape.
The goal is reuse. Define a value once in a variable, then reference it everywhere you need it. When requirements change, you update the variable definition and Terraform handles the rest.
Comments