Skip to content

2.05 Terraform Variable Types

Overview

Terraform variables support a range of types — from simple primitives like string, number, and bool, to complex structures like list, map, set, object, and tuple. Using the correct type constraint catches misconfiguration early and makes modules self-documenting.

Abstract

The type argument in a variable block is optional but strongly recommended. When omitted, Terraform defaults to any, accepting values of any type. Explicit type constraints enforce correctness at plan time, before any infrastructure is touched.


Why It Matters in Production

  • Type constraints catch invalid variable values before apply, preventing mid-deployment failures
  • Complex types (object, map) allow entire environment configurations to be passed as a single variable, reducing the number of individual declarations
  • list and map types enable dynamic resource creation using for_each and count
  • Mismatched types produce clear, actionable errors rather than silent misconfiguration

Variable Type Reference

Type Example Value Notes
string "/root/pets.txt" Alphanumeric single value
number 1, -5, 3.14 Positive or negative
bool true / false Boolean flag
any Any value Default when type is omitted
list ["cat", "dog"] Ordered, indexed from 0, allows duplicates
map pet1 = cat Key-value pairs
set ["cat", "dog"] Like a list but no duplicate elements allowed
object Complex data structure Mix of named attributes with different types
tuple Complex data structure Ordered sequence with fixed mixed types

Variable Block — Full Syntax

variable "<name>" {
  description = "What this variable is for"
  type        = <type>
  default     = <value>
  sensitive   = false
}

Basic type examples

variable "filename" {
  default     = "/root/pets.txt"
  type        = string
  description = "the path of local file"
}

variable "length" {
  default     = 2
  type        = number
  description = "length of the pet name"
}

variable "password_change" {
  default = true
  type    = bool
}

List

A list is an ordered, indexed collection of values. Elements are referenced by their zero-based index using var.<name>[index].

# variables.tf
variable "prefix" {
  default = ["Mr", "Mrs", "Sir"]
  type    = list
}
# main.tf
resource "random_pet" "my-pet" {
  prefix = var.prefix[0]   # "Mr"
}

Index reference:

Index Value
0 Mr
1 Mrs
2 Sir

List with type constraint

# List of strings (valid)
variable "prefix" {
  default = ["Mr", "Mrs", "Sir"]
  type    = list(string)
}

# List of numbers (valid)
variable "prefix" {
  default = [1, 2, 3]
  type    = list(number)
}

Type mismatch error

Declaring type = list(number) but providing string defaults causes a plan-time error:

Error: Invalid default value for variable
This default value is not compatible with the variable's type constraint:
a number is required.


Map

A map stores data as key-value pairs. Values are accessed by key using var.<name>["key"].

# variables.tf
variable "file-content" {
  type    = map
  default = {
    "statement1" = "We love pets!"
    "statement2" = "We love animals!"
  }
}
# main.tf
resource "local_file" "my-pet" {
  filename = "/root/pets.txt"
  content  = var.file-content["statement2"]   # "We love animals!"
}

Key-value reference:

Key Value
statement1 We love pets!
statement2 We love animals!

Map with type constraint

# Map of strings
variable "cats" {
  default = {
    "color" = "brown"
    "name"  = "bella"
  }
  type = map(string)
}

# Map of numbers
variable "pet_count" {
  default = {
    "dogs"     = 3
    "cats"     = 1
    "goldfish" = 2
  }
  type = map(number)
}

Set

A set is like a list but does not allow duplicate elements. Attempting to use duplicate values causes a plan-time error.

# Valid — no duplicates
variable "prefix" {
  default = ["Mr", "Mrs", "Sir"]
  type    = set(string)
}

variable "fruit" {
  default = ["apple", "banana"]
  type    = set(string)
}

variable "age" {
  default = [10, 12, 15]
  type    = set(number)
}

Duplicate element error

# Invalid — "Sir" appears twice
variable "prefix" {
  default = ["Mr", "Mrs", "Sir", "Sir"]
  type    = set(string)
}

# Invalid — "banana" appears twice
variable "fruit" {
  default = ["apple", "banana", "banana"]
  type    = set(string)
}

# Invalid — 10 appears twice
variable "age" {
  default = [10, 12, 15, 10]
  type    = set(number)
}

List vs Set:

Feature list set
Ordered ✅ Yes ❌ Not guaranteed
Index access var.name[0] ❌ No index access
Duplicates allowed ✅ Yes ❌ No

Object

An object combines multiple named attributes of different types into a single variable — useful for grouping related configuration together.

# variables.tf
variable "bella" {
  type = object({
    name         = string
    color        = string
    age          = number
    food         = list(string)
    favorite_pet = bool
  })

  default = {
    name         = "bella"
    color        = "brown"
    age          = 7
    food         = ["fish", "chicken", "turkey"]
    favorite_pet = true
  }
}

Object attribute reference:

Key Example Value Type
name bella string
color brown string
age 7 number
food ["fish", "chicken", "turkey"] list(string)
favorite_pet true bool

Tuple

A tuple is an ordered sequence of elements with fixed, mixed types. Unlike a list (same type throughout), each position in a tuple has its own declared type, and the element count must match exactly.

# variables.tf — valid
variable "kitty" {
  type    = tuple([string, number, bool])
  default = ["cat", 7, true]
}

Tuple length/type mismatch error

Adding an extra element or using the wrong type causes a plan-time error:

# Invalid — 4 elements provided, tuple expects 3
variable "kitty" {
  type    = tuple([string, number, bool])
  default = ["cat", 7, true, "dog"]
}
Error: Invalid default value for variable
This default value is not compatible with the variable's type constraint:
tuple required.

List vs Tuple:

Feature list tuple
Element types All the same Each position has its own type
Length Variable Fixed at declaration
Use case Homogeneous collections Fixed-schema sequences

Best Practices

Best Practices

  • Always specify type in variable blocks — it documents intent and catches errors at plan time rather than apply time.
  • Use object to group related variables (e.g. all properties of a server) instead of declaring many separate variables.
  • Use map(string) or map(number) rather than plain map to enforce consistent value types across all keys.
  • Prefer list(string) over list so Terraform validates element types.
  • Use set instead of list when order doesn't matter and uniqueness must be guaranteed (e.g. security group rules, tag sets).
  • Add description to every variable, especially complex types, so module consumers know what structure to provide.

Security Best Practices

Security

  • Use sensitive = true on any variable that holds a password, token, or key — regardless of its type (works with string, object, etc.).
  • Avoid storing complex object variables with secrets in default — the default value is stored in state in plaintext.
  • When using map variables to pass environment configs, ensure the map does not include keys that hold secrets unless the variable is marked sensitive.

Do and Don't

✅ Do ❌ Don't
Use type = list(string) to constrain element types Use bare type = list in shared modules
Use object to group related attributes into one variable Create dozens of individual variables for logically related data
Use set when uniqueness of elements must be enforced Use list when duplicates would cause incorrect behaviour
Match default values to the declared type Provide string defaults for a list(number) variable
Access list elements with var.name[index] Try to use index access on a set — sets are unordered

Common Mistakes

Common Mistakes

  • Type/default mismatch: Declaring type = list(number) with string defaults (e.g. ["Mr", "Mrs"]) causes a plan error. Default values must match the declared type.
  • Duplicate values in a set: Sets enforce uniqueness — adding a repeated element causes an immediate error.
  • Exceeding tuple length: A tuple type declaration is a contract — providing more or fewer elements than declared types always fails.
  • Using index access on a set: Unlike lists, sets have no guaranteed order and do not support index ([0]) access.
  • Bare map or list without subtype: Works, but allows mixed types in values/elements, which causes unexpected behaviour in modules and for_each loops.

Quick Recap

  • Terraform has three primitive types (string, number, bool) and five complex types (list, map, set, object, tuple)
  • type is optional — omitting it defaults to any, but explicit types are always preferred
  • List: ordered, indexed from 0, duplicates allowed — access with var.name[index]
  • Map: key-value pairs — access with var.name["key"]
  • Set: like a list but no duplicates and no guaranteed order
  • Object: named attributes of mixed types — defines a schema for complex variables
  • Tuple: fixed-length sequence where each position has a declared type
  • Type mismatches and constraint violations are caught at terraform plan before any infrastructure changes

Interview / Revision Notes

Revision

  • What is the default type if type is omitted from a variable block? any.
  • What is the difference between a list and a set? A list allows duplicates and supports index access; a set does not allow duplicates and has no guaranteed order.
  • What is the difference between a list and a tuple? A list contains elements of the same type; a tuple has a fixed length where each position can have a different type.
  • How do you access the second element of a list variable named prefix? var.prefix[1] (zero-based index).
  • How do you access a map value by key? var.<map_name>["<key>"].
  • What error occurs if a set variable has duplicate default values? Terraform throws a plan-time error — sets require all elements to be unique.
  • When would you use object over multiple separate variables? When grouping logically related attributes (e.g. all properties of a server or pet) into a single, schema-enforced variable.