Terraform 0.13 GA: for_each для модулей и переезд с 0.12
Обновляем IaC-репозиторий с Terraform 0.12 на 0.13: terraform 0.13upgrade, for_each для модулей и автоматическая установка провайдеров через required_providers.
Terraform 0.13 GA: поддержка count/for_each для вызовов модулей и автоматическая установка провайдеров через блок required_providers
Terraform 0.13 вышел в GA в августе, мы смотрели на него осторожно - всё-таки minor release с несколькими ломающими изменениями в синтаксисе провайдеров. На прошлой неделе наконец перевели первый production-репозиторий. Рассказываем что реально меняется и где были неожиданности.
terraform 0.13upgrade: в чём помогает и где недостаточно
HashiCorp добавили команду terraform 0.13upgrade, которая автоматически конвертирует конфиги. Основное что она делает - переписывает блоки provider в новый синтаксис required_providers:
# Было (0.12):
provider "vsphere" {
version = "~> 1.18"
...
}
# Стало (0.13):
terraform {
required_providers {
vsphere = {
source = "hashicorp/vsphere"
version = "~> 1.18"
}
}
}
Команда прошлась по нашему репозиторию за секунды, сгенерировала корректные required_providers блоки для всех провайдеров. Хорошая новость: для HashiCorp-провайдеров она сама проставляет source = "hashicorp/<name>". Плохая: сторонние провайдеры она не знает - там нужен source вида <hostname>/<namespace>/<name>, и его надо проставить вручную. У нас был один самописный провайдер для внутреннего железа - пришлось вписать source руками.
После конвертации запускаем terraform init - он скачивает провайдеры уже с новым механизмом через реестр, генерирует .terraform.lock.hcl. Вот этот файл надо закоммитить: он фиксирует хэши провайдеров и платформу. Мы сначала добавили его в .gitignore по старой привычке, потом поняли ошибку - 0.13 ожидает что lock-файл в репозитории.
for_each для модулей: то чего ждали
Ещё в июле мы писали про ожидание for_each для модулей. Теперь это в GA, и мы сразу переписали сетевые конфиги.
Была такая картина - конфигурация VLAN-сегментов для одного клиента:
module "segment_mgmt" {
source = "gitlab.internal/infra/vlan-segment/adg"
version = "~> 1.0"
name = "mgmt"
vlan_id = 100
mtu = 1500
}
module "segment_prod" {
source = "gitlab.internal/infra/vlan-segment/adg"
version = "~> 1.0"
name = "prod"
vlan_id = 101
mtu = 9000
}
module "segment_dmz" {
source = "gitlab.internal/infra/vlan-segment/adg"
version = "~> 1.0"
name = "dmz"
vlan_id = 200
mtu = 1500
}
И так ещё девятнадцать блоков. Файл занимал больше двухсот строк, из которых полезного кода - декларация модуля и переменные. Остальное - дублирование source и version.
После переписывания:
locals {
vlan_segments = {
mgmt = { vlan_id = 100, mtu = 1500 }
prod = { vlan_id = 101, mtu = 9000 }
dmz = { vlan_id = 200, mtu = 1500 }
}
}
module "segments" {
for_each = local.vlan_segments
source = "gitlab.internal/infra/vlan-segment/adg"
version = "~> 1.0"
name = each.key
vlan_id = each.value.vlan_id
mtu = each.value.mtu
}
Файл сократился примерно втрое. locals с картой сегментов читается как таблица - новый сегмент добавляется одной строкой вместо блока из восьми.
Есть важный нюанс с адресацией ресурсов: раньше был module.segment_mgmt, теперь module.segments["mgmt"]. При первом apply Terraform видит это как удаление старых ресурсов и создание новых. Если инфраструктура реальная и продовая - надо делать terraform state mv для каждого ресурса перед apply, иначе получите destroy/create вместо in-place переименования. Мы прошли через это на staging, руками перетащили state для двадцати двух модульных вызовов. Муторно, но один раз.
required_providers и автоматическая установка
Второе большое изменение - провайдеры больше не нужно устанавливать вручную через terraform init -plugin-dir. В 0.13 блок required_providers с указанием source позволяет terraform init скачать провайдер напрямую из реестра. Включая сторонние реестры - не только registry.terraform.io.
Для нас это закрыло боль с CI: раньше в pipeline надо было либо бандлить провайдеры в образ, либо прокидывать кэш директории со скачанными бинарями. Сейчас terraform init сам разбирается с зависимостями, lock-файл гарантирует воспроизводимость. Pipeline стал на несколько шагов проще.
Правда, если у вас air-gapped среда без доступа к registry.terraform.io - нужен внутренний зеркальный реестр. Это отдельная история, нас эта задача не касается, но видели что такой сценарий официально поддерживается через network_mirror в конфиге Terraform CLI.
Итог первого переезда
Репозиторий одного клиента переведён, pipeline зелёный, сетевые модули переписаны через for_each. terraform plan показывает No changes после state mv - значит реальная инфраструктура не затронута, только адресация в state изменилась.
Осталось ещё два репозитория. Процесс уже понятен: terraform 0.13upgrade, проверить сторонние провайдеры, добавить lock-файл в git, переписать повторяющиеся модули через for_each, сделать state mv если нужно. Часа три на репозиторий среднего размера, включая проверку плана.
Managed-сопровождение в этом смысле даёт одно практическое преимущество: миграцию инструментария делаем последовательно, не второпях. Есть время правильно пройти через state mv, а не накатить обновление под давлением и разгребать потом.