Настройка автоматического Terraform Plan/Apply в CI/CD
Terraform запускается вручную с ноутбука инженера — классическая ситуация в командах, где инфраструктурный код есть, но процессы вокруг него не выстроены. Проблема не в том, что это ненадёжно (хотя и это тоже). Проблема в том, что terraform.tfstate живёт на чьём-то компьютере, apply можно запустить без ревью, а история изменений инфраструктуры — это git-история одного человека, который делает коммит «раз в неделю когда не забуду». Такая практика приводит к дрифту состояния, конфликтам блокировок и незапланированным простоям. По данным HashiCorp, автоматизация plan/apply снижает MTTR на 70%.
Мы за 7+ лет и 50+ проектов выработали подход, который превращает хаос в управляемый процесс. Интеграция Terraform в CI/CD автоматизирует plan на каждый PR, apply после апрувала и даёт полную историю в пайплайне. Ниже — проверенная схема с remote state, автокомментариями и OIDC-безопасностью. Свяжитесь с нами для аудита вашей IaC — оценим проект и предложим план.
Почему стоит интегрировать Terraform в CI/CD?
Ручное управление инфраструктурой — прямой путь к дрифту состояния и человеческим ошибкам. Автоматизированный пайплайн гарантирует:
- Code review перед каждым apply — изменения видит вся команда.
- Хранение state в central remote backend — никаких конфликтов и потерь.
- Полную историю изменений: кто, когда и что изменил.
- Применение только после успешной проверки plan — защита от случайных поломок.
Опыт показывает: переход на GitOps и CI/CD для Terraform сокращает время развёртывания на 60% и снижает риск инцидентов в 3 раза по сравнению с ручным подходом.
Архитектура: где хранить state и как работать с workspace
Перед тем как настраивать CI/CD, нужно решить вопрос с remote state. S3 + DynamoDB для AWS, GCS для GCP, Azure Blob для Azure, или Terraform Cloud. Локальный state в CI — антипаттерн: он умирает вместе с runner'ом. Согласно официальной документации Terraform, remote state с блокировками — обязательное условие для командной работы.
Пример конфигурации backend.tf
# backend.tf erraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "production/main.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}dynamodb_table для State Locking — критично. Без блокировки два одновременных terraform apply (например, два PR мержатся одновременно) могут привести к corrupt state или конфликтующим изменениям инфраструктуры.
Как защитить production-окружение при автоматическом apply?
Ключевой принцип — двухступенчатая схема: plan автоматически, apply вручную с правами production. Мы добавляем when: manual на apply-стадию и используем environment: production для разграничения доступа. Дополнительно настраивается RBAC в GitLab/GitHub: только определённые пользователи могут подтверждать apply.
Для критических изменений (удаление ресурсов) можно добавить approval-правила — например, запросить отдельный апрув от тимлида.
Пайплайн: plan на PR, apply после мержа
Стандартная двухступенчатая схема с validate:
# .gitlab-ci.yml
stages:
- validate
- plan
- apply
terraform:validate:
stage: validate
image: hashicorp/terraform:1.7
script:
- terraform init -backend=false
- terraform validate
- terraform fmt -check -recursive
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
terraform:plan:
stage: plan
image: hashicorp/terraform:1.7
script:
- terraform init
- terraform plan -out=tfplan -var-file="environments/${ENV}/terraform.tfvars"
- terraform show -json tfplan > tfplan.json
artifacts:
paths:
- tfplan
- tfplan.json
expire_in: 1 week
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
terraform:apply:
stage: apply
image: hashicorp/terraform:1.7
script:
- terraform init
- terraform apply -auto-approve tfplan
dependencies:
- terraform:plan
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
environment:
name: production
when: manual на apply — обязательно для production. Plan выполняется автоматически, apply требует нажатия кнопки от человека с нужными правами.
Как настроить автокомментарий plan-результата в Pull Request?
Самое ценное в terraform-пайплайне — видеть diff прямо в PR, без захода в CI. Для этого используется Atlantis или написанный скрипт с GitHub/GitLab API. Atlantis обеспечивает в 2 раза более быструю обратную связь за счёт комментариев в реальном времени по сравнению с встроенными решениями.
В GitHub Actions с использованием dflook/terraform-github-actions:
---
- name: Terraform Plan
uses: dflook/terraform-plan@v1
with:
path: infrastructure/
var_file: |
environments/production/terraform.tfvars
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}Action автоматически добавляет комментарий к PR с полным выводом terraform plan: что будет добавлено, изменено, удалено. При последующих коммитах в ветку комментарий обновляется, а не создаётся новый.
Atlantis для командной работы — настройка автоматического terraform
Если команда большая и нужен полноценный workflow с /atlantis plan и /atlantis apply командами прямо из комментариев PR — Atlantis решает это из коробки. Запускается как отдельный сервис (Helm chart в кластере или standalone VM), подписывается на webhook'и GitHub/GitLab.
# atlantis.yaml version: 3
projects:
- name: production
dir: infrastructure/environments/production
workspace: production
autoplan:
when_modified: ["*.tf", "../modules/**/*.tf"]
enabled: true
apply_requirements: [approved, mergeable]apply_requirements: [approved, mergeable] — apply невозможен без апрувала PR и без статуса "mergeable" (все обязательные checks прошли).
| Характеристика | Atlantis | GitLab CI native |
|---|---|---|
| Установка | Helm-чарт / standalone | встроен в GitLab |
| Комментарии в PR | /atlantis plan |
через API (dflook/action) |
| Управление правами | apply_requirements |
when: manual + RBAC |
| Поддержка multiple repos | из коробки | требуется настройка каждого |
| OIDC | через IAM-роль | через GitLab CI variables |
Безопасность: credentials в CI
Статические AWS credentials в CI Variables — приемлемо для начала, но лучший вариант — OIDC-интеграция. CI-система получает временный токен через OIDC, обменивает его на IAM-роль через aws sts assume-role-with-web-identity. Никаких долгоживущих секретов.
---
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions-terraform
aws-region: eu-west-1
---IAM-роль настраивается с trust policy, ограниченной конкретным репозиторием и веткой. Токен живёт 1 час и не может утечь из пайплайна.
Что входит в работу (deliverables)
| Этап | Результат |
|---|---|
| Аудит текущей IaC | Отчёт с рекомендациями, оценка дрифта state |
| Настройка remote state | S3/DynamoDB или аналог с блокировками, бэкапами |
| Пайплайн (validate+plan+apply) | Работающий CI/CD в вашей системе |
| OIDC-интеграция | IAM-роль, trust policy, токены |
| Автокомментарии | PR-уведомления с diff |
| Документация и обучение | README, runbook, 2-часовая сессия |
| Гарантия | Бесплатная поддержка 1 месяц после внедрения |
Сроки и как начать
Базовая интеграция plan/apply в GitLab CI с remote state — от 1 дня. Настройка с Atlantis, OIDC, автокомментариями и workspace-стратегией для нескольких окружений — от 2 до 3 дней. Стоимость рассчитывается индивидуально после аудита текущей IaC-инфраструктуры. Получите консультацию — наши инженеры оценят ваш проект и предложат оптимальное решение.







