Terraform uses a powerful and flexible configuration language (HCL - HashiCorp Configuration Language) to define infrastructure as code. As with any programming or declarative language, understanding the types of values you're working with is fundamental to writing effective and error-free code.
In Terraform, value types are used to define variables, locals, module inputs, and more. Types determine how values behave, how they can be accessed or transformed, and whether they are valid for a given resource or input block.
Terraform supports a rich type system that includes:
string
, number
, and bool
list
, set
, and map
object
and tuple
any
and null
, used for flexible or optional values
This tutorial explores each of these types in detail, explains how and when to use them, and demonstrates techniques
for converting between types using built-in functions. We'll also compare similar types (like list
vs
set
, or map
vs object
) to help you choose the right one for your use case.
Whether you're writing simple configurations or building reusable modules, mastering Terraform's type system will make your code more predictable, modular, and robust.
Terraform supports three primitive types: string
, number
, and bool
.
These are the simplest types used to represent individual values and are commonly used in variables,
locals, and resource arguments.
string
A string
is a sequence of Unicode characters enclosed in double quotes ("..."
).
Multiline strings can be created using heredoc syntax (<<EOT ... EOT
).
Example:
<<EOT
(Standard Heredoc) preserves all whitespace exactly as written, including any leading
tabs or spaces at the beginning of lines.
<<-EOT
(Indented Heredoc) strips leading tabs (but not spaces) from each line in the heredoc
content. This allows you to indent your heredoc content in your Terraform file for readability, without those
indentations being preserved in the actual string value.
Terraform also supports string interpolation using the ${...}
syntax, which allows you to embed variable
values and expressions inside strings.
number
A number
represents a numeric value. It can be an integer or a floating-point number. Terraform
does not distinguish between integer and float types internally; both are treated as number
.
Example:
You can use arithmetic operations and functions when defining numeric variables:
bool
A bool
is a boolean value that can be either true
or false
. Booleans are
commonly used in conditional expressions and to toggle features in configuration.
Example:
You can use bool
values directly in condition evaluations:
Terraform supports complex data types that allow you to group multiple values into structured collections.
Complex types fall into two main categories:
list
, set
, and map
containing multiple values of
the same typeobject
and tuple
consisting of multiple values of several
potentially distinct types
These types form the foundation for more advanced Terraform features like
for_each
, dynamic blocks, and complex variable schemas. Choosing the right type, especially when
working with inputs and modules, helps make your configuration more predictable, modular, and reusable.
Let's review each type in more details.
list(type)
A list
is an ordered collection of values, all of the same type. List elements are identified by
a number (index), starting from 0.
Example:
You can access individual list
elements using the square-bracket index notation:
map(type)
A map
is a collection of key-value pairs, where the keys are strings and all values have the
same type. Maps are great for structured configurations or lookup tables.
Example:
You can use map's keys and either the square-bracket index notation or the dot-separated attribute notation to access map values:
object({ ... })
An object
has a fixed schema with named attributes, each with its own type declaration. Unlike a map, the types
and structure of the values are explicitly defined. Objects are ideal for passing structured settings to modules and for creating
predictable input formats
The schema for object types is a comma-separated series of <ATTR_NAME> = <TYPE>
,
pairs wrapped in curly braces -
{ <ATTR_NAME> = <TYPE>, <ATTR_NAME> = <TYPE>, ... }
.
Example:
Individual elements are accessed using the square-bracket index notation (var.app_config["name"]
) or the
dot-separated attribute notation (var.app_config.name
).
set(type)
A set
is an unordered collection of unique values, all of the same type. Sets do not preserve
order, and duplicate values are automatically removed. Also, sets do not have any secondary identifiers,
therefore you cannot directly access elements of a set by index.
Sets are useful when you need uniqueness and element order is not important, such as applying a tag list or creating service accounts.
Example:
tuple([ ... ])
A tuple
is an ordered collection of values, where each position can have a different type.
Tuples are fixed-length and positional.
Use tuples when the position and type of each element are significant.
The schema for tuple types is a comma-separated series of types wrapped in a pair of square brackets
[ <TYPE>, <TYPE>, ... ]
.
Example:
Items are accessed using the square-bracket index notation (var.app_info[0]
).
In addition to primitive and structural types, Terraform supports special types - any
and null
that provide flexibility when writing reusable modules or dynamic logic.
any
The any
type acts as a wildcard type that accepts any valid Terraform value, whether primitive,
collection, or structural.
It's often used in situations when you want to allow maximum flexibility and let the user supply any kind of value, such as:
Use any
sparingly, and only when truly needed. Prefer more specific types whenever possible or use
object type constraints with optional
modifier.
Examples:
null
The null
value in Terraform represents the absence or omission of a value. It's not a type itself,
but a special value that can be assigned to any type (except bool
, where it defaults to false
).
Terraform utilizes null
value in several ways:
Terraform's type system supports nesting of value types, allowing you to define rich, structured data. These nested types are particularly useful for defining complex input variables and local values in reusable modules.
You can combine collection types (list
, set
, map
) and structural types
(object
, tuple
) to model advanced data structures such as:
Examples 1: List of Maps
A list of maps is commonly used to represent a series of items where each item has the same schema (like tags, policies, rules, etc.).
Examples 2: Map of Tuples
A map of tuples is used when each key maps to a fixed set of values of different types.
Examples 3: Object with Nested List
You can define an object that includes nested collections, such as a list of objects inside an object.
Terraform offers several types that can seem similar but behave differently. Understanding how they compare helps you choose the right one for clarity, validation, and functionality.
Here are the key similarities and differences between the similar value types:
map
vs object
Similarities:
map
and object
are key-value collectionsvar.abc["name"]
or var.abc.name
Differences:
Feature | map(type) |
object({ ... }) |
---|---|---|
Schema | Arbitrary keys, all values of the same type | Named attributes with specific types |
Flexibility | Dynamic, number of items may vary | Fixed-length, attributes must match the schema exactly |
tuple
vs list
Similarities:
tuple
and list
are ordered collectionsvar.abc[index]
Differences:
Feature | tuple([type1, type2, ...]) |
list(type) |
---|---|---|
Value Types | Each item can have a different type | All items must have the same type |
Flexibility | Fixed-length, positional structure | Dynamic, number of items may vary |
list
vs set
Similarities:
list
and set
are collections of values of the same typeDifferences:
Feature | list(type) |
set(type) |
---|---|---|
Ordering | Preserves element order | Unordered (order is ignored) |
Duplicates | Allowed | Not allowed (duplicates removed during conversion) |
Indexable | Yes, access with [index] |
No, index access not supported |
Type | Ordered | Key-Value | Value Type(s) |
---|---|---|---|
list(type) |
Yes | No | Same type |
map(type) |
No | Yes | Same type |
object({...}) |
No | Yes | Named fields, allows different types |
set(type) |
No | No | Same type |
tuple([...]) |
Yes | No | Positional, allows different types |
Terraform Types Venn Diagram:
Terraform allows both implicit and explicit value type conversions, and offers built-in functions to convert between types when needed.
Understanding how Terraform handles type conversion helps prevent errors and makes your configurations more predictable when working with functions or module inputs.
Let's explore implicit and explicit type conversions in more details.
Terraform performs automatic (implicit) type conversion in situations where it is safe and unambiguous. These conversions typically occur when passing values to variables, modules, or resource arguments that expect a different but compatible type.
Conversion of Primitive Types
Terraform will automatically convert between primitive types when possible:
"42" → 42
"true" → true
"Count: ${3}" → "Count: 3"
"Enabled: ${true}" → "Enabled: true"
Conversion of Complex Types
Terraform also performs automatic conversions between similar collection and structural types when their content and structure are compatible. The actual conversion behavior is determined by the source and target types and the structure and content of the value being assigned. In some scenarios, conversion may result in data loss.
Also remember, when assigning a value to a variable, Terraform interprets input as follows:
[ ... ]
create a tuple{ ... }
with named keys create an objectBelow we provide more details about conversions between some Terraform value types.
map
to object
A map(type)
can be implicitly converted to an object({...})
if the map's keys
match the attributes expected in the object and the values can be converted to the corresponding
object's types.
Constraints:
Example:
The following code assigns a map(string)
value (variable mymap
) to the module's input
variable of object()
type and displays the conversion results:
To display the conversion results, run
terraform init
terraform apply -auto-approve
Notice how Terraform converts a string value ("b" = "5"
) into a number ("b" = 5
)
and discards "c" = "extra"
.
You can also use Terraform console to check the source and target types:
Conversion fails if the source map()
does not contains all keys present in the target
object()
:
object
to map
An object()
can be implicitly converted to a map(type)
if all attribute values
can be converted to the target value type.
Constraints:
Example:
The following code assigns a object()
value (variable myobj
) to the module's
input variable of map(string)
type and displays the conversion results:
To display the conversion results, run
terraform init
terraform apply -auto-approve
Notice how Terraform converts the numeric and boolean values into strings.
You can also use Terraform console to check the source and target types:
list
to set
A list(type)
can be converted to a set(type)
if all element values can be converted
to the target set's type.
Constraints:
Example:
The following code assigns a list(number)
value (variable mylist
) to the module's input
variable of set(string)
type and displays the conversion results:
To display the conversion results, run
terraform init
terraform apply -auto-approve
Notice how Terraform converts all numeric values into strings and removes duplicates.
You can also use the type()
function to check the source and target types in
the Terraform console (as shown in the previous example).
set
to list
A set(type)
can be converted to a list(type)
if all element values can be converted
to the target type; element order is not guaranteed.
Constraints:
Example:
The following code assigns a set(number)
value (variable myset
) to the module's input
variable of list(string)
type and displays the conversion results:
To display the conversion results, run
terraform init
terraform apply -auto-approve
Notice how Terraform converts all numeric values into strings.
You can also use the type()
function to check the source and target types in the
Terraform console (as shown in one of the previous examples).
tuple
to list
A tuple([...])
can be converted to a list(type)
if all elements are of the same type or
convertible to the list's element type.
Constraints:
Example:
The following code assigns a tuple([...])
value (variable mytuple
) to the module's
input variable of list(string)
type and displays the conversion results:
To display the conversion results, run
terraform init
terraform apply -auto-approve
Notice how Terraform converts all numeric and boolean values into strings.
You can also use the type()
function to check the source and target types in the
Terraform console (as shown in one of the previous examples).
tuple
to set
A tuple([...])
can be converted to a set(type)
if all elements are of the same type
or can be converted to the set's type.
Constraints:
Example:
The following code assigns a tuple([...])
value (variable mytuple
) to the module's
input variable of set(string)
type and displays the conversion results:
To display the conversion results, run
terraform init
terraform apply -auto-approve
Notice how Terraform converts all numeric and boolean values into strings and removes duplicates
You can also use the type()
function to check the source and target types in the
Terraform console (as shown in one of the previous examples).
Terraform provides a set of built-in functions to convert between value types:
Function | Description | Example |
---|---|---|
tobool() |
Converts strings to boolean | tobool("true") → true |
tolist() |
Converts a set or tuple to a list | tolist(["a", 5, true]) → ["a", "5", "true"] |
tomap() |
Converts a structure to a map | tomap({a = "b", b = 5}) → {a = "b", b = "5"} |
tonumber() |
Converts a string (if numeric) to a number | tonumber("4.200") → 4.2 |
toset() |
Converts a list or tuple to a set | toset(["a", "a", "b"]) → ["a", "b"] |
tostring() |
Converts any primitive or structured value to a string | tostring(4.200) → "4.2" |
type() |
Returns the type of a value (Terraform console only) | type(["a", 5, true]) →
|
Example: Using conversion functions in terraform console
In an empty directory create a file, for example: main.tf
, containing the following code:
Launch terraform console
and try some of the conversion examples from the table above.
Terraform's type system is flexible enough to express rich and structured inputs. Understanding how type conversion works, especially between similar types, will help you build reusable modules, prevent subtle bugs, and write more reliable configuration code.
Terraform's wide selection of value types offers both flexibility and precision for managing infrastructure as code. In this tutorial we analyzed how primitive, collection, and structural types work and illustrated how Terraform performs implicit and explicit type conversions.
By understanding the nuances of the Terraform type system and following the best practices outlined below, you'll improve reliability and reusability of your infrastructure code and reduce bugs caused by unexpected behaviors or silent type mismatches.
Best Practices for Using Terraform Types:
type = ...
in variable blocks to clearly
document what structure and value types are expected. This simplifies validation and improves code
maintainability.
object
, tuple
) for strict schema enforcement.
Use object({...})
when you need clearly defined named attributes, and
tuple([...])
for fixed-position mixed-type arrays.
any
, list(any)
, etc.) sparingly.
These types can be helpful during prototyping, but should be replaced with more specific types to
avoid runtime errors and unexpected conversions.
locals
if you need to perform type conversions.
type()
in Terraform console to inspect values.
Helpful when debugging complex or nested input types.
tostring()
, tolist()
, tomap()
, etc. to ensure predictable
code behavior.
set
is unordered, don't use it when sequence of items is important.
Do you want to learn more about Terraform? Please, check our other tutorials:
Handling Terraform State in Multi-Environment Deployments
Understanding Terraform Variable Precedence
Terraform count
Explained with Practical Examples
Terraform for_each
Tutorial with Practical Examples
Exploring Terraform dynamic
Blocks with GCP Examples
Working with External Data in Terraform
Handling Sensitive and Ephemeral Data in Terraform
Terraform Modules FAQ