Terraform Variable Types
Terraform’s type system lets you specify exactly what kind of data a variable accepts. This catches configuration mistakes before apply, provides better IDE support, and makes module interfaces self-documenting.
Primitive Types
string
variable "region" { type = string default = "us-east-1"}
variable "cluster_name" { type = string description = "Name of the EKS cluster" # No default — required input}number
variable "replica_count" { type = number default = 3}
variable "disk_size_gb" { type = number default = 100
validation { condition = var.disk_size_gb >= 20 && var.disk_size_gb <= 16384 error_message = "Disk size must be between 20 GB and 16,384 GB." }}Numbers in Terraform are arbitrary-precision decimals — both 3 and 3.14 are valid number values.
bool
variable "enable_monitoring" { type = bool default = true}
variable "multi_az" { type = bool default = false}
# Usage in conditionalsresource "aws_db_instance" "main" { multi_az = var.multi_az monitoring_interval = var.enable_monitoring ? 60 : 0}Collection Types
list(T)
An ordered sequence of values of the same type:
variable "availability_zones" { type = list(string) default = ["us-east-1a", "us-east-1b", "us-east-1c"]}
variable "allowed_ports" { type = list(number) default = [80, 443, 8080]}
# Usageresource "aws_subnet" "public" { count = length(var.availability_zones) availability_zone = var.availability_zones[count.index] cidr_block = cidrsubnet("10.0.0.0/16", 8, count.index) vpc_id = aws_vpc.main.id}set(T)
An unordered collection of unique values (no duplicates):
variable "allowed_account_ids" { type = set(string) default = ["123456789012", "234567890123"]}
variable "enabled_regions" { type = set(string) default = ["us-east-1", "eu-west-1", "ap-southeast-1"]}
# Use toset() to convert a list to a set for for_eachresource "aws_s3_bucket" "regional" { for_each = var.enabled_regions bucket = "mycompany-${each.key}-data"}map(T)
Key-value pairs where all values are the same type:
variable "instance_types_by_env" { type = map(string) default = { dev = "t3.micro" staging = "t3.small" production = "t3.large" }}
variable "replica_count_by_env" { type = map(number) default = { dev = 1 staging = 2 production = 5 }}
# Usageresource "aws_instance" "app" { instance_type = lookup(var.instance_types_by_env, var.environment, "t3.micro")}
# Or with map lookupresource "aws_autoscaling_group" "app" { desired_capacity = var.replica_count_by_env[var.environment]}Structural Types
object({})
Named attributes with potentially different types:
variable "database" { type = object({ engine = string engine_version = string instance_class = string allocated_storage = number multi_az = bool backup_days = number }) default = { engine = "postgres" engine_version = "16.3" instance_class = "db.t3.micro" allocated_storage = 20 multi_az = false backup_days = 7 }}
resource "aws_db_instance" "main" { engine = var.database.engine engine_version = var.database.engine_version instance_class = var.database.instance_class allocated_storage = var.database.allocated_storage multi_az = var.database.multi_az backup_retention_period = var.database.backup_days}list(object({})) — Most Commonly Used Complex Type
variable "ingress_rules" { type = list(object({ port = number protocol = string description = string cidr_blocks = list(string) })) default = [ { port = 443 protocol = "tcp" description = "HTTPS" cidr_blocks = ["0.0.0.0/0"] }, { port = 8080 protocol = "tcp" description = "App port from ALB only" cidr_blocks = ["10.0.0.0/8"] } ]}
resource "aws_security_group" "app" { dynamic "ingress" { for_each = var.ingress_rules content { from_port = ingress.value.port to_port = ingress.value.port protocol = ingress.value.protocol description = ingress.value.description cidr_blocks = ingress.value.cidr_blocks } }}tuple
An ordered sequence where each position has a specific type (rare — usually list or object is better):
variable "config_pair" { type = tuple([string, number]) default = ["t3.micro", 3]}# config_pair[0] = "t3.micro" (string)# config_pair[1] = 3 (number)The any Type
Disables type checking — use sparingly:
variable "flexible_config" { type = any default = {}}any is sometimes used in module variables where the exact type depends on the caller. Prefer explicit types — they catch mistakes early.
Type Conversion Functions
# Convert types when neededlocals { # String to number port_number = tonumber("8080")
# Number to string port_string = tostring(8080)
# List to set (removes duplicates, loses order) unique_regions = toset(["us-east-1", "eu-west-1", "us-east-1"]) # Result: {"eu-west-1", "us-east-1"}
# String to list az_list = tolist(["a", "b", "c"])}Quick Reference
| Type | Example | Notes |
|---|---|---|
string | "us-east-1" | Unicode text |
number | 42 or 3.14 | Arbitrary precision decimal |
bool | true / false | Boolean |
list(string) | ["a", "b", "c"] | Ordered, allows duplicates |
set(string) | toset(["a", "b"]) | Unordered, unique values |
map(string) | {key = "val"} | String keys, same-type values |
object({}) | {name = string, age = number} | Named attributes, mixed types |
tuple([]) | [string, number] | Fixed-length, mixed types |
any | Anything | Disables type checking |