DEV Community

Arseny Zinchenko
Arseny Zinchenko

Posted on • Originally published at rtfm.co.ua on

Terraform: introduction to primitives and complex data types

Terraform: Introduction to primitives and complex data types

Overview of data types in Terraform — string, number, bool, list/tuple, and map/set

In this post, we will take a brief look at the data types that we can use in Terraform to better understand the topic of the following post — Terraform: count, for_each, and for loops.

Documentation — Type Constraints and Types and Values.

We have the following types divided into groups:

  • Primitive Types:
  • string: sequence of Unicode characters, plain text
  • number: numerical values
  • bool: true or false
  • Complex Types:
  • Collection Types:
  • list: a list is a type of structure for storing a simple collection of values ​​of the same type, accessible by index
  • map: a key:value collection of values ​​of the same type
  • set: similar to the list, but without indexes and sorting
  • Structural Types:
  • object: to store values ​​of different data types – a set of named attributes, each with its own data type
  • tuple: a sequence of elements, each with its own data type, indexed as in the list

Primitive types

The simplest type, in which we can store only one value of a certain type.

string

An example:

variable "var_string" {
  type = string
  default = "a string"
}

output "string" {
  value = var.var_string
}
Enter fullscreen mode Exit fullscreen mode

The result is obvious:

...
Outputs:

string = "a string"
Enter fullscreen mode Exit fullscreen mode

number

Similarly, but for integer values:

variable "var_number" {
  type = number
  default = 1
}

output "number" {
  value = var.var_number
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

number = 1
Enter fullscreen mode Exit fullscreen mode

bool

Used for the Conditional Expressions:

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

output "number" {
  value = var.var_bool ? "True" : "False"
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

number = "True"
Enter fullscreen mode Exit fullscreen mode

Or creating a resource if the condition is valid:

resource "local_file" "file" {
  count = var.var_bool ? 1 : 0

  filename = "file.txt"
  content = var.var_string
}
Enter fullscreen mode Exit fullscreen mode

Collection Types

list

A sequence of values ​​of the same type with indices starting from zero.

When creating a list, you can either not specify the type (default == any), or limit it to one specific type:

variable "var_list_any" {
  type = list 
  default = ["a string", 10]
}

variable "var_list_string" {
  type = list(string)
  default = ["first string", "second string"]
}

resource "local_file" "file" {
  filename = "file-${var.var_list_any[1]}.txt"

  content = var.var_list_string[0]
}

output "list_any" {
  value = var.var_list_any
}

output "list_string" {
  value = var.var_list_string
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

list_any = tolist([
  "a string",
  "10",
])
list_string = tolist([
  "first string",
  "second string",
])
Enter fullscreen mode Exit fullscreen mode

And the file:

$ cat file-10.txt 
first string
Enter fullscreen mode Exit fullscreen mode

Within a list you can use other data types – other list, map, etc.

At the same time, in a list we can have different types of primitives (string, number, bool), but the same type for other types, i.e.:

variable "var_list_any" {
  type = list
  default = ["a", true, 1]
}

variable "var_list_lists" {
  type = list
  default = [
    ["a", "b"],
    ["c", "d"]
  ]
}

output "list_any" {
  value = var.var_list_any
}

output "list_lists" {
  value = var.var_list_lists
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

list_any = tolist([
  "a",
  "true",
  "1",
])
list_lists = tolist([
  [
    "a",
    "b",
  ],
  [
    "c",
    "d",
  ],
])
Enter fullscreen mode Exit fullscreen mode

With the list we can use loops, for example:

variable "var_list_any" {
  type = list 
  default = ["a string", 10]
}

variable "var_list_string" {
  type = list(string)
  default = ["first string", "second string"]
}

resource "local_file" "file" {
  for_each = toset(var.var_list_any)

  filename = "file-${each.key}.txt"
  content = each.value
}

output "list_string" {
  value = [for a in var.var_list_string : upper(a)]
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

list_string = [
  "FIRST STRING",
  "SECOND STRING",
]
Enter fullscreen mode Exit fullscreen mode

And files:

$ ls -1
file-10.txt
'file-a string.txt'

$ cat file-a\ string.txt  
a string
Enter fullscreen mode Exit fullscreen mode

map

A value in the key:value form with access to the value by the key name:

variable "var_map" {
  type = map
  default = {
    "one" = "first",
    "two" = "second"
  }
}

output "map_one" {
  value = var.var_map["one"]
}

output "map_two" {
  value = var.var_map["two"]
}
Enter fullscreen mode Exit fullscreen mode

We can also display the attribute in outputs, i.e. value = var.var_map.one.

Result:

...
Outputs:

map_one = "first"
map_two = "second"
Enter fullscreen mode Exit fullscreen mode

We can also use the lookup() to search for values in a map ​​by a key:

output "map_lookup" {
  value = lookup(var.var_map, "one", "None")
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

map_lookup = "first"
map_one = "first"
map_two = "second"
Enter fullscreen mode Exit fullscreen mode

Or a more complex example — choosing a number of instances by the price depending on the instance type:

variable "instance_cost" {
  type = map
  default = {
    "t3.medium" = "0.04USD",
    "t3.large" = "0.08USD",
  }
}

variable "instance_number" {
  type = map
  default = {
    "0.04USD" = 2,
    "0.08USD" = 1,
  }
}

output "instances_count" {
  value = lookup(var.instance_number, var.instance_cost["t3.medium"], 0)
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

instances_count = 2
Enter fullscreen mode Exit fullscreen mode

A map can also include a list or another map, but all objects must be of the same type (that is, you cannot have a map with a list in a first item, and another map in a second):

variable "var_map_of_maps" {
  type = map
  default = {
    "out-map-key-1" = {
      "in-map-key-1" = "inner map 1 key one",
      "in-map-key-2" = "inner map 1 inner key two",
    },
    "out-map-key-2" = {
      "in-map-key-1" = "inner map 2 key one",
      "in-map-key-2" = "inner map 2 key two",
    },
  }
}

output "map_of_maps" {
  value = var.var_map_of_maps
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

map_of_maps = tomap({
  "out-map-key-1" = {
    "in-map-key-1" = "inner map 1 key one"
    "in-map-key-2" = "inner map 1 inner key two"
  }
  "out-map-key-2" = {
    "in-map-key-1" = "inner map 2 key one"
    "in-map-key-2" = "inner map 2 key two"
  }
})
Enter fullscreen mode Exit fullscreen mode

set

A sequence of values ​​of the same or different types as in list, but without indexes and sorting:

variable "var_set_any" {
  type = set(any)
  default = ["string", 1]
}

variable "var_set_string" {
  type = set(string)
  default = ["string1", "string2"]
}

output "set_any" {
  value = var.var_set_any
}

output "set_string" {
  value = var.var_set_string
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
...
Outputs:

set_any = toset([
  "1",
  "string",
])
set_string = toset([
  "string1",
  "string2",
])
Enter fullscreen mode Exit fullscreen mode

Like with list or map, a set can have nested types:

variable "var_set_lists" {
  type = set(list(any))
  default = [
    ["a", "b"],
    ["c", "d"]
  ]
}

output "set_any" {
  value = var.var_set_lists
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
set_any = toset([
  tolist([
    "a",
    "b",
  ]),
  tolist([
    "c",
    "d",
  ]),
])
Enter fullscreen mode Exit fullscreen mode

Structural Types

object

Unlike map and list, object is a structural type that can have values ​​of various types, including several list and map.

Similar to Struct in C or Golang:

variable "var_object" {
  type = object({
    name = string,
    id = number,
    data = list(string)
    data_map = map(any)
  })

  default = {
    name = "one",
    id = 10,
    data = ["first", "second"],
    data_map = {
      "one" = "first",
      "two" = "second"
    }
  }
}

output "object" {
  value = var.var_object
}

output "object_map" {
  value = var.var_object.data_map
}
Enter fullscreen mode Exit fullscreen mode

Result:

...
Outputs:

object = {
  "data" = tolist([
    "first",
    "second",
  ])
  "data_map" = tomap({
    "one" = "first"
    "two" = "second"
  })
  "id" = 10
  "name" = "one"
}
object_map = tomap({
  "one" = "first"
  "two" = "second"
})
Enter fullscreen mode Exit fullscreen mode

tuple

Similar to the object, but with indexes instead of key names:

variable "var_tuple" {
  type = tuple ([
    string,
    number,
    list(string),
    map(any)
  ] )

  default = [
    "one",
    10,
    ["first", "second"],
    {
      "one" = "first",
      "two" = "second"
    }
  ]
}

output "tuple" {
  value = var.var_tuple
}

output "tuple_map" {
  value = var.var_tuple[3]
}
Enter fullscreen mode Exit fullscreen mode

Result:

Outputs:

tuple = [
  "one",
  10,
  tolist([
    "first",
    "second",
  ]),
  tomap({
    "one" = "first"
    "two" = "second"
  }),
]
tuple_map = tomap({
  "one" = "first"
  "two" = "second"
})
Enter fullscreen mode Exit fullscreen mode

In the next post, we will look at loops in Terraform.

Originally published at RTFM: Linux, DevOps, and system administration.


Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay