Terraform
Редактировать на GitHubtfstate, чтобы несколько пользователей не изменяли инфраструктуру одновременно?Как перенести ресурс, созданный через GUI, в код Terraform?В чём отличие между condition (тернарным оператором) и lookup в Terraform?Практические вопросыТы написал код в Terraform, выполнил terraform plan и вышел. После этого твой коллега добавил свой код, выполнил plan и тоже вышел. Теперь тебе нужно применить свою инфраструктуру — будут ли ошибки?У вас есть 20 серверов, созданных с помощью Terraform, но вы хотите удалить только один из них. Можно ли уничтожить один ресурс, не внося изменения в конфигурационные файлы?Какие есть best practices для “ухода” за Terraform tfstate?У вас есть несколько сред — dev, stage, prod — и вы хотите использовать один и тот же код Terraform для всех. Как это сделать?Теоретические вопросы
В чём отличие Ansible и Terraform, если через Ansible теперь тоже можно создавать инфраструктуру?
Да, через Ansible сегодня можно создавать инфраструктуру — у него есть модули для разных провайдеров. Но принципиальное отличие остаётся в подходе и архитектуре инструментов:
- Terraform — это инструмент декларативного описания инфраструктуры. Он хранит состояние (state), понимает, что уже создано, и вносит только нужные изменения. Это делает его идеальным для управления большими облачными средами
- Ansible — остаётся инструментом процедурного конфигурирования. Он выполняет задачи пошагово, не отслеживая текущее состояние ресурсов. При помощи модулей он может создавать те же виртуалки или сети, но не управляет изменениями и зависимостями так эффективно, как Terraform.
Итог: Да, Ansible может делать то же, что Terraform, но Terraform делает это лучше и безопаснее для инфраструктуры, а Ansible удобнее для настройки и конфигурации систем после их создания.
Что такое провайдер?
Провайдер (provider) — это плагин, через который Terraform взаимодействует с внешними системами: облаками, виртуализацией, API и другими сервисами.
Он отвечает за создание, изменение и удаление ресурсов конкретного типа — например, виртуальных машин, сетей, DNS-записей и т.п.
Что такое ресурс?
Ресурс (resource) — это основной строительный блок в Terraform, описывающий конкретный объект инфраструктуры, который нужно создать, изменить или удалить.
Каждый ресурс управляется через провайдер и представляет реальный объект во внешней системе — например:
- виртуальную машину,
- сетевой интерфейс,
- S3-бакет,
- DNS-запись,
- Kubernetes pod и т.д.
Что такое tfstate?
tfstate — это файл состояния Terraform, который хранит актуальное описание созданной инфраструктуры.
Terraform использует этот файл, чтобы понимать:
- какие ресурсы уже существуют,
- какие параметры у них заданы,
- что нужно создать, изменить или удалить при следующем запуске
terraform apply.
По умолчанию файл называется terraform.tfstate и создаётся в рабочей директории, но его можно хранить удалённо (в S3, GCS, etcd, Consul и т.д.) — это обязательно при командной работе.
Важно знать:
tfstate— единственный источник правды о состоянии инфраструктуры.- При повреждении или потере файла Terraform не сможет корректно рассчитать изменения.
- Для командной работы используют remote backend и state locking (блокировку состояния).
Что такое configuration drift (дрейф конфигурации)?
Configuration drift — это расхождение между описанием инфраструктуры в коде (Terraform, Ansible и т.д.) и её фактическим состоянием в реальности.
Иными словами — когда кто-то вручную или внешним инструментом изменил ресурсы, и теперь то, что задекларировано в коде, не совпадает с тем, что реально работает.
Примеры дрейфа:
- кто-то изменил параметры VM через консоль;
- удалили ресурс вручную, но он всё ещё описан в коде;
- поменяли сетевые настройки, которых нет в Terraform.
Как Terraform с этим работает:
- Команда
terraform planобнаруживает дрейф — она сравнивает содержимоеtfstateс текущим состоянием инфраструктуры через API провайдеров. - Если есть расхождения, Terraform предложит действия (create, update, delete), чтобы привести реальность в соответствие с кодом.
Как избежать:
- Не вносить ручные изменения в инфраструктуру.
- Хранить
tfstateцентрализованно (remote backend). - Делать регулярные
terraform planдля проверки.
Как блокировать tfstate, чтобы несколько пользователей не изменяли инфраструктуру одновременно?
Terraform поддерживает механизм state locking — блокировку состояния, чтобы предотвратить одновременные изменения инфраструктуры несколькими пользователями или процессами.
Блокировка включается автоматически при использовании remote backend, который поддерживает lock-функциональность.
🔧 Основные способы блокировки:
| Backend | Механизм блокировки |
|---|---|
| S3 + DynamoDB | S3 хранит state, DynamoDB — управляет блокировками. Это самый распространённый вариант. |
| Consul | Поддерживает блокировку через встроенный механизм sessions. |
| GCS (Google Cloud Storage) | Использует объектные блокировки (preconditions). |
| Terraform Cloud / Enterprise | Встроенная автоматическая блокировка. |
Пример:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "eu-central-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}Здесь:
S3хранит файл состояния,DynamoDBиспользуется для блокировки (LockIDзаписывается в таблицу).
Если локальный backend (local) — блокировка не поддерживается, поэтому важно хранить state в удалённом бекенде при командной работе.
Как перенести ресурс, созданный через GUI, в код Terraform?
Два пути:
terraform import/ import-blocks — импортируем существующий объект в state, а HCL пишем сами.- Генераторы кода (напр. Terraformer) — сразу получают и state, и HCL.
Классический способ (надёжный):
# 1) Инициализация
terraform init
# 2) Пишем минимальный ресурс в HCL (тип и имя)
resource "aws_s3_bucket" "logs" {}
# 3) Импортируем существующий объект по его ID
terraform import aws_s3_bucket.logs my-logs-bucket
# 4) Проверяем и дописываем конфиг до желаемого состояния
terraform planВ новых версиях можно использовать import blocks и затем
terraform plan/apply, чтобы заполнить state безterraform import.
Автогенерация кода:
- Terraformer (GCP/AWS/Azure/K8s и др.):
выгружает существующие ресурсы → генерирует*.tf+tfstate. Удобно для стартовой миграции. - Аналоги: Former2 (AWS, из CloudFormation/аккаунта), aztfy (Azure).
Замечания:
- Импорт не создаёт конфиг (если без генераторов) — HCL нужно привести к фактическому состоянию, иначе
planпокажет изменения. - Важно знать точный ID ресурса и тип (
aws_…,google_…,azurerm_…). - Зависимости (
depends_on, ссылки на VPC/SG и т.п.) придётся связать вручную или сгенерировать инструментом.
В чём отличие между condition (тернарным оператором) и lookup в Terraform?
Оба используются для выбора значений, но работают по-разному:
| Критерий | condition (тернарный оператор) | lookup() |
|---|---|---|
| Назначение | Условный выбор между двумя выражениями | Получение значения по ключу из map |
| Синтаксис | condition ? value_if_true : value_if_false | lookup(map, key, default) |
| Тип | Логический оператор (if-else) | Функция поиска |
| Пример | var.env == "prod" ? "10.0.0.1" : "10.0.0.2" | lookup(var.subnets, var.env, "default-subnet") |
| Ошибка при отсутствии значения | Нет, если условие не выполняется | Нет, если указан default |
| Когда использовать | Когда нужно выбрать между двумя вариантами | Когда нужно взять значение из map по ключу |
Кратко:
condition— это if-else в одну строку.lookup— это поиск значения по ключу в словаре (map) с возможностью указать значение по умолчанию.
Практические вопросы
Ты написал код в Terraform, выполнил terraform plan и вышел. После этого твой коллега добавил свой код, выполнил plan и тоже вышел. Теперь тебе нужно применить свою инфраструктуру — будут ли ошибки?
Нет, ошибок не будет, потому что команда terraform plan не вносит изменений в инфраструктуру и не модифицирует tfstate.
Проблемы могут возникнуть только, если вы оба одновременно выполняете apply или используете устаревший файл плана (plan -out), после того как кто-то изменил состояние.
Обычно перед apply делают новый plan, чтобы состояние было актуальным.
У вас есть 20 серверов, созданных с помощью Terraform, но вы хотите удалить только один из них. Можно ли уничтожить один ресурс, не внося изменения в конфигурационные файлы?
Да, можно. Для этого используется команда terraform destroy -target=<resource>,
которая удаляет только указанный ресурс из инфраструктуры, не трогая остальные.
Какие есть best practices для “ухода” за Terraform tfstate?
- Remote backend — хранить state удалённо (например, S3/GCS/Terraform Cloud) для совместной работы и безопасности.
- Блокировка (locking) — включить lock на время
apply(S3 + DynamoDB, Consul и т.п.), чтобы избежать конфликтов. - Ограничение доступа — минимум прав (PoLP), отдельные роли/аккаунты, доступ только авторизованным пользователям/CI.
- Бэкапы и версионирование — включить versioning и регулярные бэкапы, чтобы можно было восстановиться.
- Шифрование — at rest (KMS/SSE) и in transit (TLS).
- Изоляция стейтов — раздельные state для prod/stage/dev и для компонентов; не сваливать всё в один файл.
- Без секретов — не хранить чувствительные данные в state; использовать внешние секрет-менеджеры.
- Процесс применения —
planпередapply, по возможности применять только из CI/CD; избегать ручных правок state. - Аудит — включить логирование доступа к бэкенду и периодически проверять дрейф (
terraform plan).
У вас есть несколько сред — dev, stage, prod — и вы хотите использовать один и тот же код Terraform для всех. Как это сделать?
Используйте одни и те же модули, а различия между средами выносите в переменные. Организуйте проект так, чтобы конфиг каждой среды подставлял свои значения. Для продакшена обычно выбирают модули + отдельные переменные/конфиги на среду (часто через Terragrunt). Workspaces существуют, но редко рекомендуются для изоляции prod/stage/dev.
Подход 1: Модули + переменные (рекомендуется)
- Бизнес-логика инфраструктуры — в модуле.
- Для каждой среды — свой набор переменных (tfvars) и/или отдельный «обёрточный» слой.
- Изоляция по state и по учёткам (часто отдельный AWS account на среду).
Структура (вариант без Terragrunt):
infra/
├─ modules/
│ └─ vpc/
│ ├─ main.tf
│ ├─ variables.tf
│ └─ outputs.tf
└─ envs/
├─ dev/
│ ├─ main.tf # вызывает module "vpc"
│ └─ dev.tfvars # значения для dev
├─ stage/
│ ├─ main.tf
│ └─ stage.tfvars
└─ prod/
├─ main.tf
└─ prod.tfvarsПример env-конфига (prod/main.tf):
module "vpc_prod" {
source = "../../modules/vpc"
env = "prod"
cidr_block = "10.100.0.0/16"
public_subnets = ["10.100.12.0/24", "10.100.13.0/24", "10.100.14.0/24"]
private_subnets = ["10.100.21.0/24", "10.100.22.0/24"]
}Запуск:
cd infra/envs/prod
terraform init
terraform apply -var-file=prod.tfvarsИзоляция аккаунтов/ролей (AWS):
provider "aws" {
region = "eu-central-1"
assume_role {
role_arn = "arn:aws:iam::123456789012:role/RemoteAdministrator"
session_name = "terraform-session"
}
}Для каждой среды используйте свой backend (или свой key в S3) и свою роль/аккаунт.
Подход 2: Terraform Workspaces (обычно не для прод-изоляции)
- Один и тот же код, разные workspace → разные значения переменных и разные state в одном backend.
- Подходит для лёгких вариаций (dev/test), но для prod лучше отдельные каталоги/state/аккаунты.
Пример:
terraform workspace new dev
terraform workspace new stage
terraform workspace new prod
terraform workspace select prod
terraform apply -var-file=prod.tfvarsПоследнее обновление: 9 окт. 2025 г., 11:36:16