Terraform: знайомство з типами даних – primitives та complex

Автор |  04/09/2023
 

В цьому пості трохи подивимось на типи даних, які можемо використовувати в Terraform, щоб простіше розібратись з наступним постом – Terraform: цикли count, for_each та for.

Документація – Type Constraints та Types and Values.

Маємо наступні типи поділені на групи:

  • Primitive Types:
    • string: послідовність Unicode символів, простий текст
    • number: числові значення
    • bool: true або false
  • Complex Types:
    • Collection Types:
      • list: список – тип структури для зберігання простої колекції значень одного типу, доступних по індексам
      • map: колекція key:value значень одного типу
      • set: аналогічна list, але без індексів та сортування
    • Structural Types:
      • object: для зберігання значені різних типів даних – набір іменованих атрибутів, кожен з власним типом даних
      • tuple: послідовність елементів, кожен з власним типом даних, з індексами як у list

Primitive types

Найпростіший тип, в якому можемо зберігати тільки одне значення певного типу.

string

Приклад:

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

output "string" {
  value = var.var_string
}

Результат очевидний:

...
Outputs:

string = "a string"

number

Аналогічно, але для integer значень:

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

output "number" {
  value = var.var_number
}

Результат:

...
Outputs:

number = 1

bool

Використовується для Conditional Expressions:

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

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

Результат:

...
Outputs:

number = "True"

Або створення ресурсу, якщо умова дійсна:

resource "local_file" "file" {
  count = var.var_bool ? 1 : 0
  
  filename = "file.txt"
  content = var.var_string
}

Collection Types

list

Послідовність значень одного типу з індексами, починаючи з нуля.

При створенні list можна або не вказувати тип (default == any), або обмежити одним певним типом:

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
}

Результат:

...
Outputs:

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

І файл:

$ cat file-10.txt 
first string

В list можна використовувати інші типи даних – інші list, map тощо.

При цьому в одному list можуть бути різні типи примітивів (string, number, bool), але однаковий тип для інших типів, тобто:

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
}

Результат:

...
Outputs:

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

Зі списками можемо використовувати цикли, наприклад:

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

Результат:

...
Outputs:

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

Та файли:

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

$ cat file-a\ string.txt  
a string

map

Значення у формі key:value з доступом до значення по імені ключа:

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

Також в outputs можемо вивести атрибут, тобто value = var.var_map.one.

Результат:

...
Outputs:

map_one = "first"
map_two = "second"

Також з map можемо використати lookup() для пошуку значення по ключу:

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

Результат:

...
Outputs:

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

Або більш складний приклад – вибір кількості інстансів за ціною в залежності від типу:

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

Результат:

...
Outputs:

instances_count = 2

map також може включати в себе list або інший map, але всі об’єкти мають бути одного типу (тобто, не можна мати map в якому будуть і list, і другий map):

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
}

Результат:

...
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

Послідовність значень одного або різних типів як в list, але без індексів та сортування:

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
}

Результат:

...
...
Outputs:

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

Як і list або map, set може мати вкладені типи:

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

output "set_any" {
  value = var.var_set_lists
}

Результат:

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

Structural Types

object

На відміну від map та list, object є структурним типом, який може мати значення різних типів, в тому числі включати в себе типи list та map.

Схож на Struct в C або 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
}

Результат:

...
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

Подібний до object, але з індексами замість імен ключів:

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

Результат:

Outputs:

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

В наступному пості подивимось на цикли.

Посилання по темі