Contents
Type constraints control the types of values that Terraform accepts for variables and outputs. Terraform supports primitive types (string, number, bool), collection types (list, map, set), and structural types (object, tuple).
Primitive Types
A primitive type is a simple type that isn’t made from any other types. All primitive types in Terraform are represented by a type keyword. The available primitive types are:
string— a sequence of Unicode characters representing some text, such as"hello".number— a numeric value. Thenumbertype can represent both whole numbers like15and fractional values such as6.283185.bool— eithertrueorfalse.boolvalues can be used in conditional logic.
Conversion of Primitive Types
The Terraform language will automatically convert number and bool values to string values when needed, and vice-versa as long as the string contains a valid representation of a number or boolean value.
trueconverts to"true", and vice-versafalseconverts to"false", and vice-versa15converts to"15", and vice-versa
Complex Types
A complex type is a type that groups multiple values into a single value. Complex types are represented by type constructors, but several of them also have shorthand keyword versions.
There are two categories of complex types: collection types (for grouping similar values), and structural types (for grouping potentially dissimilar values).
Collection Types
A collection type allows multiple values of one other type to be grouped together as a single value. The type of value within a collection is called its element type. All collection types must have an element type, which is provided as the argument to their constructor.
For example, the type list(string) means “list of strings”, which is a different type than list(number), a list of numbers. All elements of a collection must always be of the same type.
The three kinds of collection type in the Terraform language are:
list(...)— a sequence of values identified by consecutive whole numbers starting with zero. The keywordlistis a shorthand forlist(any), which accepts any element type as long as every element is the same type.map(...)— a collection of values where each is identified by a string label. The keywordmapis a shorthand formap(any), which accepts any element type as long as every element is the same type.set(...)— a collection of unique values that do not have any secondary identifiers or ordering.
Map values can be defined using {}, :, or =. For example, { "foo": "bar", "bar": "baz" } and { foo = "bar", bar = "baz" } define the same map. You must place map keys in quotation marks when a key starts with a number, contains spaces, or contains special characters. Commas are required between key-value pairs in single-line maps. Multi-line maps can place key-value pairs on new lines.
Structural Types
A structural type allows multiple values of several distinct types to be grouped together as a single value. Structural types require a schema as an argument, to specify which types are allowed for which elements.
The two kinds of structural type in the Terraform language are:
object(...)— a collection of named attributes that each have their own type. The schema for object types is{ <KEY> = <TYPE>, <KEY> = <TYPE>, ... }. Values that match the object type must contain all of the specified keys, and the value for each key must match its specified type. Values with additional keys can still match an object type, but the extra attributes are discarded during type conversion.tuple(...)— a sequence of elements identified by consecutive whole numbers starting with zero, where each element has its own type. The schema for tuple types is[<TYPE>, <TYPE>, ...]. Values that match the tuple type must have exactly the same number of elements, and the value in each position must match the specified type for that position.
For example, an object type of object({ name=string, age=number }) would match:
{
name = "John"
age = 52
}A tuple type of tuple([string, number, bool]) would match:
["a", 15, true]Optional Object Type Attributes
To mark attributes as optional, use the optional modifier in the object type constraint:
variable "with_optional_attribute" {
type = object({
a = string # a required attribute
b = optional(string) # an optional attribute
c = optional(number, 127) # an optional attribute with default value
})
}The optional modifier takes one or two arguments:
- Type — (Required) The first argument specifies the type of the attribute.
- Default — (Optional) The second argument defines the default value that Terraform should use if the attribute is not present. This must be compatible with the attribute type. If not specified, Terraform uses a
nullvalue of the appropriate type as the default.
Nested Structures with Optional Attributes and Defaults
The following example defines a variable for storage buckets that host a website. This variable type uses several optional attributes, including website, which is itself an optional object type that has optional attributes and defaults.
variable "buckets" {
type = list(object({
name = string
enabled = optional(bool, true)
website = optional(object({
index_document = optional(string, "index.html")
error_document = optional(string, "error.html")
routing_rules = optional(string)
}), {})
}))
}Examples
Primitive Types
variable "instance_name" {
type = string
default = "web-server"
}
variable "instance_count" {
type = number
default = 3
}
variable "enable_monitoring" {
type = bool
default = true
}List
variable "availability_zones" {
type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
variable "instance_ports" {
type = list(number)
default = [80, 443, 8080]
}Map
variable "instance_tags" {
type = map(string)
default = {
Environment = "production"
Team = "platform"
ManagedBy = "terraform"
}
}
variable "instance_counts" {
type = map(number)
default = {
dev = 1
staging = 2
prod = 3
}
}Set
variable "allowed_cidrs" {
type = set(string)
default = [
"10.0.0.0/16",
"172.16.0.0/12",
"192.168.0.0/24",
]
}Object
variable "database" {
type = object({
engine = string
instance_class = string
allocated_storage = number
multi_az = bool
})
default = {
engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 50
multi_az = true
}
}Tuple
variable "rule" {
type = tuple([string, number, bool])
default = ["allow", 443, true]
}List of Objects
variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{
port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
]
}Map of Objects
variable "environments" {
type = map(object({
instance_type = string
min_size = number
max_size = number
enable_ssl = bool
}))
default = {
dev = {
instance_type = "t3.small"
min_size = 1
max_size = 2
enable_ssl = false
}
prod = {
instance_type = "t3.large"
min_size = 2
max_size = 10
enable_ssl = true
}
}
}Nested Objects
variable "application" {
type = object({
name = string
network = object({
vpc_cidr = string
subnets = list(object({
cidr = string
az = string
type = string
}))
})
database = object({
engine = string
replicas = number
backup = object({
enabled = bool
retention_days = number
})
})
})
default = {
name = "my-app"
network = {
vpc_cidr = "10.0.0.0/16"
subnets = [
{ cidr = "10.0.1.0/24", az = "us-east-1a", type = "public" },
{ cidr = "10.0.2.0/24", az = "us-east-1b", type = "public" },
{ cidr = "10.0.3.0/24", az = "us-east-1a", type = "private" },
{ cidr = "10.0.4.0/24", az = "us-east-1b", type = "private" },
]
}
database = {
engine = "postgres"
replicas = 2
backup = {
enabled = true
retention_days = 7
}
}
}
}Nested with Optional Attributes
variable "services" {
type = list(object({
name = string
port = number
health_check = optional(object({
path = optional(string, "/health")
interval = optional(number, 30)
timeout = optional(number, 5)
}), {})
scaling = optional(object({
min_count = optional(number, 1)
max_count = optional(number, 3)
cpu_target = optional(number, 70)
}))
tags = optional(map(string), {})
}))
}Usage in terraform.tfvars:
services = [
{
name = "api"
port = 8080
health_check = {
path = "/api/health"
interval = 10
}
scaling = {
min_count = 2
max_count = 10
}
tags = { tier = "backend" }
},
{
name = "frontend"
port = 3000
# health_check and scaling use defaults
},
]