ADG Оставить заявку
Блог DevOps 4 мин чтения

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, а не накатить обновление под давлением и разгребать потом.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.