Terraform: state, модулі та безпечний workflow
Що таке state і чому його не можна тримати локально, як структурувати код модулями, як рефакторити без перестворення ресурсів і як не знищити продакшен одним apply.
Terraform описує інфраструктуру кодом: мережі, бази, кластери, DNS-записи. Синтаксис HCL засвоюється за день. Складнощі починаються пізніше — зі state, структурою коду і роботою в команді.
ПриміткаУсе нижче так само стосується OpenTofu — відкритого форку Terraform під егідою Linux Foundation. Команди відрізняються лише назвою бінарника:
tofuзамістьterraform.
State: пам'ять Terraform
Terraform зберігає у файлі state відповідність між ресурсами в коді і реальними об'єктами в хмарі (aws_db_instance.main → db-7f3k2). Без нього він не знає, що вже створено.
Правила роботи зі state:
- Тільки віддалений backend з блокуванням — інакше двоє людей одночасно зіпсують state.
- Шифрування і обмежений доступ: у state у відкритому вигляді лежать паролі баз, ключі та інші секрети.
- Ніколи не редагуйте руками — лише через
terraform stateкоманди або блокиmoved/import/removed.
terraform {
required_version = ">= 1.9"
backend "s3" {
bucket = "acme-terraform-state"
key = "production/network.tfstate"
region = "eu-central-1"
encrypt = true
use_lockfile = true # блокування через S3 без окремої таблиці DynamoDB
}
required_providers {
aws = { source = "hashicorp/aws", version = "~> 6.0" }
}
}Розділяйте state
Один гігантський state на все — повільний plan і величезний радіус ураження. Ділять зазвичай за середовищем і шаром:
live/
production/
network/ # VPC, підмережі — змінюються рідко
database/
eks/
apps/
staging/
...
modules/
vpc/
rds-postgres/
eks-cluster/Модулі
Модуль — це папка з .tf-файлами, у якої є вхідні змінні й вихідні значення.
# modules/rds-postgres/variables.tf
variable "name" { type = string }
variable "instance_class" {
type = string
default = "db.t4g.medium"
}
variable "subnet_ids" { type = list(string) }
# live/production/database/main.tf
module "orders_db" {
source = "../../../modules/rds-postgres"
name = "orders"
instance_class = "db.r7g.large"
subnet_ids = data.terraform_remote_state.network.outputs.private_subnet_ids
}ПорадаНе робіть модуль на кожен ресурс. Хороший модуль описує логічну одиницю («база з параметрами, бекапами і алармами»), а не обгортає один
aws_s3_bucketз тими самими аргументами.
Безпечний workflow
terraform fmt -check
terraform validate
terraform plan -out=tfplan # зберегти план
terraform show tfplan # уважно прочитати
terraform apply tfplan # застосувати рівно те, що бачилиЧитайте план, звертаючи увагу на символи:
| Символ | Значення |
|---|---|
+ |
створення |
~ |
зміна на місці |
-/+ |
знищення і створення заново |
- |
знищення |
Обережно
-/+біля бази даних означає, що Terraform видалить її і створить порожню. Причиною може бути зміна аргументу, що вимагає перестворення (наприклад, ідентифікатор чи зона доступності). Для критичних ресурсів додавайтеlifecycle { prevent_destroy = true }.
Рефакторинг без перестворення
Перейменували ресурс або перенесли в модуль — Terraform вирішить, що старий треба знищити, а новий створити. Блок moved пояснює, що це той самий об'єкт:
moved {
from = aws_db_instance.main
to = module.orders_db.aws_db_instance.this
}А ресурси, створені вручну колись давно, можна взяти під керування блоком import:
import {
to = aws_s3_bucket.logs
id = "acme-legacy-logs"
}Terraform у CI
- на пул-реквест —
fmt,validate,planі коментар з планом у PR; apply— лише після мерджу, з тим самим збереженим планом;- доступ до хмари — через OIDC, без довгоживучих ключів у секретах CI;
- сканери конфігурацій (tflint, Checkov, Trivy) ловлять відкриті security groups і бакети без шифрування ще до apply.
Drift
Хтось змінив налаштування в консолі хмари — код і реальність розійшлися. Регулярний terraform plan за розкладом (наприклад, щоночі) показує такий drift. Виправляють його або поверненням до коду, або оновленням коду під реальність — але не ігноруванням.
Ви перенесли ресурс у модуль, і plan показує знищення та створення. Як уникнути перестворення?
Додати блок moved зі старою і новою адресою ресурсу (або виконати terraform state mv). Тоді Terraform зрозуміє, що це той самий об'єкт, і план не міститиме знищення.
Збережіть статтю собі
Увійдіть, щоб імпортувати її у власні нотатки, відмічати вивчене, ставити лайки й питати спільноту. Реєстрація безкоштовна: вхід через Google займає кілька секунд.
Увійти через Google Інші способи входу