Terraform: introduction to primitives and complex data types
Tags: terraform, devops, todayilearned
Published: 2023-09-10 09:28:19

---
title: Terraform: introduction to primitives and complex data types
published: true
date: 2023-09-09 15:20:14 UTC
tags: terraform,devops,todayilearned
canonical_url: https://rtfm.co.ua/en/terraform-introduction-to-data-types-primitives-and-complex/
---

### Terraform: Introduction to primitives and complex data types

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

![](https://cdn-images-1.medium.com/max/1024/1*-OlfXBGOKX3pS7UgeRGGpQ.png)

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](https://rtfm.co.ua/en/terraform-count-for_each-and-for-loops/).

Documentation — [Type Constraints](https://developer.hashicorp.com/terraform/language/expressions/type-constraints) and [Types and Values](https://developer.hashicorp.com/terraform/language/expressions/types).

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
- s`et`: 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
}
```

The result is obvious:

```
...
Outputs:

string = "a string"
```

#### `number`

Similarly, but for integer values:

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

output "number" {
  value = var.var_number
}
```

Result:

```
...
Outputs:

number = 1
```

#### `bool`

Used for the [Conditional Expressions](https://developer.hashicorp.com/terraform/language/expressions/conditionals):

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

output "number" {
  value = var.var_bool ? "True" : "False"
}
```

Result:

```
...
Outputs:

number = "True"
```

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
}
```

### 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
}
```

Result:

```
...
Outputs:

list_any = tolist([
  "a string",
  "10",
])
list_string = tolist([
  "first string",
  "second string",
])
```

And the file:

```
$ cat file-10.txt 
first string
```

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
}
```

Result:

```
...
Outputs:

list_any = tolist([
  "a",
  "true",
  "1",
])
list_lists = tolist([
  [
    "a",
    "b",
  ],
  [
    "c",
    "d",
  ],
])
```

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)]
}
```

Result:

```
...
Outputs:

list_string = [
  "FIRST STRING",
  "SECOND STRING",
]
```

And files:

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

$ cat file-a\ string.txt  
a string
```

#### `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"]
}
```

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

Result:

```
...
Outputs:

map_one = "first"
map_two = "second"
```

We can also use the [`lookup()`](https://developer.hashicorp.com/terraform/language/functions/lookup) to search for values in a `map` ​​by a key:

```
output "map_lookup" {
  value = lookup(var.var_map, "one", "None")
}
```

Result:

```
...
Outputs:

map_lookup = "first"
map_one = "first"
map_two = "second"
```

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)
}
```

Result:

```
...
Outputs:

instances_count = 2
```

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
}
```

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"
  }
})
```

#### `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
}
```

Result:

```
...
...
Outputs:

set_any = toset([
  "1",
  "string",
])
set_string = toset([
  "string1",
  "string2",
])
```

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
}
```

Result:

```
...
set_any = toset([
  tolist([
    "a",
    "b",
  ]),
  tolist([
    "c",
    "d",
  ]),
])
```

### 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](https://www.programiz.com/c-programming/c-structures) 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
}
```

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"
})
```

#### `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]
}
```

Result:

```
Outputs:

tuple = [
  "one",
  10,
  tolist([
    "first",
    "second",
  ]),
  tomap({
    "one" = "first"
    "two" = "second"
  }),
]
tuple_map = tomap({
  "one" = "first"
  "two" = "second"
})
```

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

_Originally published at_ [_RTFM: Linux, DevOps, and system administration_](https://rtfm.co.ua/en/terraform-introduction-to-data-types-primitives-and-complex/)_._

* * *