Terraform: инфраструктура как код на практике

Курс знакомит с Terraform и подходом Infrastructure as Code: от базового синтаксиса и работы с состоянием до модулей, командной работы и автоматизации. В результате вы научитесь проектировать, разворачивать и сопровождать инфраструктуру безопасно и воспроизводимо.

1. Введение в IaC и Terraform: установка и первый проект

Введение в IaC и Terraform: установка и первый проект

Зачем нужна инфраструктура как код

Инфраструктура как код (IaC, Infrastructure as Code) — это подход, при котором инфраструктура (сети, виртуальные машины, базы данных, DNS, политики доступа и т. п.) описывается в виде файлов с кодом и управляется через стандартные инженерные практики:

  • хранение в системе контроля версий
  • воспроизводимые изменения через проверки и ревью
  • возможность повторить окружение (dev/stage/prod) по одному описанию
  • предсказуемость за счёт планирования изменений
  • Практические проблемы, которые IaC решает:

  • ручные изменения в консоли облака сложно повторить и контролировать
  • знания об инфраструктуре часто «живут в голове» у отдельных людей
  • внесение изменений без истории и проверки повышает риск простоев
  • Что такое Terraform

    Terraform — инструмент для описания и управления инфраструктурой через декларативные конфигурации. Вы описываете желаемое состояние, а Terraform вычисляет, какие действия нужны, чтобы привести реальность к этому состоянию.

    Ключевые понятия (будем использовать их весь курс):

  • Провайдер — плагин, который умеет работать с конкретной платформой (AWS, Azure, Google Cloud, Kubernetes и т. п.).
  • Ресурс — управляемый объект (например, сеть, виртуальная машина, запись DNS).
  • Состояние (state) — файл/хранилище, в котором Terraform хранит соответствие между конфигурацией и реально созданными объектами.
  • План (plan) — предварительный расчёт изменений (что будет создано, изменено, удалено).
  • Применение (apply) — выполнение плана.
  • Официальная документация:

  • Terraform
  • Как работает Terraform: жизненный цикл проекта

    Типичный цикл работы выглядит так:

  • Пишем или меняем конфигурацию (.tf файлы)
  • Инициализируем проект (terraform init)
  • Просматриваем план (terraform plan)
  • Применяем изменения (terraform apply)
  • При необходимости удаляем созданное (terraform destroy)
  • !Диаграмма показывает типичный цикл работы Terraform и связь со state

    Почему важен state:

  • Terraform сравнивает конфигурацию с state и понимает, какие ресурсы он уже создал.
  • По state Terraform определяет, что нужно изменить, а что оставить как есть.
  • Установка Terraform

    Перед установкой выберите подходящий способ. Рекомендуемый вариант для обучения и работы на компьютере разработчика — менеджер пакетов.

    Установка на macOS

  • Установите Terraform через Homebrew:
  • Проверьте версию:
  • Установка на Windows

  • Установите Terraform через Chocolatey:
  • Проверьте версию:
  • Установка на Linux

    На Linux способ установки зависит от дистрибутива. Универсальный и всегда актуальный вариант — следовать официальным инструкциям:

  • Install Terraform
  • После установки проверьте:

    Первый проект без облака: создаём файл как ресурс

    Чтобы сосредоточиться на базовых принципах Terraform, начнём с проекта, который не требует облачных аккаунтов. Мы используем провайдер local и ресурс local_file, который создаёт файл на вашем компьютере.

    Структура проекта

    Создайте пустую папку проекта, например terraform-first-project, и внутри создайте файл main.tf.

    Рекомендуемая минимальная структура:

  • main.tf — основной файл конфигурации
  • variables.tf — переменные (добавим позже)
  • outputs.tf — выходные значения (добавим позже)
  • README.md — описание проекта (в реальной работе)
  • Конфигурация main.tf

    Добавьте в main.tf:

    Что здесь происходит:

  • Блок terraform описывает требования к провайдерам.
  • required_providers говорит Terraform, откуда скачать провайдер и какую версию использовать.
  • provider "local" {} включает провайдер (здесь без настроек).
  • resource "local_file" "hello" описывает ресурс типа local_file с именем hello.
  • - filename — куда записать файл. - content — содержимое файла.

    Инициализация

    В папке проекта выполните:

    Результат:

  • Terraform скачает провайдер local.
  • В каталоге появятся служебные данные (например, .terraform/).
  • Планирование изменений

    Выполните:

    Terraform покажет, что планирует создать 1 ресурс (файл). На этом шаге ничего ещё не меняется — это только расчёт.

    Применение

    Примените изменения:

    Terraform попросит подтверждение. После применения в папке проекта появится hello.txt.

    Проверка результата

    Откройте файл hello.txt и убедитесь, что содержимое соответствует описанию.

    Удаление созданного

    Чтобы удалить ресурс, выполните:

    Это удалит файл, потому что Terraform управляет им как ресурсом.

    Переменные и outputs: делаем проект удобнее

    Сейчас значения filename и content зашиты в код. В реальных проектах так делают редко — вместо этого используют переменные.

    variables.tf

    Создайте variables.tf:

    Объяснение:

  • variable объявляет переменную.
  • type = string означает, что значение должно быть строкой.
  • default задаёт значение по умолчанию.
  • Обновляем main.tf

    Измените ресурс так, чтобы он использовал переменные:

    var.filename и var.message — обращение к переменным.

    outputs.tf

    Создайте outputs.tf:

    Объяснение:

  • output публикует полезный результат выполнения.
  • local_file.hello.filename — обращение к атрибуту ресурса.
  • Примените изменения:

    После применения Terraform выведет значение created_file.

    Передача переменных при запуске

    Вы можете переопределять переменные через флаги:

    Форматирование и базовая проверка конфигурации

    Terraform поддерживает команды, которые полезно применять постоянно:

  • Форматирование:
  • Базовая проверка структуры конфигурации:
  • validate не гарантирует, что провайдер примет все значения, но помогает отловить синтаксические ошибки и некоторые типовые проблемы.

    Что важно про state с первого дня

    Даже в примере с файлом Terraform создаст state (по умолчанию локально). Обычно это файл terraform.tfstate.

    Правила безопасности и практики:

  • terraform.tfstate может содержать чувствительные данные (например, пароли или ключи), если они участвуют в ресурсах.
  • В командной работе state обычно хранят удалённо с блокировками (эту тему мы разберём в следующих статьях).
  • Для начала запомните практическое правило:

  • не редактируйте terraform.tfstate вручную
  • Итоги

    В этой статье вы:

  • разобрались, что такое IaC и почему Terraform удобен для управления инфраструктурой
  • установили Terraform и проверили работоспособность
  • создали первый проект без облака и прошли цикл initplanapplydestroy
  • вынесли параметры в variable и научились публиковать результат через output
  • узнали, зачем нужен state и почему с ним нужно обращаться аккуратно
  • Дальше мы будем усложнять проекты: подключать облачные провайдеры, изучать устройство state глубже, добавлять модули, удалённые бекенды, блокировки и командные практики.

    2. Язык HCL: ресурсы, переменные, outputs и выражения

    Язык HCL: ресурсы, переменные, outputs и выражения

    Зачем разбираться в HCL

    В прошлой статье вы прошли базовый цикл Terraform initplanapplydestroy и создали первый ресурс через провайдер local. Дальше скорость и качество работы будут зависеть от того, насколько уверенно вы читаете и пишете конфигурации на HCL.

    HCL (HashiCorp Configuration Language) — язык конфигураций Terraform. Он создан так, чтобы:

  • быть читаемым как человеком, так и инструментами
  • поддерживать выражения (вычисления), типы данных и функции
  • помогать описывать инфраструктуру декларативно
  • Официальные источники:

  • Terraform Language
  • Configuration Language (HCL)
  • Базовая форма конфигурации: блоки, аргументы и выражения

    Terraform-конфигурация состоит из блоков. Блок имеет:

  • тип блока (например, resource, variable, output)
  • метки (labels) у некоторых типов блоков (например, у resource их две)
  • тело блока с аргументами
  • Типовой пример:

    Здесь:

  • resource — тип блока
  • local_file — первая метка (тип ресурса)
  • hello — вторая метка (локальное имя ресурса в конфигурации)
  • filename и content — аргументы
  • значения справа — выражения (expression). Даже строка в кавычках — это выражение, просто самое простое.
  • !Визуально показывает, из чего состоит HCL-блок и как читать конфигурацию

    Ресурсы: адресация и ссылки на атрибуты

    Что такое ресурс в HCL

    Ресурс — это то, чем Terraform управляет через провайдер. В HCL ресурс задаётся блоком resource.

    Формат:

    Адрес ресурса

    У любого ресурса есть адрес вида:

  • resource_type.resource_name
  • Например:

  • local_file.hello
  • Этот адрес используется для ссылок на атрибуты ресурса.

    Ссылки на атрибуты

    Многие блоки (например, output) могут читать атрибуты ресурса:

    Важно:

  • local_file.hello.filename — это выражение, которое Terraform вычисляет после того, как станет понятно значение filename у ресурса
  • ссылки на другие объекты формируют зависимости, поэтому Terraform понимает порядок создания
  • Переменные: типы, значения и валидация

    Переменные позволяют сделать конфигурации переиспользуемыми и удобными. Переменные объявляются блоком variable.

    Официальная документация:

  • Input Variables
  • Объявление переменной

    Как это читать:

  • type задаёт ожидаемый тип значения
  • default делает переменную необязательной
  • description полезен для команды и документации
  • Использование переменной

    Использование всегда через var.<name>:

    Базовые типы и составные типы

    Чаще всего вам понадобятся:

  • string — строка
  • number — число
  • booltrue или false
  • list(<type>) — список значений одного типа
  • map(<type>) — словарь строковых ключей в значения одного типа
  • object({ ... }) — структурированный объект с полями
  • Пример переменной-объекта:

    Практический смысл типов:

  • Terraform начинает ловить ошибки раньше, ещё на terraform validate и terraform plan
  • становится проще читать конфигурацию: понятно, что ожидается на вход
  • Валидация переменных

    Можно добавить правила, которые проверяются при запуске:

    Здесь:

  • endswith(...) — встроенная функция Terraform
  • при нарушении условия Terraform остановится с понятным сообщением
  • Документация:

  • Custom Conditions (Validation)
  • Functions
  • Как задаются значения переменных

    На практике значения приходят из разных источников (приоритеты и детали разберём позже), но уже сейчас полезно знать основные способы:

  • через default в объявлении
  • через флаги -var при запуске
  • через файлы *.tfvars
  • через переменные окружения TF_VAR_<name>
  • Outputs: как отдавать результат наружу

    Outputs публикуют полезные значения после apply: идентификаторы, адреса, имена файлов, вычисленные строки.

    Документация:

  • Outputs
  • Пример:

    Когда outputs особенно полезны

  • для скриптов и CI/CD: забрать результат выполнения (например, IP-адрес)
  • для модулей: передать значения из одного слоя конфигурации в другой
  • Чувствительные outputs

    Если output содержит секрет, его можно пометить как чувствительный:

    Это не «шифрование» и не защита state, но Terraform будет аккуратнее показывать значение в выводе.

    Locals: переменные для вычислений внутри модуля

    Если input variables — это входные параметры, то locals — это удобный способ объявить промежуточные вычисления, чтобы не дублировать выражения.

    Документация:

  • Local Values
  • Пример:

    Также часто используют функции для сборки строк:

    Условное выражение

    Форма:

  • <условие> ? <значение_если_true> : <значение_если_false>
  • Пример:

    Как это применять:

  • переключать имена ресурсов, теги, размеры, включать или выключать части конфигурации (позже — через count и for_each)
  • Полезные функции

    Terraform включает много функций. Несколько, которые пригодятся почти сразу:

  • length(x) — длина списка/строки/карты
  • lower(x) и upper(x) — регистр
  • replace(str, from, to) — замена
  • join(sep, list) — склейка списка в строку
  • split(sep, str) — разбиение строки
  • jsonencode(x) — сериализация в JSON
  • Пример с JSON (часто используют для политик, конфигов и шаблонов):

    Коллекции и циклы в выражениях: for-выражения

    Когда входные данные становятся списками и картами, важно уметь трансформировать их выражениями.

    Документация:

  • For Expressions
  • Преобразование списка

    Здесь:

  • for n in var.names перебирает элементы
  • upper(n) применяется к каждому элементу
  • результат — новый список
  • Преобразование в map

    Смысл:

  • ключом карты станет строка ("80", "443")
  • значением — число (80, 443)
  • Фильтрация (if в for-выражении)

    Terraform оставит только те элементы, для которых условие истинно.

    Terraform console: быстрые эксперименты с выражениями

    Когда вы не уверены, что вернёт выражение, удобно использовать интерактивную консоль:

    Внутри можно вычислять выражения, например:

    Ключевые идеи:

  • for_each создаёт по одному ресурсу на элемент входной коллекции
  • each.key и each.value доступны внутри ресурса при использовании for_each
  • locals помогают не дублировать выражения
  • Документация:

  • The for_each Meta-Argument
  • outputs.tf

    Что здесь важно:

  • local_file.generated при for_each становится набором ресурсов
  • for-выражение собирает атрибут filename в список
  • Связь с state: почему выражения работают предсказуемо

    Terraform вычисляет многие выражения во время plan и уточняет значения во время apply. При этом Terraform опирается на:

  • вашу конфигурацию
  • текущее состояние (state)
  • ответы провайдеров
  • Практическое следствие:

  • ссылки на атрибуты ресурсов делают зависимости явными
  • Terraform может корректно упорядочить создание и изменение ресурсов
  • Итоги

    В этой статье вы:

  • разобрали, из чего состоит HCL-конфигурация: блоки, аргументы, выражения
  • научились адресовать ресурсы и ссылаться на их атрибуты
  • углубили работу с variable, типами, валидацией и источниками значений
  • освоили output и locals
  • познакомились с выражениями: интерполяция, условия, функции, for-выражения
  • увидели практический пример, который создаёт несколько ресурсов через for_each
  • Дальше эти знания станут основой для более реалистичных проектов: работы с удалённым state, модулями, окружениями и облачными провайдерами.

    3. Провайдеры и ресурсы: жизненный цикл, зависимости, data sources

    Провайдеры и ресурсы: жизненный цикл, зависимости, data sources

    Как эта тема связана с предыдущими статьями

    В первых двух статьях вы:

  • прошли базовый цикл initplanapplydestroy на примере local_file
  • разобрали HCL-блоки, переменные, locals, outputs и выражения
  • Теперь пора разобраться, почему Terraform вообще может управлять разными платформами (облаками, Kubernetes, GitHub, DNS) и как он упорядочивает операции так, чтобы инфраструктура создавалась корректно. Для этого нужны три ключевые темы:

  • провайдеры: плагины, которые общаются с платформами
  • ресурсы: управляемые объекты (создать/изменить/удалить)
  • data sources: чтение уже существующих объектов без управления ими
  • Официальная документация:

  • Providers
  • Resources
  • Data Sources
  • Провайдер: что это и из чего состоит подключение

    Провайдер в Terraform — это плагин, который знает API конкретной платформы и умеет выполнять CRUD-операции над объектами этой платформы.

    Чтобы провайдер работал, обычно нужны:

  • источник провайдера (где его взять)
  • версия провайдера (какую совместимую версию использовать)
  • конфигурация провайдера (как подключаться: регион, токен, эндпоинт)
  • Требования к провайдеру: required_providers

    Вы уже использовали это в первом проекте:

    Смысл:

  • source указывает, откуда скачивать плагин провайдера
  • version фиксирует совместимый диапазон версий, чтобы команда получала предсказуемое поведение
  • Документация:

  • Provider Requirements
  • Конфигурация провайдера: блок provider

    Блок provider задаёт параметры подключения:

    У local почти нет настроек, но у облачных провайдеров они обычно есть (регион, креды и т. п.).

    Документация:

  • Provider Configuration
  • Несколько конфигураций одного провайдера: alias

    Иногда нужно работать с одной платформой, но в разных регионах или аккаунтах. Тогда используют alias:

    Ключевая идея:

  • ресурс по умолчанию использует провайдер без alias
  • атрибут provider = aws.us явно привязывает ресурс к альтернативной конфигурации
  • Ресурс: что именно Terraform «управляет»

    Ресурс — это объект, жизненным циклом которого Terraform управляет через провайдера: создаёт, изменяет, удаляет.

    Форма записи:

    Где:

  • <TYPE> — тип ресурса, который определён провайдером (например, local_file, aws_instance)
  • <NAME> — локальное имя в конфигурации, чтобы на ресурс можно было ссылаться
  • Адрес ресурса:

  • local_file.hello
  • Ссылки на атрибуты ресурса (формируют зависимости):

  • local_file.hello.filename
  • Документация:

  • Resource Syntax and Behavior
  • Жизненный цикл ресурса: от конфигурации до реальных изменений

    Terraform работает не «построчно», а через сравнение:

  • конфигурация (ваши .tf файлы)
  • state (что Terraform считает созданным и как это связано с реальностью)
  • ответы провайдера (что реально существует и какие атрибуты получились)
  • В упрощённом виде ресурс проходит состояния:

  • создание
  • обновление
  • замена (удаление и создание заново)
  • удаление
  • !Схема показывает, как plan/apply связаны с провайдером и state

    Что происходит на plan

    На terraform plan Terraform:

  • анализирует конфигурацию
  • строит граф зависимостей
  • сравнивает желаемое состояние с текущим состоянием (state)
  • формирует план действий: что создать, что изменить, что удалить
  • Важно понимать ограничение:

  • plan показывает намерение Terraform, но часть значений может быть неизвестна до apply (например, id ресурса, сгенерированный платформой)
  • Что происходит на apply

    На terraform apply Terraform:

  • выполняет план в правильном порядке с учётом зависимостей
  • вызывает провайдер, который делает реальные API-запросы
  • получает итоговые атрибуты объектов
  • обновляет state
  • Когда Terraform делает «замену» ресурса

    Иногда изменение аргумента нельзя применить «на месте». Тогда Terraform делает замену:

  • удаляет старый объект
  • создаёт новый объект
  • В плане это обычно видно как -/+ (destroy and then create) или как replace.

    Причины замены задаются провайдером (это часть схемы ресурса): например, у многих платформ смена некоторых параметров требует пересоздания объекта.

    Зависимости: как Terraform определяет порядок действий

    Terraform должен понимать, что создавать раньше, а что позже. Для этого он строит граф зависимостей.

    !Пример графа зависимостей: чтение данных → ресурсы → outputs

    Неявные зависимости через ссылки

    Самый правильный и распространённый способ задать зависимость — сослаться на атрибут другого объекта.

    Пример (на локальном провайдере это не всегда «жизненно нужно», но хорошо иллюстрирует механику):

    Что здесь демонстрируется:

  • resource создаёт объект
  • data читает объект и возвращает атрибуты (например, content)
  • derived зависит от data, потому что использует data.local_file.source.content
  • depends_on на data добавлен как подчёркивание порядка (в этом примере он избыточен, но полезен как приём для сложных случаев)
  • Команды:

    Частые ошибки и практические правила

  • Не подменяйте data ресурсами: если объект не должен управляться Terraform, читайте его через data.
  • Не злоупотребляйте depends_on: чаще всего зависимость должна выражаться ссылкой на атрибут.
  • Проверяйте план: plan должен быть привычкой перед любыми изменениями.
  • Осторожно с lifecycle.ignore_changes: он скрывает изменения, а значит может скрыть и проблемы.
  • Помните про state: ресурсы и data sources опираются на него при планировании, и его нельзя редактировать вручную.
  • Итоги

    В этой статье вы разобрали:

  • что такое провайдер, как он подключается и зачем фиксировать версии
  • что такое ресурс и как Terraform управляет его жизненным циклом через plan и apply
  • как Terraform строит порядок действий через зависимости, и когда нужен depends_on
  • как управлять поведением изменений через lifecycle
  • что такое data sources, чем они отличаются от ресурсов и как используются для чтения существующей инфраструктуры
  • Дальше эти знания станут базой для более «боевых» практик: удалённого state, командной работы, модулей и построения окружений.

    4. Состояние Terraform: backend, locking, workspace и миграции

    Состояние Terraform: backend, locking, workspace и миграции

    Зачем так много внимания state

    В прошлых статьях вы уже использовали terraform plan и terraform apply. Оба шага опираются на состояние Terraform (state) — источник правды о том, какие реальные объекты соответствуют ресурсам в вашей конфигурации.

    State нужен, чтобы Terraform мог:

  • понимать, какие ресурсы уже созданы и с какими идентификаторами у провайдера
  • строить корректный план изменений, сравнивая желаемое (конфигурация) и текущее (state)
  • вычислять зависимости между ресурсами и упорядочивать операции
  • По умолчанию состояние хранится локально в файле terraform.tfstate. Для одиночной практики это удобно, но для команды и CI/CD почти всегда нужен удалённый state, блокировки и понятная стратегия окружений.

    Официальные материалы:

  • Terraform State
  • Backends
  • !Как Terraform использует state и backend при plan/apply

    Что хранится в state и почему это важно

    State — это данные, в которых Terraform хранит соответствие между:

  • адресом ресурса в конфигурации (например, aws_instance.web)
  • реальным объектом у провайдера (например, EC2 instance с конкретным id)
  • набором атрибутов, которые Terraform знает о ресурсе
  • Практически важные последствия:

  • В state могут оказаться чувствительные данные (пароли, токены, приватные ключи), если они участвуют в ресурсах или outputs.
  • Потеря state может привести к ситуации, когда Terraform «забудет» о созданных объектах и попытается создать дубликаты, либо не сможет корректно обновлять существующие.
  • Одновременные изменения state из двух запусков Terraform могут повредить инфраструктуру и сам state.
  • Рекомендации по безопасной работе со state:

  • не редактируйте state вручную
  • храните state в удалённом backend с контролем доступа
  • включайте шифрование на стороне хранилища (если backend это поддерживает)
  • делайте резервные копии перед миграциями
  • Backend: где и как хранится состояние

    Backend — это механизм хранения state и связанных служебных данных.

    Локальный backend

    По умолчанию используется локальное хранение в текущем каталоге проекта.

    Плюсы:

  • просто начать
  • не нужны внешние сервисы
  • Минусы:

  • неудобно и опасно в команде
  • нет централизованной блокировки (зависит от сценария)
  • сложно подключать CI/CD
  • Удалённые backends

    Удалённый backend решает командные проблемы:

  • единый источник state для всех участников
  • (часто) поддержка блокировок
  • удобная интеграция с CI/CD
  • Terraform поддерживает разные backends. На практике часто встречаются:

  • s3 (AWS S3) + блокировки через DynamoDB
  • azurerm (Azure Storage)
  • gcs (Google Cloud Storage)
  • Terraform Cloud / Terraform Enterprise
  • Документация по типам backend находится в том же разделе:

  • Backends
  • Как задаётся backend в конфигурации

    Backend задаётся в блоке terraform.

    Пример (общая форма):

    Важно:

  • настройки backend не поддерживают обычные выражения Terraform так же свободно, как ресурсы; обычно их делают максимально простыми и статичными
  • после изменения настроек backend обычно нужно переинициализировать проект
  • Частичная конфигурация backend

    Часто чувствительные или окруженческие параметры backend не кладут в репозиторий, а передают при init.

    Пример (идея, без привязки к конкретному облаку):

    А конкретику передают через -backend-config:

    Locking: защита от параллельных изменений

    Locking (блокировка) — это механизм, который не позволяет двум процессам Terraform одновременно изменять один и тот же state.

    Почему это критично:

  • два параллельных apply могут изменить инфраструктуру в противоречащих направлениях
  • одновременная запись может повредить state
  • Как это выглядит в жизни:

  • первый запуск Terraform ставит lock
  • второй запуск получит ошибку и остановится (или будет ждать, в зависимости от backend и настроек)
  • Заметки:

  • поддержка locking зависит от выбранного backend
  • для s3 типичный подход — блокировки через DynamoDB (настройка dynamodb_table), детали зависят от backend s3
  • Полезная команда, когда lock «завис» (использовать осторожно и только понимая, что параллельного запуска нет):

    Документация:

  • State Locking
  • terraform force-unlock
  • Workspaces: несколько состояний в рамках одной конфигурации

    Workspace — это способ хранить несколько независимых состояний для одной и той же конфигурации.

    Идея простая:

  • одна папка с .tf кодом
  • несколько state, переключаемых по имени workspace
  • Команды:

    Когда workspaces полезны

    Workspaces удобны, когда:

  • вам нужны быстрые изолированные окружения для одного и того же кода (например, временные окружения для тестов)
  • вы понимаете, как backend раскладывает state по workspace и вам подходит эта модель
  • Документация:

  • Workspaces
  • Ограничения workspaces и практическое правило для окружений

    Главное ограничение: workspaces — это не полноценная стратегия продакшн-окружений сама по себе.

    Причины, почему для prod часто выбирают отдельный backend или отдельный ключ state (а иногда и отдельный аккаунт/подписку облака):

  • проще управлять доступами (разные права на dev и prod)
  • меньше риск случайно применить изменения «не в то окружение»
  • проще реализовать разные политики безопасности, аудит, процессы approvals
  • Практический подход, который часто работает лучше workspaces для dev/stage/prod:

  • отдельные каталоги (или отдельные root-модули) под окружения
  • отдельные state (разные key/путь в backend)
  • отдельные переменные и политики доступа
  • Миграции: перенос state и безопасные изменения структуры

    Под «миграциями» в Terraform обычно имеют в виду два разных класса задач:

  • миграция хранилища состояния (переезд с локального state на удалённый backend, смена backend)
  • миграция адресов ресурсов в state при рефакторинге кода (переименования, вынос в модуль, смена count на for_each)
  • Важно различать эти сценарии, потому что инструменты и риски отличаются.

    Миграция backend: переезд state в другое хранилище

    Типовой пример: вы начинали с локального terraform.tfstate, а затем решили хранить состояние удалённо.

    Рекомендуемый процесс:

  • Убедитесь, что рабочая директория чистая и вы понимаете текущий план (terraform plan).
  • Сделайте резервную копию локального state.
  • Добавьте/измените блок terraform { backend ... }.
  • Выполните инициализацию с миграцией.
  • Ключевые флаги terraform init:

  • -migrate-state переносит существующий state в новый backend
  • -reconfigure заставляет Terraform забыть предыдущую конфигурацию backend и настроить заново
  • Пример команды:

    Если вы меняли настройки backend и Terraform «цепляется» за старые параметры:

    Документация:

  • terraform init
  • Миграция адресов ресурсов в state при рефакторинге

    Terraform идентифицирует ресурсы по адресу (например, local_file.hello или module.app.aws_instance.web). Если вы меняете структуру кода, Terraform может решить, что старый ресурс нужно удалить, а новый создать, хотя в реальности вы просто «переместили» описание.

    Есть два основных инструмента, чтобы сказать Terraform: это тот же объект, просто теперь он живёт по новому адресу.

    #### moved blocks: декларативная миграция внутри кода

    Если вы перемещаете или переименовываете ресурсы, используйте блоки moved.

    Документация:

  • Moved Configuration Block
  • Идея (пример):

    Что это даёт:

  • миграция становится частью истории изменений в репозитории
  • меньше ручных операций со state
  • проще и безопаснее проводить рефакторинг через PR и ревью
  • #### terraform state mv: ручной перенос адреса в state

    Команда terraform state mv физически переносит запись в state с одного адреса на другой.

    Документация:

  • terraform state mv
  • Пример:

    Практическое правило:

  • для повторяемых командных изменений предпочитайте moved
  • state mv полезен, когда нужно быстро исправить state или выполнить миграцию, которую сложно описать декларативно
  • Миграция между подходами count/for_each

    При переходе с count на for_each (или наоборот) меняются адреса экземпляров ресурсов в state. Если этого не учесть, Terraform может запланировать удаление и пересоздание.

    В таких случаях чаще всего применяют:

  • moved блоки для переезда адресов
  • аккуратный план рефакторинга: маленькими шагами, с проверкой terraform plan на каждом шаге
  • Практическая модель командной работы со state

    Ниже минимальный чек-лист, который стоит внедрять рано:

  • удалённый backend для state
  • блокировки (locking) включены и проверены
  • права доступа на state ограничены (особенно для prod)
  • terraform plan обязателен перед apply
  • миграции backend и рефакторинг ресурсов делаются через PR, с понятным планом отката
  • Итоги

    В этой статье вы разобрали:

  • зачем Terraform нужен state и почему это не просто «файл в папке»
  • что такое backend и почему удалённое хранение state — стандарт для команды
  • как работает locking и зачем он защищает инфраструктуру от параллельных изменений
  • что такое workspace и где он полезен, а где опасен как стратегия окружений
  • как мигрировать state между backends и как безопасно переносить адреса ресурсов при рефакторинге (moved, terraform state mv)
  • Дальше эти знания станут основой для более реалистичных проектов: командной работы, CI/CD пайплайнов, модулей и разделения инфраструктуры на окружения без риска случайных изменений.

    5. Модули и композиция: переиспользование, версии и registry

    Модули и композиция: переиспользование, версии и registry

    Зачем нужны модули в Terraform

    По мере роста Terraform-кода вы быстро увидите повторяющиеся фрагменты: одинаковые сети для разных окружений, типовые настройки логирования, единые правила именования, одинаковые наборы ресурсов для сервиса.

    Модули решают эту задачу так же, как функции и библиотеки в программировании:

  • выносите повторяемую логику в отдельный блок кода
  • задаёте входы через variable
  • отдаёте результаты через output
  • переиспользуете один и тот же модуль много раз
  • Связь с предыдущими темами курса:

  • вы уже умеете работать с variable, locals, output, for_each, depends_on и понимаете жизненный цикл ресурсов
  • вы знаете, что Terraform опирается на state, а значит модульность влияет на адреса ресурсов в состоянии и на безопасный рефакторинг
  • Официальная документация:

  • Модули в Terraform
  • Источники модулей
  • Terraform Registry
  • Термины: root-модуль и child-модуль

    Terraform всегда запускается в конкретной директории с .tf файлами. Это и есть root-модуль.

  • Root-модуль: текущая папка, где вы выполняете terraform init/plan/apply.
  • Child-модуль: модуль, который подключается из root-модуля или из другого child-модуля через блок module.
  • Важное практическое правило:

  • terraform init скачивает child-модули и кладёт их в .terraform/modules, но исходный код модулей должен быть управляемым и версионируемым (в репозитории или через registry)
  • !Диаграмма показывает, как root-модуль связывает child-модули через inputs/outputs и как это отражается в адресах ресурсов

    Синтаксис блока module: минимально необходимое

    Подключение модуля выглядит так:

    Где:

  • name это локальное имя модуля в вашем root-модуле
  • source это откуда взять модуль (папка, Git, Registry и другие источники)
  • остальные аргументы это значения variable из подключаемого модуля
  • Обращение к output модуля:

  • module.<name>.<output_name>
  • Практика без облака: модуль, который создаёт файлы

    Ниже мини-проект на провайдере local, который показывает главный принцип: модуль принимает параметры и создаёт набор ресурсов.

    Структура каталогов

    Рекомендуемая структура:

  • main.tf
  • variables.tf
  • outputs.tf
  • modules/files/
  • modules/files/main.tf
  • modules/files/variables.tf
  • modules/files/outputs.tf
  • Код child-модуля modules/files

    modules/files/variables.tf:

    modules/files/main.tf:

    modules/files/outputs.tf:

    Что важно:

  • внутри модуля используются те же HCL-конструкции, что и в root-модуле
  • for_each создаёт набор ресурсов, а output отдаёт результат наружу
  • Код root-модуля

    main.tf:

    outputs.tf:

    Команды:

    Результат:

  • root-модуль скачает и подготовит модуль
  • модуль создаст файлы dev-app.txt и dev-db.txt
  • root-модуль выведет список файлов через module.configs.filenames
  • Как модули связаны со state и адресами ресурсов

    Terraform хранит соответствие между конфигурацией и реальными объектами через адреса ресурсов в state. С модулями адреса становятся составными.

    Пример:

  • без модулей: local_file.hello
  • внутри модуля configs: module.configs.local_file.this["app"]
  • Практические последствия:

  • при рефакторинге (переезд ресурса в модуль, переименование модуля) Terraform может запланировать удаление и создание заново, потому что адрес изменился
  • чтобы миграция была безопасной, применяйте moved блоки, которые вы изучали в теме про состояние
  • Документация:

  • Refactoring и moved blocks
  • Источники модулей: откуда брать module source

    Локальный путь

    Используется для обучения, монорепозиториев и простых сценариев:

    Минусы локального пути:

  • сложнее переиспользовать между репозиториями
  • версионирование завязано на весь репозиторий
  • Git-репозиторий

    Подходит, когда вы храните модуль отдельно и хотите получать его как зависимость.

    Документация:

  • Git как источник модулей
  • Типовой вид (пример формы записи):

    Практические правила для Git-модулей:

  • фиксируйте ref на тег или commit, чтобы сборки были воспроизводимыми
  • не ссылайтесь на плавающие ветки вроде main, если это продакшн-инфраструктура
  • Terraform Registry

    Terraform Registry это каталог модулей и провайдеров. Он удобен тем, что поддерживает версионирование и стандартный формат источника.

  • Terraform Registry
  • Подключение модуля из registry обычно выглядит так:

    Что важно:

  • version относится к версии модуля, а не провайдера
  • конкретные входные переменные и outputs зависят от автора модуля и описаны в документации на странице модуля в registry
  • Версионирование модулей: почему это критично

    Без версионирования модулей вы теряете главный инженерный плюс IaC: повторяемость.

    В Terraform есть два уровня версионирования:

  • версия провайдера, задаётся в required_providers
  • версия модуля, задаётся в блоке module через version (для registry) или через ref (для Git)
  • Документация про синтаксис ограничений версий:

  • Version Constraints
  • Практические подходы:

  • для провайдеров используйте диапазоны, которые допускают безопасные обновления, и фиксируйте их в репозитории
  • для модулей в registry обычно фиксируют точную версию в root-модулях, а обновление делают осознанно через PR
  • Композиция модулей: как собирать инфраструктуру слоями

    Обычно Terraform-код строят как композицию:

  • root-модуль описывает сборку системы из крупных блоков
  • child-модули описывают повторяемые подсистемы
  • Паттерн: wiring через outputs

    Частый сценарий:

  • модуль network создаёт сеть и отдаёт subnet_ids
  • модуль app принимает subnet_ids и создаёт вычислительные ресурсы
  • Это соединение делается не через depends_on, а через данные:

  • если module.app использует module.network.subnet_ids, Terraform автоматически построит зависимость
  • Паттерн: несколько экземпляров одного модуля

    Модуль можно инстанцировать несколько раз:

    Или создавать динамически через for_each:

    Практическая выгода:

  • один шаблон инфраструктуры, много экземпляров (сервисы, окружения, регионы), при этом поведение воспроизводимо
  • Провайдеры и модули: важные нюансы

    Где объявлять required_providers

    Общее правило:

  • каждый модуль должен явно объявлять required_providers, которые он использует
  • Это делает модуль самодостаточным и снижает сюрпризы при подключении.

    Документация:

  • Provider Requirements
  • Как передать alias-провайдер в модуль

    Если вы используете alias (например, разные регионы), то модулю часто нужно явно указать, какой конфигурацией провайдера пользоваться.

    Документация:

  • Передача провайдеров в модули
  • Общая форма:

    Практическое правило:

  • если модуль должен работать в разных аккаунтах или регионах, заранее проектируйте его так, чтобы он поддерживал передачу нужной конфигурации провайдера
  • Что скачивается при init и где искать проблемы

    При terraform init Terraform:

  • скачивает провайдеры
  • скачивает модули
  • Для модулей обычно важно помнить:

  • код модулей раскладывается в .terraform/modules
  • если вы поменяли source или версию модуля, часто требуется повторный terraform init
  • Полезные команды:

    Документация:

  • terraform init
  • terraform get
  • Типовые ошибки при работе с модулями

  • Пытаться использовать depends_on вместо передачи данных через inputs/outputs.
  • Не фиксировать версии модулей и провайдеров, из-за чего поведение меняется «само».
  • Переименовывать модуль или переносить ресурсы в модуль без moved блоков, получая неожиданные пересоздания.
  • Делать модуль слишком «умным» и универсальным, теряя ясность и предсказуемость.
  • Итоги

    В этой статье вы:

  • разобрали, что такое root-модуль и child-модуль, и как Terraform подключает модули
  • научились проектировать модуль через variable и output и соединять модули через outputs
  • поняли, как модули влияют на адреса ресурсов в state и почему это важно для рефакторинга
  • изучили основные источники модулей: локальные пути, Git и Terraform Registry
  • разобрали практику версионирования модулей и провайдеров
  • увидели паттерны композиции: wiring, множественные экземпляры и for_each для модулей
  • Дальше модульность станет основой для «боевой» структуры репозитория: разделения на окружения, повторяемых компонент и безопасных обновлений через CI/CD, не ломая state и не создавая неожиданных пересозданий.

    6. Планирование и безопасность: policy, secrets, drift и импорт

    Планирование и безопасность: policy, secrets, drift и импорт

    Зачем эта тема нужна после модулей и state

    В предыдущих статьях вы научились описывать инфраструктуру на HCL, понимать жизненный цикл ресурсов, работать с состоянием (state), удалёнными backend, блокировками и модулями. Следующий шаг к практике — сделать работу с Terraform безопасной и управляемой в команде.

    В этой статье разберём четыре больших направления:

  • Планирование как основной механизм контроля изменений
  • Policy as code — правила, которые автоматически запрещают опасные изменения
  • Secrets — как не утечь секретам через код, логи и state
  • Drift — как находить и исправлять расхождение между описанием и реальностью
  • Импорт — как аккуратно «подхватывать» уже существующую инфраструктуру
  • !Типовой безопасный процесс: plan в PR, проверки политик, apply после одобрения

    Планирование как основа контроля изменений

    Что именно даёт terraform plan

    terraform plan — это не просто «посмотреть разницу». Это механизм, который позволяет:

  • увидеть предложенные действия до реальных изменений
  • поймать ошибки типа зависимостей, пересозданий, удаления критичных ресурсов
  • запустить проверки безопасности и политик на планируемые изменения
  • Документация:

  • Команда terraform plan
  • Planfile: фиксируем план, который будет применён

    Ключевой практический приём для CI/CD и командной работы — сохранять план в файл и применять именно его:

    Почему это важно:

  • apply без planfile пересчитает план заново, и результат может отличаться (например, из-за параллельных изменений или изменений данных)
  • planfile даёт возможность прогнать политики и сканеры до применения
  • Полезные флаги планирования

  • -var и -var-file — управление входными параметрами
  • -destroy — план на уничтожение (используйте осторожно)
  • -refresh-only — план, который только обновляет state по факту реальности, не меняя инфраструктуру
  • Документация:

  • Команда terraform apply
  • Почему -target — почти всегда плохая идея

    Флаг -target заставляет Terraform «прицелиться» в конкретные ресурсы и может обходить нормальный граф зависимостей. Это полезно в аварийных ситуациях, но опасно как регулярная практика.

    Практическое правило:

  • используйте -target только для точечных аварийных действий и с полным пониманием последствий
  • после применения обязательно делайте обычный terraform plan, чтобы убедиться, что система снова консистентна
  • Policy as code: как запрещать опасные изменения автоматически

    Что такое policy и где она живёт

    Policy as code — это формализованные правила, которые проверяют:

  • конфигурацию (static checks)
  • или план изменений (plan-time checks)
  • И решают: можно ли применять изменения.

    Типовые политики:

  • запрет публичного доступа к хранилищам
  • запрет ресурсов без обязательных тегов
  • запрет слишком широких прав доступа
  • запрет удаления определённых классов ресурсов
  • Варианты enforcement: где именно «остановить» изменения

  • В Git-процессе
  • - проверка на PR: если политика не проходит, PR нельзя смержить
  • В CI/CD пайплайне
  • - даже если PR смёржен, пайплайн не выполнит apply
  • На уровне платформы
  • - Terraform Cloud/Enterprise могут централизованно запрещать apply

    Terraform Cloud и Sentinel

    Sentinel — язык политик, встроенный в Terraform Cloud/Enterprise.

    Документация:

  • Sentinel в Terraform Cloud
  • Идея:

  • Terraform Cloud получает план
  • Sentinel оценивает его по правилам
  • если политика нарушена, apply запрещён
  • OPA и Conftest: политики поверх plan в JSON

    В open-source среде частый подход:

  • Сгенерировать план
  • Преобразовать его в JSON
  • Проверить правилами OPA (Rego) через Conftest
  • Ссылки:

  • Open Policy Agent
  • Conftest
  • Команда terraform show
  • Пример потока:

    Пример очень упрощённой идеи правила (не «боевой» пример, только чтобы понять механику): запретить удаление ресурсов.

    Ключевой принцип:

  • политики должны проверять то, что реально будет сделано, то есть план
  • Статические сканеры: tfsec, Checkov, TFLint

    Эти инструменты не заменяют policy enforcement, но сильно повышают качество.

    | Инструмент | Что проверяет | Где применяют | |---|---|---| | TFLint | стиль, ошибки, специфичные правила для провайдеров | локально и в CI | | tfsec | security misconfigurations в Terraform | CI, PR checks | | Checkov | политики безопасности для IaC (Terraform и не только) | CI, PR checks |

    Практическая связка:

  • terraform fmt и terraform validate как минимальный барьер
  • сканеры как второй барьер
  • policy enforcement по plan как финальный барьер перед apply
  • Secrets: как не утекать секретам через код и state

    Где «живут» секреты в Terraform

    Секреты — это любые данные, утечка которых опасна:

  • API-токены
  • пароли
  • приватные ключи
  • строки подключения
  • Типовые места утечек:

  • *.tfvars в репозитории
  • вывод terraform plan и terraform apply в CI логах
  • outputs
  • terraform.tfstate и удалённый backend
  • Важно: sensitive скрывает вывод, но не делает секрет «невидимым»

    Terraform умеет помечать значения как sensitive:

    Что это даёт:

  • Terraform постарается не печатать значение в консоли
  • Чего это не даёт:

  • секрет всё равно может попасть в state
  • любой, кто читает state, потенциально может получить секрет
  • Документация:

  • Sensitive данные в Terraform
  • Sensitive outputs
  • Практики хранения и передачи секретов

  • Не хранить секреты в Git
  • - добавляйте *.tfvars с секретами в .gitignore
  • Передавать секреты через окружение
  • - переменные вида TF_VAR_<name>
  • Использовать секрет-хранилища
  • - например, HashiCorp Vault

    Ссылки:

  • HashiCorp Vault
  • SOPS и шифрованные tfvars

    Если вам нужно хранить значения рядом с кодом (например, для GitOps), часто используют шифрование файлов с секретами.

    Один из популярных инструментов:

  • Mozilla SOPS
  • Практический смысл:

  • в репозитории лежат только зашифрованные данные
  • дешифрование происходит в CI/CD при наличии ключей
  • Доступ к state — это доступ к секретам

    Если в вашей инфраструктуре есть секреты, то:

  • доступ на чтение к backend со state фактически равен доступу к чувствительным данным
  • Практические меры:

  • минимизируйте права на чтение state
  • используйте отдельные backends и ключи state для prod
  • включайте шифрование на хранилище (на стороне S3/GCS/Azure или Terraform Cloud)
  • Drift: как обнаружить и лечить расхождение с реальностью

    Что такое drift

    Drift — это ситуация, когда:

  • в конфигурации Terraform описано одно
  • в реальной платформе получилось другое
  • Типовые причины:

  • ручные изменения в консоли облака
  • действия внешних систем (autoscalers, оператор Kubernetes, политики организации)
  • изменение параметров по умолчанию у провайдера/платформы
  • !Цикл обнаружения drift и варианты реакции

    Как проверять drift безопасно

    Terraform позволяет обновить state, не меняя инфраструктуру:

    Если нужно обновить state по факту реальности (например, после ручного изменения, которое вы решили принять):

    Документация:

  • Флаг -refresh-only для terraform plan
  • Флаг -refresh-only для terraform apply
  • Как правильно реагировать на drift

    Есть два корректных сценария:

  • Drift недопустим
  • - вы исправляете реальность через terraform apply, возвращаясь к описанному состоянию
  • Drift допустим и это новое требование
  • - вы меняете конфигурацию Terraform, чтобы она отражала реальность, и затем применяете

    Плохой сценарий:

  • скрывать drift через lifecycle { ignore_changes = ... } без строгой причины
  • ignore_changes полезен, когда атрибут обязан управляться внешней системой, но это должно быть архитектурно обосновано.

    Документация:

  • ignore_changes
  • Импорт: как взять под управление уже существующие ресурсы

    Зачем нужен импорт

    Импорт нужен, когда ресурс уже существует, но Terraform о нём «не знает». Частые ситуации:

  • инфраструктуру создали вручную до внедрения Terraform
  • ресурс создал другой процесс, но теперь вы хотите управлять им декларативно
  • Важно:

  • импорт не «угадывает» конфигурацию автоматически
  • импорт только связывает адрес ресурса в Terraform с реальным объектом в state
  • Базовый подход: terraform import

    Высокоуровневый процесс:

  • Написать конфигурацию ресурса в .tf
  • Выполнить импорт в state
  • Запустить terraform plan и довести конфигурацию до совпадения с реальностью
  • Документация:

  • Команда terraform import
  • Пример формы команды:

    Где:

  • <АДРЕС_РЕСУРСА> — адрес вида aws_instance.web или module.app.aws_instance.web
  • <ID_ОБЪЕКТА> — идентификатор в платформе (его формат зависит от провайдера)
  • Import blocks: импорт как часть кода

    Начиная с новых версий Terraform, можно описывать импорт декларативно через import блоки.

    Документация:

  • Import blocks
  • Идея:

  • импорт становится частью PR
  • меньше ручных команд
  • проще повторять процесс в предсказуемом виде
  • Импорт и for_each: важность адресов

    Если ресурс создаётся через for_each, адрес в state включает ключ:

  • aws_s3_bucket.this["logs"]
  • Это влияет на импорт: вы должны импортировать конкретный экземпляр по правильному адресу.

    Как не сломать инфраструктуру при импорте

    Практический чек-лист:

  • делайте импорт маленькими шагами
  • после каждого шага запускайте terraform plan
  • не пытайтесь «сразу импортировать всё» без понимания зависимостей
  • заранее продумайте структуру модулей, чтобы не делать массовые миграции адресов сразу после импорта
  • Если нужно перестать управлять ресурсом через Terraform:

  • используется удаление объекта из state без удаления в платформе
  • Документация:

  • Команда terraform state rm
  • Рекомендуемый безопасный процесс для команды

    Ниже практическая схема, которая обычно хорошо масштабируется.

  • Локально разработчик делает изменения
  • Запускаются базовые проверки
  • - terraform fmt - terraform validate - линтеры и сканеры
  • В PR пайплайн строит plan -out=tfplan
  • План проверяют политики
  • - Sentinel в Terraform Cloud или OPA/Conftest в CI
  • Apply запускается только после одобрения и только из доверенной среды
  • - с удалённым backend - с locking - с секретами из секрет-хранилища
  • Drift-check по расписанию
  • - terraform plan -refresh-only как регулярный контроль

    Итоги

    Вы научились смотреть на Terraform не только как на инструмент создания ресурсов, но как на контролируемый процесс изменений:

  • terraform plan и planfile — основа воспроизводимых и проверяемых изменений
  • policy as code позволяет автоматически запрещать опасные действия
  • secrets требуют дисциплины: чувствительные значения могут оказаться в state и логах
  • drift нужно регулярно обнаруживать и осознанно либо исправлять, либо принимать через изменение кода
  • импорт помогает постепенно перевести существующую инфраструктуру под управление Terraform, но требует аккуратной работы с адресами и планом
  • 7. Автоматизация и CI/CD: тестирование, линтинг и лучшие практики

    Автоматизация и CI/CD: тестирование, линтинг и лучшие практики

    Terraform раскрывается полностью, когда изменения инфраструктуры проходят так же управляемо, как изменения кода: через ветки, ревью, автоматические проверки и предсказуемый apply. В прошлых статьях вы разобрали жизненный цикл (plan и apply), состояние (backend, locking, миграции), модули и основы безопасности (policy, secrets, drift, импорт). Здесь соберём это в практический процесс автоматизации.

    !Диаграмма типового пайплайна Terraform от PR до применения

    Принципы надёжного пайплайна Terraform

    Цель CI/CD для Terraform не в том, чтобы просто запустить команды, а в том, чтобы сделать изменения:

  • повторяемыми: одинаковые входы дают одинаковые результаты
  • проверяемыми: риск ловится на стадии plan, а не после apply
  • безопасными: секреты не попадают в логи и Git, а state защищён
  • разделёнными: plan и apply выполняются в разных шагах с контролем доступа
  • Ключевой паттерн из предыдущей статьи:

  • делать terraform plan -out=tfplan
  • применять именно этот план: terraform apply tfplan
  • Документация:

  • terraform plan
  • terraform apply
  • Структура проверок: что запускать локально, что в CI

    Удобно мыслить пайплайн как набор уровней.

    Базовые проверки (должны быть везде)

  • terraform fmt -check как проверка форматирования
  • terraform validate как базовая валидация конфигурации
  • Документация:

  • terraform fmt
  • terraform validate
  • Практическая деталь для CI: часто делают terraform init без подключения backend, чтобы проверки не требовали доступов к state.

  • terraform init -backend=false
  • Документация:

  • terraform init
  • Линтинг Terraform-кода

    Terraform сам по себе валидирует синтаксис, но линтеры ловят проблемы раньше и делают код единообразным.

  • TFLint проверяет типовые ошибки, стиль и правила для конкретных провайдеров
  • Ссылки:

  • TFLint
  • Типовой запуск:

    Проверки безопасности конфигурации

    Статические сканеры помогают находить небезопасные настройки до планирования или независимо от облачных доступов.

    Часто используют:

  • tfsec
  • Checkov
  • Важно: сканеры полезны как ранний барьер, но они не заменяют policy as code по плану.

    Тестирование Terraform: что реально можно тестировать

    Тестирование в IaC обычно делят на уровни, потому что часть логики живёт в выражениях HCL и модулях, а часть проявляется только при создании ресурсов.

    Тесты на уровне Terraform CLI

    В Terraform есть команда terraform test, которая запускает тест-раннер Terraform.

    Документация:

  • terraform test
  • Общая идея:

  • вы описываете тестовые сценарии
  • Terraform выполняет проверки, чтобы поймать ошибки модулей и выражений
  • Пример минимального запуска:

    Практический смысл:

  • тесты особенно полезны для модулей, где много for_each, условий и вычислений в locals
  • Интеграционные тесты Terratest

    Если вам нужно проверить реальное создание ресурсов в облаке, часто используют Terratest.

    Ссылки:

  • Terratest
  • Репозиторий Terratest
  • Плюсы:

  • можно поднимать реальную инфраструктуру, проверять её, затем удалять
  • Минусы:

  • тесты дороже и медленнее
  • нужен отдельный подход к аккаунтам, квотам, стоимости и изоляции окружений
  • Policy as code в CI: проверяем не код, а план

    Политики должны оценивать то, что будет применено, то есть план.

    Типовой поток:

  • terraform plan -out=tfplan
  • terraform show -json tfplan > tfplan.json
  • проверка tfplan.json политиками
  • Документация:

  • terraform show
  • Для open-source стека часто используют OPA и Conftest:

  • Open Policy Agent
  • Conftest
  • Минимальный пример запуска:

    Практическое правило:

  • статические сканеры ловят типовые проблемы
  • policy по плану запрещает то, что недопустимо именно в вашей организации
  • Секреты и доступы в CI/CD

    То, что вы уже знаете из темы про secrets и state, в CI становится ещё важнее.

    Практики, которые хорошо работают:

  • секреты не хранятся в репозитории
  • секреты передаются как переменные окружения или через секрет-хранилище
  • state хранится в удалённом backend, доступы минимальны
  • apply выполняется только из защищённой среды (например, отдельный workflow, отдельные права)
  • Если у вас облачный провайдер, по возможности используйте короткоживущие учётные данные, например через OIDC, чтобы не держать долгие ключи в секретах CI.

    Plan и Apply как два разных процесса

    Надёжная схема для команды:

  • в PR выполняется plan и публикуется результат
  • после одобрения выполняется apply тем же planfile
  • Ключевые детали:

  • planfile должен быть артефактом пайплайна
  • окружение для apply должно иметь доступ к backend и блокировкам
  • Пример пайплайна GitHub Actions

    Ниже пример, который показывает базовую структуру. Он не привязан к конкретному облаку, но отражает правильное разделение стадий.

    Что важно в реальной жизни улучшить по сравнению с примером:

  • вместо terraform apply -auto-approve использовать planfile и ручное одобрение
  • добавить policy checks по tfplan.json перед apply
  • ограничить права apply-джоба и доступы к секретам
  • Ссылки:

  • hashicorp/setup-terraform
  • Pre-commit: переносим качество на машину разработчика

    Часть проверок лучше запускать до того, как код попадёт в CI.

    Подход:

  • поставить pre-commit
  • подключить хуки для Terraform
  • Популярный набор хуков:

  • pre-commit-terraform
  • Практическая цель:

  • форматирование и базовые проверки выполняются автоматически
  • уменьшается количество циклов правок только ради CI
  • Ускорение CI и стабильность окружения

    Полезные практики для уменьшения времени пайплайна и снижения флапающих ошибок:

  • фиксируйте версии Terraform и провайдеров
  • кэшируйте плагины провайдеров через TF_PLUGIN_CACHE_DIR
  • избегайте динамики в apply без planfile
  • не используйте -target как нормальный режим работы
  • Документация:

  • Environment variables: TF_PLUGIN_CACHE_DIR
  • Drift-check по расписанию

    Даже с хорошим процессом drift может появляться из-за ручных действий или внешних систем.

    Типовой подход:

  • по расписанию запускать terraform plan -refresh-only
  • уведомлять команду, если есть изменения
  • Документация:

  • plan -refresh-only
  • Лучшие практики итогом

    Набор правил, который обычно даёт максимальный эффект:

  • fmt и validate обязательны локально и в CI
  • plan делается в PR, apply делается после одобрения
  • политики проверяют tfplan.json, а не только код
  • state удалённый, с locking и минимальными правами
  • секреты не в Git и не в выводе пайплайна
  • модули и версии фиксируются, обновления делаются осознанно
  • Итоги

    Вы собрали практический процесс автоматизации Terraform:

  • какие проверки запускать на разных этапах и зачем
  • как добавлять линтинг и security scanning
  • как тестировать модули и когда нужны интеграционные тесты
  • как строить policy checks по плану
  • как организовать безопасный plan и контролируемый apply в CI/CD
  • Эта автоматизация завершает картину курса: Terraform становится не просто способом создать ресурсы, а воспроизводимым процессом изменения инфраструктуры с качеством, безопасностью и командными практиками.