Примитивные типы
Базовых типов три:
| Тип | Литерал | Пример |
|---|---|---|
string | в двойных кавычках | "us-east-1" |
number | целое или дробное | 42, 3.14 |
bool | true / false | true |
В переменных тип объявляется явно:
variable "region" {type = string
default = "us-east-1"
}
variable "retention_days" {type = number
default = 30
}
variable "enable_versioning" {type = bool
default = false
}
Сложные коллекционные типы
Collection, это «много одинакового». Все элементы одного типа.
list (упорядоченный)
variable "az_names" {type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
Обращение по индексу: var.az_names[0] → "us-east-1a". Порядок
важен и стабилен.
set (без порядка, без дубликатов)
variable "allowed_cidrs" {type = set(string)
default = ["10.0.0.0/8", "192.168.0.0/16"]
}
Порядка нет, по индексу не обратишься. Используется с for_each,
там как раз нужны уникальные ключи без порядка.
map (ключ-значение, ключи строковые)
variable "tags" {type = map(string)
default = {Environment = "dev"
Owner = "platform"
}
}
Обращение по ключу: var.tags["Environment"] → "dev". Все значения
одного типа (string в этом примере).
Структурные типы
Structural, это «много разного, заранее известной формы».
object (поля разных типов)
variable "bucket_settings" { type = object({name = string
versioning = bool
retention_days = number
})
default = {name = "my-bucket"
versioning = true
retention_days = 90
}
}
Поля могут быть разных типов. Обращение через точку:
var.bucket_settings.name. Если в дефолте указать не все поля или
лишние, terraform упадёт на validate.
tuple (упорядоченная пара/тройка)
variable "name_and_size" {type = tuple([string, number])
default = ["primary", 100]
}
Tuple, это как list, но каждый элемент своего типа, фиксированной длины. На практике почти не нужен: для понятности обычно лучше object.
Чем list отличается от tuple
list(T), любое количество элементов одного типаT.tuple([T1, T2, T3]), ровно три элемента, каждый своего типа.
Если коллеги пишут одно и то же, это list. Если структура с несколькими полями, object предпочтительнее, чем tuple.
Чем map отличается от object
map(T), произвольное количество ключей, все значения типаT.object({...}), фиксированный набор ключей, каждое значение своего типа.
Tag set, это map(string). Конфиг ресурса, object({...}).
Простой эвристический способ: если ключи приходят снаружи (от
пользователя, не из кода). map; если ты сам пишешь имена ключей,
object.
any, отказ от типизации
variable "anything" {type = any
}
Terraform не проверит структуру вообще. Это лазейка для случаев,
когда тип меняется в runtime, и она почти всегда, анти-паттерн.
Если используешь any, в комментарии пиши, почему точно нельзя
было типизировать.
Optional поля в object
С Terraform 1.3+ поля object можно делать необязательными:
variable "logging" { type = object({enabled = bool
target_arn = optional(string)
prefix = optional(string, "logs/") # default
})
default = {enabled = true
}
}
optional(string), поле можно не указывать, значение будетnull.optional(string, "logs/"), значение по умолчанию.
Это спасает от длинных object-схем, где половина полей нужна редко.
Подводные камни
null≠ отсутствие поля. В object с обязательными полями нужно передать все, дажеnullявно.map(string)противobject({a=string})в variable. Приmap(string)ключи могут быть любые; приobject, только перечисленные.- Set теряет порядок. Если важен порядок (например, в outputs): переключайся на list.
- Числа в HCL, без точности. terraform внутри хранит число как
cty.Number(BigFloat). На больших значениях возможно округление, но обычно это не проблема. - Boolean из строки.
"true"(строка) иtrue(bool): разные типы. terraform не приводит автоматически, будет ошибка типа.