Тестируемые модули и проверки: validation, precondition, postcondition

Рейтинг: 45.3% · 9 голосов
Подробный курс по Terraform и OpenTofu на примерах Yandex Cloud: ресурсы и провайдеры, состояние и бэкенды, модули, циклы и условия HCL, секреты и Lockbox, тестирование, CI/CD и командная работа. С актуализацией на 2026 (Terraform 1.3-1.10, OpenTofu).
Ответить
Аватара пользователя
Vlad_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Тестируемые модули и проверки: validation, precondition, postcondition

Сообщение Vlad_DevOps »

Оглавление курса (47)
  1. Что такое инфраструктура как код и зачем она нужна
  2. Категории инструментов IaC: чем отличаются Ansible, Packer, Kubernetes и Terraform
  3. Чем Terraform отличается от Ansible, Pulumi и CloudFormation
  4. Как работает Terraform под капотом и кто такой OpenTofu
  5. Установка Terraform и OpenTofu, подготовка Yandex Cloud
  6. Первый ресурс: разворачиваем виртуальную машину в Yandex Cloud
  7. Веб-сервер на ВМ: метаданные, user-data и группы безопасности
  8. Переменные ввода и выходные значения в Terraform
  9. Кластер ВМ: группа экземпляров Yandex Compute Instance Group
  10. Балансировщик нагрузки: Application Load Balancer в Yandex Cloud
  11. Состояние Terraform: что такое tfstate и чем опасен локальный файл
  12. Удалённое хранилище состояния: бэкенд на Yandex Object Storage
  13. Изоляция состояния: рабочие области против раскладки по папкам
  14. Источники данных и terraform_remote_state: связываем компоненты
  15. Модули Terraform: переиспользуем инфраструктуру
  16. Входные, локальные и выходные переменные модуля
  17. Версионирование модулей и подводные камни
  18. Циклы в Terraform: параметр count
  19. Циклы в Terraform: for_each по множествам и картам
  20. Выражения for и строковые директивы
  21. Условная логика: count-трюк, for_each и директива if
  22. Встроенные функции, типы и выражения HCL
  23. Развёртывание без простоя: create_before_destroy
  24. Подводные камни Terraform и рефакторинг с блоком moved
  25. Управление секретами: основы и типы
  26. Инструменты управления секретами: Yandex Lockbox, Vault, SOPS
  27. Аутентификация провайдера и секреты: сервисные аккаунты и OIDC
  28. Секреты в ресурсах и состоянии, ephemeral-значения
  29. Провайдеры Terraform: установка, версии, required_providers
  30. Несколько копий одного провайдера: alias и мультизона
  31. Несколько провайдеров и аккаунтов: configuration_aliases
  32. Terraform, Docker и Kubernetes: Managed Kubernetes в Yandex Cloud
  33. Код Terraform промышленного уровня: почему это долго
  34. Мелкие компонуемые модули вместо монолита
  35. Тестируемые модули и проверки: validation, precondition, postcondition (вы здесь)
  36. Версионирование, публикация модулей и Terragrunt
  37. Ручное тестирование Terraform и очистка ресурсов
  38. Автоматические тесты на Terratest: модульные тесты
  39. Интеграционные и сквозные тесты, пирамида тестирования
  40. Нативный terraform test и статический анализ
  41. Внедрение Terraform в команде: процессы и культура
  42. Конвейер развёртывания прикладного кода
  43. Конвейер инфраструктурного кода и золотое правило Terraform
  44. Стиль, ревью и CI/CD-инструменты: Atlantis, TACOS, policy as code
  45. Что нового в Terraform 1.3-1.10: моды, импорт, провайдер-функции, Stacks
  46. OpenTofu в 2026: чем живёт форк и как мигрировать
  47. Сертификация HashiCorp Terraform Associate и карьера
Представь ситуацию. Ты написал аккуратный модуль для виртуалки в Yandex Cloud, выложил его в общий репозиторий, гордишься собой. А через неделю коллега передаёт в переменную zone значение "ru-central1-zzz", в cores пишет 3 (а Yandex Cloud такое число ядер не переваривает), и в итоге terraform apply падает где-то в глубине API с сообщением вроде "rpc error: code = InvalidArgument". Коллега идёт к тебе. Ты тратишь полчаса, чтобы понять, что ошибка-то банальная: опечатка во вводе.

Хороший модуль ловит мусор на входе сам и говорит человеку понятным языком, что именно не так. В этом уроке разберём три уровня защиты, которые Terraform даёт из коробки: validation на переменных, precondition/postcondition внутри ресурсов и output, и check-блоки для проверок после применения. Это и есть та самая разница между "скриптом, который у меня работает" и переиспользуемым модулем, который не стыдно отдать другим. Инфраструктура как код (iac) - это не только про создание ресурсов, но и про то, чтобы код сопротивлялся неверному использованию.

Сразу про инструменты и версии, чтобы было понятно про что речь. precondition и postcondition появились в Terraform 1.2, check-блоки - в Terraform 1.5. К 2026 году всё это давно стабильно и есть как в свежем Terraform, так и в OpenTofu (это форк, который понимает ту же самую конфигурацию). Так что примеры ниже работают и там, и там.

Уровень 1. Валидация входных переменных

Самая первая линия обороны - блок validation внутри variable. Он проверяет значение ещё на этапе terraform plan, до того как Terraform полезет читать data-источники или создавать что-либо. Если условие не выполнено - сразу ошибка с твоим текстом, апрель ресурсов даже не начнётся.

Блок состоит из двух частей: condition (логическое выражение, которое должно быть true) и error_message (что сказать человеку, если false). Ссылаться внутри condition можно на сам var.имя_переменной.

Код: Выделить всё

variable "zone" {
  type        = string
  description = "Зона доступности Yandex Cloud"

  validation {
    condition     = contains(["ru-central1-a", "ru-central1-b", "ru-central1-d"], var.zone)
    error_message = "Зона должна быть одной из: ru-central1-a, ru-central1-b, ru-central1-d. Получено: ${var.zone}."
  }
}

variable "cores" {
  type        = number
  description = "Количество vCPU"

  validation {
    condition     = var.cores >= 2 && var.cores <= 32
    error_message = "cores должно быть в диапазоне 2..32. В Yandex Cloud нечётные значения и единица недопустимы для платформы standard-v3."
  }
}
Пара важных приёмов. Внутри condition можно делать несколько validation-блоков на одну переменную - каждый проверяет свой аспект, и Terraform покажет сразу все, что не прошли. Для проверок формата отлично заходит can() в связке с regex(): функция can ловит ошибку выражения и возвращает false вместо падения.

Код: Выделить всё

variable "vm_name" {
  type = string

  validation {
    condition     = can(regex("^[a-z][a-z0-9-]{1,61}$", var.vm_name))
    error_message = "Имя ВМ: строчные латинские буквы, цифры и дефис, начинается с буквы, до 63 символов."
  }
}
И ещё одно, что важно знать в 2026 году: раньше внутри condition можно было ссылаться только на саму переменную. В свежих версиях Terraform (с 1.9) и в OpenTofu это ограничение сняли - теперь в условии можно использовать и другие переменные, и local-значения. Это удобно для перекрёстных проверок, например когда max_size обязан быть не меньше min_size.

Изображение

Уровень 2. precondition и postcondition

Валидация переменной хороша, но она знает только про вход. А что если проверка зависит от данных, которых на момент разбора переменных ещё нет? Например, ты берёшь образ через data-источник и хочешь убедиться, что у него нужная архитектура. Или хочешь гарантировать, что выбранная зона реально входит в список зон, которые отдаёт API. Тут вступают lifecycle-проверки.

precondition проверяется до создания/обновления ресурса (на этапе plan, если данные известны). Смысл - "не начинай, если предпосылки неверны". postcondition проверяется после того, как ресурс создан или обновлён, и смотрит уже на фактический результат через self. Смысл - "то, что получилось, соответствует ожиданиям?". Оба живут внутри блока lifecycle.

Код: Выделить всё

data "yandex_compute_image" "ubuntu" {
  family = "ubuntu-2204-lts"
}

resource "yandex_compute_instance" "vm" {
  name        = var.vm_name
  zone        = var.zone
  platform_id = "standard-v3"

  resources {
    cores  = var.cores
    memory = var.memory
  }

  boot_disk {
    initialize_params {
      image_id = data.yandex_compute_image.ubuntu.id
    }
  }

  network_interface {
    subnet_id = var.subnet_id
    nat       = true
  }

  lifecycle {
    precondition {
      condition     = data.yandex_compute_image.ubuntu.os_type == "LINUX"
      error_message = "Ожидался Linux-образ, а пришёл ${data.yandex_compute_image.ubuntu.os_type}. Проверь family."
    }

    postcondition {
      condition     = self.network_interface[0].nat_ip_address != ""
      error_message = "ВМ создана без публичного IP, хотя nat = true. Что-то не так с подсетью или квотой."
    }
  }
}
Обрати внимание на self в postcondition - это ссылка на сам только что созданный ресурс. В precondition self использовать нельзя (ресурса ещё нет), там опираешься на переменные и data-источники.

precondition и postcondition можно ставить и в блоке output. Это удобно, когда модуль отдаёт наружу значение и ты хочешь гарантировать его корректность - например, что список зон не пустой:

Код: Выделить всё

output "zones" {
  value = data.yandex_compute_zones.available.zones

  precondition {
    condition     = length(data.yandex_compute_zones.available.zones) > 0
    error_message = "API не вернул ни одной зоны доступности. Проверь folder_id и права сервисного аккаунта."
  }
}
Уровень 3. check-блоки для post-apply ассертов

precondition и postcondition - жёсткие: упали - и весь apply красный. Иногда это перебор. Допустим, ты хочешь регулярно проверять, что HTTP-эндпойнт жив, но не хочешь, чтобы временная недоступность сайта рушила всё развёртывание. Для таких "мягких" проверок в Terraform 1.5 завезли check-блоки.

Ключевое отличие: проваленный assert внутри check выдаёт предупреждение (warning), а не ошибку. apply при этом завершается успешно. Это идеально для мониторинговых ассертов и для проверок, которые зависят от внешнего мира.

Код: Выделить всё

check "health" {
  data "http" "endpoint" {
    url = "https://${yandex_compute_instance.vm.network_interface[0].nat_ip_address}/health"
  }

  assert {
    condition     = data.http.endpoint.status_code == 200
    error_message = "Эндпойнт /health вернул ${data.http.endpoint.status_code}, ожидался 200."
  }
}
Внутри check можно объявлять scoped data-источник (он виден только внутри блока) и сколько угодно assert-блоков. check выполняется самым последним - уже после всех postcondition. Запомни простое правило выбора: validation - для входа, precondition/postcondition - для жёстких инвариантов, check - для мягких post-apply проверок, которые не должны валить пайплайн.

Типичные грабли
  • error_message без контекста. "Неверное значение" - бесполезно. Всегда вставляй в текст само значение и что от него хотели: человек должен понять причину, не открывая код модуля.
  • Тяжёлая логика в condition. Условие должно быть булевым выражением. Если оно громоздкое - вынеси промежуточные вычисления в locals и сошлись на них.
  • Путаница self. self доступен только в postcondition и только указывает на текущий ресурс. В precondition его нет.
  • Ожидание, что check остановит apply. Не остановит. Если тебе нужна жёсткая гарантия - это postcondition, а не check.
  • regex без can. Голый regex() на неподходящей строке сам бросит ошибку и сломает план. Оборачивай в can(), чтобы получить аккуратный false.
Мини-лаба

Возьми любой свой модуль ВМ для Yandex Cloud и руками добавь в него:
  • validation на переменную zone (список допустимых зон) и на vm_name (regex формата);
  • precondition в yandex_compute_instance, который проверяет os_type образа из data-источника;
  • postcondition с self, проверяющий, что у ВМ появился публичный IP;
  • output с precondition на непустой список зон;
  • check-блок с одним assert (можно проверить, например, что cores * memory укладывается в твой бюджет).
Затем намеренно сломай ввод (например, zone = "ru-central1-z") и запусти terraform plan. Убедись, что сообщение об ошибке понятно тебе самому через неделю.

Контрольные вопросы
  • На каком этапе срабатывает validation переменной и почему это раньше, чем precondition?
  • Чем принципиально отличается поведение проваленного postcondition от проваленного assert в check-блоке?
  • Где можно использовать ссылку self, а где нельзя, и почему?
  • В какой версии Terraform появились precondition/postcondition, а в какой - check-блоки?
Что запомнить

Три уровня проверок - это слои защиты модуля: validation стережёт вход, precondition/postcondition держат инварианты ресурсов и output, check добавляет мягкие ассерты после apply. Жёсткие проверки валят план или apply и тем самым берегут тебя от кривой инфраструктуры; check лишь предупреждает. Самое ценное в любой из них - не само условие, а понятное error_message: именно оно превращает чужой модуль в инструмент, которым приятно пользоваться. Тестируемый модуль - это модуль, который объясняет, почему он отказался работать.
👍4 ❤️2 🔥 😄 🤔1
Аватара пользователя
caffeinesamurai
Сообщения: 1
Зарегистрирован: 21 май 2026, 15:07

Re: Тестируемые модули и проверки: validation, precondition, postcondition

Сообщение caffeinesamurai »

Спасибо, наконец дошло в чем разница между postcondition и check. Раньше думал что check тоже все ломает, а оно просто варнинг кидает. Пойду переделаю свой модуль где зря понаставил postcondition.
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
llama2010
Сообщения: 1
Зарегистрирован: 22 май 2026, 02:14

Re: Тестируемые модули и проверки: validation, precondition, postcondition

Сообщение llama2010 »

А подскажите, можно ли в validation сослаться на другую переменную? У меня min и max в разных переменных, хочу проверить что max не меньше min. Или это только через precondition?
👍3 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Мелкие компонуемые модули вместо монолита
Следующая глава →
Версионирование, публикация модулей и Terragrunt

Все главы курса «Terraform: инфраструктура как код на Yandex Cloud»

Поделиться темой: ✈ Telegram VK
Похожие запросы: как вынести terraform в модуль и переиспользовать инфраструктурувходные и выходные переменные модуля terraform как передатькак создать роль ansible с нуля и структура каталогов

Вернуться в «Terraform: инфраструктура как код на Yandex Cloud»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость