Налаштування автоматичного 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
terraform {
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-інфраструктури. Отримайте консультацію — наші інженери оцінять ваш проєкт і запропонують оптимальне рішення.







