Terraform
Play ITЗагрузка интерактивного демо…
Terraform создаёт и меняет облачные ресурсы (серверы, сети, БД); настройку ОС и пакетов часто делают Ansible.
План и apply в CI — в GitHub Actions или GitLab CI (1.035 / 501).
Альтернатива на Python/TypeScript — Pulumi.
Terraform
Общее определение и назначение
Terraform — это программа, которая позволяет описать всю вашу инфраструктуру в текстовых файлах, а потом одной командой создать её в облаке или локально.
Представьте, что вы пишете рецепт:
— 1 сервер с 2 CPU и 4GB RAM
— 1 база данных PostgreSQL
— 1 сеть с диапазоном 10.0.0.0/16
— 1 firewall, который пускает только 80 и 443 порты
Разбор:
- Список — декларативное описание желаемого состояния инфраструктуры, а не пошаговых команд.
- ВМ с CPU/RAM, PostgreSQL, VPC
10.0.0.0/16и firewall — типичные зависимые ресурсы в облаке. - Terraform сам выстроит порядок создания (сеть → ВМ → БД → правила firewall) по графу зависимостей.
А потом говорите: "Сделайте мне это". И Terraform идёт в AWS/GCP/Azure/Proxmox/куда угодно и создаёт всё в точности по рецепту. Вы описываете что должно быть, а не как это сделать. Программа сама догадывается, что создавать первым, что вторым, что удалить, что изменить.
Например:
- для создания dev-окружения придётся полдня тыкаться;
- чтобы узнать, что сейчас в проде, надо дёргать сеньора;
- чтобы восстановить после падения дата-центра, надо молиться и плакать два дня;
- чтобы понять, что изменилось за последнюю неделю, надо сравнивать скриншоты;
- чтобы удалить всё и не оставить мусора, нужно долго копаться;
- а в команде из пяти человек кто последний, тот и правит консоль.
С Terraform инфраструктура превращается в код.
А код можно:
- выполнять командой;
- хранить в Git;
- проверять через код-ревью;
- откатывать;
- копировать;
- автоматически тестировать в CI.
Terraform — это инструмент с открытым исходным кодом, предназначенный для управления инфраструктурой как код. Он позволяет описывать целевые состояния облачных ресурсов, локальных систем или гибридных сред в декларативных конфигурационных файлах. Terraform читает эти файлы, строит граф зависимостей между ресурсами и применяет изменения таким образом, чтобы достичь описанного состояния. Этот подход обеспечивает воспроизводимость, предсказуемость и контроль над инфраструктурой.
Основная задача Terraform — унифицировать управление разнородными системами через единый интерфейс. Поддержка осуществляется через провайдеры — специализированные плагины, которые взаимодействуют с API конкретных платформ. Благодаря этому один и тот же рабочий процесс применим к AWS, Azure, Google Cloud, Kubernetes, базам данных, сетевому оборудованию и многим другим компонентам.
Для начала работы его нужно просто установить, добавить terraform в PATH и всё. Это единый бинарник, никаких серверов, баз данных и дополнительных зависимостей.
Ваш компьютер/CI-сервер
│
├── main.tf (ваши рецепты)
├── terraform.tfstate (файл с памяткой "что уже создано")
│
└── Terraform (один бинарник)
│
├── идёт в AWS API → создаёт EC2, S3, VPC...
├── идёт в GCP API → создаёт Compute Engine, Cloud SQL...
├── идёт в Kubernetes API → создаёт Pod, Service, Ingress...
└── идёт в Proxmox API → создаёт VM
Разбор:
main.tf— HCL-конфигурация "рецепта";terraform.tfstate— память о уже созданных ресурсах.- Бинарник Terraform на CI или ноутбуке вызывает API провайдеров (AWS, GCP, Kubernetes, Proxmox).
- Нет обязательного облачного SaaS от HashiCorp: управление идёт из вашего контура с вашими credentials.
Terraform просто запускается на вашей машине (или в CI), ходит по API туда, куда вы ему скажете, и делает ресурсы. Нет никакого облачного сервера HashiCorp, куда бы вы слали команды.
Есть локальный режим (для одного человека или учёбы):
terraform init # скачать плагины провайдеров
terraform plan # посмотреть, что будет сделано
terraform apply # сделать это
terraform destroy # всё удалить
Разбор:
terraform init— скачивает провайдеры и настраивает backend для каталога.terraform plan— diff "текущее состояние ↔ желаемый HCL" без изменений в облаке.terraform apply— применяет план (создание/изменение/удаление ресурсов).terraform destroy— удаляет все ресурсы, описанные в конфигурации и учтённые в state.- Локальный
tfstateрядом с кодом подходит для учёбы; в команде state выносят в S3 с блокировкой.
Файл состояния terraform.tfstate лежит рядом с кодом. Так делают новички.
А есть удалённое состояние, для команды. Файл состояния хранится в общем месте (S3 bucket, GCS bucket, Consul, PostgreSQL, HTTP backend). Тогда все члены команды видят одно и то же состояние, и Terraform не даст двоим одновременно применить изменения.
# backend.tf
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "production/network/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-locks"
}
}
Разбор:
- Блок
terraform { backend "s3" { ... } }— удалённое хранение state в бакете S3. key— путь к файлу state для конкретного стека (например, сеть prod).dynamodb_table— таблица для блокировки: дваapplyодновременно не испортят state.region— регион AWS для бакета и DynamoDB.
Тогда, при выполнении команды apply Terraform:
- Берёт блокировку в DynamoDB
- Загружает текущее состояние из S3
- Вычисляет изменения
- Применяет через API
- Сохраняет новое состояние обратно в S3
- Отпускает блокировку
Всё идёт через CI:
Git push в main
│
▼
GitLab CI / GitHub Actions / Jenkins
│
├── terraform init
├── terraform plan -out=tfplan
├── (человек ревьюит план в pull request)
├── terraform apply tfplan
└── записывает состояние обратно в S3
Разбор:
Git push— триггер pipeline; изменения IaC проходят review как код приложения.terraform plan -out=tfplan— артефакт плана для согласования в PR.- Ревью человеком — gate перед
applyв shared-аккаунте. terraform apply tfplan— применяется ровно согласованный план, без сюрпризов.- Обновление state в S3 — единый источник правды для всей команды.
Минимальные правила безопасной работы с Terraform в команде:
planвыполняется в каждом pull request,applyтолько после ревью.- State хранится удалённо и с блокировкой.
- В
mainне допускаются ручныеapplyс локальной машины без runbook. - Любое изменение провайдера или модуля фиксируется по версии и проходит тест в staging.
Архитектурные принципы
Декларативный стиль описания
Конфигурации Terraform написаны на языке HCL (HashiCorp Configuration Language) или в формате JSON. В них пользователь указывает желаемое состояние системы, а не последовательность команд для его достижения. Например, вместо "создай виртуальную машину, затем настрой сеть, потом подключи диск" пишется: "должна существовать виртуальная машина с такими параметрами, сетевой интерфейс с такими настройками и диск такого объёма". Terraform сам определяет порядок действий на основе зависимостей.
Пример простого описания:
resource "aws_instance" "web" {
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.micro"
}
Разбор:
resource "aws_instance" "web"— объявление ресурса типа EC2 с локальным именемwebв state.ami— идентификатор образа ОС в AWS.instance_type— размер ВМ (t3.micro— малый burstable инстанс).- Terraform сравнит блок с state и API: создаст, изменит тип или удалит при
destroy.
Этот блок говорит: "в облаке AWS должна существовать EC2-инстанция с указанным AMI и типом". Terraform проверит текущее состояние, сравнит его с описанием и при необходимости создаст, изменит или удалит ресурс.
Иммутабельность и идемпотентность
Terraform стремится к идемпотентности: повторное применение одной и той же конфигурации не приводит к побочным эффектам. Если ресурс уже соответствует описанию, он остаётся без изменений. Если параметры отличаются, Terraform заменяет ресурс или обновляет его, в зависимости от возможностей провайдера.
Многие ресурсы в облачных средах являются иммутабельными: их нельзя изменить после создания. В таких случаях Terraform создаёт новый экземпляр с новыми параметрами и удаляет старый. Это снижает риск частичных обновлений и несогласованности.
Граф зависимостей
При загрузке конфигурации Terraform строит ориентированный ациклический граф, где узлы — это ресурсы, а рёбра — зависимости между ними. Зависимости могут быть явными (через ссылки на атрибуты других ресурсов) или неявными (через использование depends_on).
Пример явной зависимости:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
}
Разбор:
aws_vpc.main— сначала в графе создаётся VPC с CIDR10.0.0.0/16.aws_subnet.publicсvpc_id = aws_vpc.main.id— явная зависимость: подсеть без VPC не создаётся.cidr_blockподсети — поддиапазон внутри VPC.- Terraform выполняет create/update в порядке DAG, подставляя
idVPC после её создания.
Здесь подсеть зависит от VPC, потому что использует её идентификатор. Terraform сначала создаст VPC, дождётся его появления, получит id, а затем создаст подсеть. Такой порядок гарантирует корректность развёртывания.
Состояние (State)
Назначение состояния
Файл состояния (обычно terraform.tfstate) хранит метаданные о текущем состоянии управляемых ресурсов. Он содержит идентификаторы, атрибуты и связи между ресурсами в реальной инфраструктуре. Этот файл позволяет Terraform понимать, какие ресурсы уже созданы, и сравнивать их с описанием в конфигурации.
Без состояния Terraform не смог бы отличить отсутствие ресурса от его наличия. Например, если в конфигурации описан бакет S3, но в облаке его нет, Terraform создаст его. Если бакет уже существует и совпадает с описанием — ничего не произойдёт. Состояние делает этот процесс возможным.
State — приватный API Terraform
Файл terraform.tfstate — JSON-связка между адресами в HCL (aws_instance.web) и реальными ID в облаке (i-0abc…). Вывод terraform plan — расхождение кода на диске и инфраструктуры в мире (по ID из state).
Формат state меняется между версиями Terraform и предназначен только для движка. Для переноса ресурсов используйте terraform import, блок moved, команды terraform state mv/rm — см. справочник.
В командной работе локальный state создаёт три проблемы:
- Общий источник правды — у всех должна быть одна актуальная копия state.
- Блокировка — два одновременных
applyбез lock могут повредить state. - Изоляция окружений — правки staging не должны затрагивать prod.
State нельзя коммитить в Git по тем же причинам, что и секреты: в state часто лежат пароли БД и ключи в открытом виде; клон репозитория размножает копии на каждой машине и CI-runner.
Локальное и удалённое хранение
По умолчанию состояние сохраняется локально в файловой системе. Это удобно для экспериментов, но неприемлемо в командной работе. При совместном использовании конфигураций несколько человек могут одновременно запустить apply, что приведёт к конфликтам и повреждению инфраструктуры.
Для решения этой проблемы Terraform поддерживает удалённое хранение состояния через бэкенды. Наиболее распространённые варианты — Amazon S3, Azure Blob Storage, Google Cloud Storage, HashiCorp Consul. Удалённое состояние автоматически блокируется во время применения, предотвращая параллельные изменения.
Пример настройки бэкенда для S3:
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "prod/web/terraform.tfstate"
region = "us-west-2"
}
}
Разбор:
- Повтор настройки
backend "s3"— state в бакетеmy-terraform-state-bucket, ключprod/web/terraform.tfstate. - Отдельный
keyна стек — несколько окружений/компонентов не делят один файл state. region— где физически лежит state (не путать с регионом ресурсов вprovider).
Такая конфигурация перенаправляет все операции со состоянием в указанный бакет. Terraform будет использовать его как единственный источник правды о текущей инфраструктуре.
Bootstrap backend (отдельный стек, один раз): справочник Terraform — S3 и DynamoDB.
Workspaces и изоляция окружений
Workspaces (terraform workspace select prod) — несколько state-файлов на одном backend при одном и том же HCL. Удобно для быстрых экспериментов; имя workspace доступно как terraform.workspace.
Для prod чаще выбирают отдельные каталоги и ключи state:
live/
├── stage/services/webserver-cluster/
│ ├── main.tf # module "webserver" { ... }
│ └── backend.tf # key = "stage/webserver/terraform.tfstate"
└── prod/services/webserver-cluster/
├── main.tf
└── backend.tf # key = "prod/webserver/terraform.tfstate"
modules/
└── services/webserver-cluster/ # переиспользуемый код
Разбор:
- Один модуль в
modules/— stage и prod вызывают его с разными.tfvars. - Разный
keyв backend — изоляция state:applyв stage не трогает prod. - Layout
modules/+live/— распространённая практика в крупных IaC-репозиториях; подробнее в Модули и структура репозитория.
Чувствительность состояния
Файл состояния может содержать конфиденциальные данные — пароли, ключи доступа, IP-адреса. По этой причине его нельзя хранить в открытых репозиториях. Terraform предоставляет механизмы для шифрования состояния (например, через AWS KMS при использовании S3) и исключения чувствительных полей из вывода (sensitive = true в переменных).
Провайдеры
Роль провайдеров
Провайдер — это плагин, который реализует взаимодействие с API конкретной платформы. Terraform не содержит встроенного кода для работы с AWS или Kubernetes. Вместо этого он загружает нужные провайдеры динамически и вызывает их функции для создания, чтения, обновления и удаления ресурсов (операции CRUD).
Каждый провайдер определяет набор ресурсов и источников данных (Данные sources). Ресурсы управляются Terraform, источники данных только читаются. Например, можно запросить информацию о существующем образе ОС без попытки его изменения.
Настройка и версионирование
Провайдеры требуют конфигурации — учётные данные, регион, endpoint и другие параметры. Эта информация передаётся в блоке provider.
Пример:
provider "aws" {
region = "eu-central-1"
profile = "production"
}
Разбор:
provider "aws"— подключает AWS-провайдер для всех ресурсовaws_*в каталоге.region— регион API по умолчанию (напримерeu-central-1).profile— именованный профиль из~/.aws/credentials(разделение prod/dev на одной машине).
Важно фиксировать версии провайдеров. Новые версии могут вносить несовместимые изменения. Terraform позволяет указать допустимый диапазон версий:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
Разбор:
required_providers— объявляет обязательные плагины и их версии.source = "hashicorp/aws"— адрес модуля в Terraform Registry.version = "~> 5.0"— допустимы патч- и минор-обновления 5.x, но не 6.0 (семантическое ограничение).terraform initскачает версию, зафиксированную в.terraform.lock.hcl.
Это гарантирует, что все участники проекта используют одинаковую версию провайдера, избегая расхождений в поведении.
Модули
Композиция и переиспользование
Модуль — это пакет конфигураций Terraform, который можно использовать как единый блок. Любой каталог с .tf-файлами является модулем, но термин обычно применяют к переиспользуемым компонентам. Модули позволяют инкапсулировать сложные паттерны — например, "полностью настроенная веб-инфраструктура с балансировщиком, группой инстансов и мониторингом".
Модуль принимает входные переменные и возвращает выходные значения. Это делает его похожим на функцию в программировании.
Пример вызова модуля:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["eu-central-1a", "eu-central-1b"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
}
Разбор:
module "vpc"— вызов переиспользуемого модуля вместо десятков ресурсов вручную.source+version— пин версии модуля из Registry (воспроизводимые сборки).name,cidr,azs,public_subnets— входные параметры модуля (интерфейс "функции").- Внутри модуля создаются VPC, подсети, route tables — скрытая сложность инкапсулирована.
Этот вызов использует официальный модуль от сообщества AWS, который создаёт полноценную VPC с подсетями, таблицами маршрутизации, шлюзами и другими компонентами. Без модуля пришлось бы писать десятки блоков вручную.
Иерархия и организация
Проекты на Terraform часто организуются в виде дерева модулей. Корневой модуль вызывает дочерние, те — свои, и так далее. Такая структура упрощает поддержку: изменения в одном компоненте не затрагивают другие, если интерфейс остаётся неизменным.
Хорошая практика — выделять модули по доменным границам — сеть, вычисления, хранилище, безопасность. Это соответствует принципам разделения ответственности и упрощает тестирование.
Жизненный цикл управления
Инициализация
Команда terraform init подготавливает рабочий каталог. Она загружает указанные провайдеры, инициализирует бэкенд состояния и устанавливает зависимости модулей. Эта команда безопасна для повторного запуска и должна выполняться при каждом клонировании репозитория или смене окружения.
terraform init
Разбор:
- Подготавливает каталог — провайдеры, модули, backend state.
- Запускают после
git cloneи при сменеrequired_providersилиbackend.
Планирование
Команда terraform plan анализирует конфигурацию и текущее состояние, строит план изменений и выводит его в человекочитаемом виде. План показывает, какие ресурсы будут созданы, изменены или удалены, и почему. Это ключевой этап для проверки перед применением.
terraform plan
terraform plan -out=plan.out
Разбор:
terraform plan— показывает diff без изменений в облаке.-out=plan.out— сохраняет бинарный план для согласованногоapply(типично в CI).- В логе видно
+ create,~ update,- destroyпо каждому ресурсу.
План можно сохранить в файл и передать на утверждение. Это особенно полезно в CI/CD-конвейерах, где человек должен подтвердить изменения до их выполнения.
Применение
Команда terraform apply выполняет ранее сгенерированный план. Она отправляет запросы к API провайдеров, дожидается завершения операций и обновляет файл состояния. Все действия атомарны — либо весь план применяется успешно, либо возвращается ошибка, и состояние остаётся прежним.
terraform apply
terraform apply plan.out
Разбор:
terraform applyбез файла — интерактивное подтверждение плана (локально).terraform apply plan.out— применяет ровно сохранённый план (без расхождения с ревью).- После успеха обновляется
terraform.tfstate(локально или в S3).
Если во время применения происходит сбой (например, недоступен API), Terraform помечает ресурс как "зависший" и предлагает восстановить состояние вручную. Это редкая, но важная ситуация, требующая внимания оператора.
Уничтожение
Команда terraform destroy удаляет все управляемые ресурсы. Она строит план, аналогичный plan, но с единственной целью — полное уничтожение. Эта команда используется при завершении проекта или очистке тестовых окружений.
terraform destroy
Разбор:
- Удаляет все ресурсы, управляемые текущей конфигурацией и записанные в state.
- Используют для teardown dev/staging; в prod — с
prevent_destroyи отдельным approve. - Необратимо для данных без бэкапов вне Terraform.
Уничтожение следует выполнять осторожно: оно необратимо. Для защиты от случайного удаления можно использовать механизм prevent_destroy в настройках ресурса.
Переменные и выходные значения
Гибкость конфигураций
Переменные позволяют параметризовать конфигурации. Вместо жёстко заданных значений используются символические имена, которым присваиваются конкретные данные при запуске.
Объявление переменной:
variable "instance_count" {
description = "Number of EC2 instances to launch"
type = number
default = 2
}
Разбор:
variable— входной параметр модуля/корня.type = number— строгая типизация HCL.default = 2— значение, если не передали в.tfvarsили-var.
Использование:
resource "aws_instance" "app" {
count = var.instance_count
# ...
}
Разбор:
count = var.instance_count— создаёт N однотипных ресурсов (индексыaws_instance.app[0]…).var.instance_count— ссылка на объявленную переменную.- Один HCL-шаблон для dev (count=1) и prod (count=10) через разные
.tfvars.
Такой подход делает конфигурацию универсальной — одну и ту же структуру можно использовать в dev, staging и prod, меняя только значения переменных.
Типизация и валидация
Terraform поддерживает строгую типизацию переменных — строки, числа, булевы значения, списки, карты, объекты, кортежи. Можно задавать пользовательские типы и правила валидации.
Пример валидации:
variable "region" {
type = string
validation {
condition = contains(["us-east-1", "eu-west-1", "ap-southeast-1"], var.region)
error_message = "Region must be one of the approved locations."
}
}
Разбор:
validation { condition = ... }— проверка доplan; неверный регион остановит Terraform.contains([...], var.region)— регион должен быть из белого списка.error_message— текст ошибки для CI и разработчика.
Это предотвращает ошибки на раннем этапе и обеспечивает соответствие политике безопасности.
Выходные значения
Выходные значения экспортируют информацию из модуля или корневой конфигурации. Они полезны для передачи данных между модулями или для отображения итоговых параметров после применения.
Пример:
output "vpc_id" {
value = aws_vpc.main.id
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
}
Разбор:
output— экспорт значений послеapply(в консоль и для-json).vpc_id— ID VPC для передачи в другой модуль или Ansible.aws_subnet.public[*].id— splat: список ID всех подсетей из ресурса сcount.
Эти значения появятся в выводе apply и могут использоваться внешними системами, например, для настройки CI/CD-пайплайнов.
Безопасность и управление доступом
Учётные данные и секреты
Terraform требует учётных данных для взаимодействия с API провайдеров. Эти данные могут включать ключи доступа, токены, сертификаты и другие формы аутентификации. Хранение таких данных в открытом виде в конфигурационных файлах недопустимо. Terraform предоставляет несколько механизмов для безопасной передачи секретов:
- Переменные окружения: большинство провайдеров автоматически читают значения из переменных вроде
AWS_ACCESS_KEY_ID,AZURE_CLIENT_SECRETи подобных. - Внешние источники — можно использовать специализированные инструменты — HashiCorp Vault, AWS Secrets Manager, Azure Key Vault — и запрашивать секреты через
Данные-блоки или внешние модули. - Локальные файлы с исключением из контроля версий: например,
secrets.auto.tfvarsдобавляется в.gitignore.
Пример безопасного использования:
provider "aws" {
region = "us-east-1"
# credentials не указаны явно — будут взяты из ~/.aws/credentials или ENV
}
Разбор:
- Провайдер без
access_keyв HCL — ключи из переменных окружения или IAM role на CI runner. region— единственный явный параметр в примере.- Секреты не попадают в Git и в plan-логи в открытом виде.
Явное указание секретов в коде считается грубой ошибкой, даже если репозиторий приватный.
Принцип минимальных привилегий
Учётные данные, используемые Terraform, должны обладать только теми правами, которые необходимы для выполнения задач. Например, если конфигурация создаёт только EC2-инстансы и VPC, то IAM-роль не должна иметь разрешений на удаление S3-бакетов или изменение IAM-политик. Это снижает потенциальный ущерб при компрометации состояния или плана.
Провайдеры часто предоставляют возможность ограничить действия через политики. В AWS это делается через IAM; в Azure — через RBAC и управляемые идентификаторы. Terraform сам по себе не проверяет полномочия — он лишь передаёт запросы, поэтому ответственность за безопасность лежит на администраторе.
Защита состояния
Файл состояния может содержать чувствительные данные — IP-адреса, DNS-имена, пароли баз данных (если они не помечены как sensitive). Чтобы минимизировать риски:
- Используется шифрование на уровне бэкенда (например, AWS KMS для S3).
- Включается опция
encrypt = trueв конфигурации бэкенда. - Все чувствительные выходные значения помечаются как
sensitive = true, что скрывает их из выводаapplyиplan.
Пример:
output "db_password" {
value = aws_db_instance.main.password
sensitive = true
}
Разбор:
outputс паролем БД — удобно для передачи в CI, но опасно в логах.sensitive = true— Terraform скрывает значение в выводеplan/apply.- Пароль всё равно может быть в
tfstate— state шифруют и ограничивают доступ.
Это не предотвращает сохранение значения в состоянии, но защищает от случайного раскрытия в логах.
Расширенные возможности языка HCL
Локальные значения
Локальные значения (locals) позволяют вычислять производные данные внутри модуля без необходимости объявлять их как переменные или выходы. Это упрощает чтение и поддержку сложных выражений.
Пример:
locals {
common_tags = {
Environment = var.env
Owner = "dev-team"
ManagedBy = "terraform"
}
}
resource "aws_instance" "web" {
tags = merge(local.common_tags, { Name = "web-server" })
}
Разбор:
locals— вычисляемые значения внутри модуля (не inputs/outputs).common_tags— единый набор меток для compliance и cost allocation.merge(local.common_tags, { Name = ... })— объединение карт тегов для конкретного ресурса.
Такой подход централизует общую логику и избегает дублирования.
Условные выражения и циклы
HCL поддерживает условную логику через тернарный оператор и функции вроде can(), try(). Циклы реализуются через мета-аргумент for_each и count.
Пример с for_each:
Код ITЗагрузка примера кода…
Разбор:
for_each = var.subnets— по одному ресурсу на ключ карты (public_a,public_b).each.value— CIDR подсети;each.key— имя/AZ в примере.- Добавление ключа в
var.subnetsсоздаёт новую подсеть без копипасты блоковresource.
Этот подход масштабируется: добавление нового элемента в subnets автоматически создаст новую подсеть без изменения структуры кода.
count и for_each — когда что
| Механизм | Когда использовать | Подводный камень |
|---|---|---|
count = N | N однотипных ресурсов по индексу | Вставка элемента в середину списка сдвигает индексы → Terraform пересоздаёт ресурсы |
for_each = map/set | Ресурсы с стабильным ключом (имя пользователя, AZ) | Ключи должны быть известны на этапе plan |
for в locals | Вычислить список/map до resource | — |
Пример: три IAM-пользователя — предпочтительнее map, а не count:
variable "user_names" {
type = set(string)
default = ["neo", "trinity", "morpheus"]
}
resource "aws_iam_user" "example" {
for_each = var.user_names
name = each.key
}
Разбор:
for_each = var.user_names— адрес в stateaws_iam_user.example["neo"], не зависит от порядка в set.- Добавление
"smith"создаёт одного пользователя; удаление ключа из set — destroy только его. - С
countпереименование или reorder списка часто даёт-/+(destroy + create) в plan.
Развёртывание без простоя — create_before_destroy
Некоторые атрибуты ресурса immutable (тип EC2, subnet CIDR). Terraform заменяет ресурс: сначала destroy, потом create — с простоем. Блок lifecycle меняет порядок:
resource "aws_instance" "web" {
ami = var.ami
instance_type = var.instance_type
lifecycle {
create_before_destroy = true
}
}
Разбор:
- Сначала создаётся новый инстанс с новым AMI/типом, затем удаляется старый.
- Работает, когда зависимости (ALB, ASG) допускают временное дублирование.
- Для ASG/ALB zero-downtime нужна согласованная схема (rolling update, health checks) — один
lifecycleна ресурсе не гарантирует бесшовность всего стека.
Функции и выражения
HCL включает богатую библиотеку встроенных функций — работа со строками (lower, replace), списками (concat, distinct), картами (lookup, merge), преобразование типов (jsonencode, yamldecode) и другие. Это позволяет строить динамические конфигурации без внешних скриптов.
Пример:
locals {
instance_names = [for i in range(var.instance_count) : "app-${i}"]
}
Разбор:
- List comprehension в HCL:
[for i in range(n) : expr]. range(var.instance_count)— 0..n-1 для имёнapp-0,app-1, …- Результат — список строк в
local.instance_namesдляcountилиfor_each.
Такие конструкции делают код гибким и адаптивным к изменяющимся требованиям.
Интеграция с CI/CD и автоматизация
Рабочие процессы
Terraform хорошо встраивается в CI/CD (GitHub Actions, GitLab CI, Jenkins). Типичный пайплайн:
terraform init
terraform validate
terraform fmt -check
terraform plan -out=tfplan
# утверждение плана
terraform apply tfplan
Разбор:
validate— синтаксис и ссылки в HCL без облака.fmt -check— стиль форматирования (часто gate в CI).plan -out=tfplan→ approve →apply tfplan— безопасный IaC-цикл в команде.
Блокировки и совместная работа
При использовании удалённого состояния Terraform автоматически блокирует файл состояния во время apply. Это предотвращает одновременные изменения от нескольких пользователей или пайплайнов. Если блокировка не поддерживается бэкендом (например, при использовании локального файла), возможны конфликты и повреждение состояния.
Некоторые бэкенды, такие как Consul или DynamoDB, обеспечивают надёжную блокировку через атомарные операции. Это критически важно в production-средах.
Мониторинг и уведомления
Результаты plan и apply можно направлять в системы мониторинга — Slack, Microsoft Teams, Email, Prometheus. Это позволяет команде отслеживать изменения в реальном времени. Например, после каждого apply в канал DevOps отправляется сообщение с перечнем изменённых ресурсов и ссылкой на коммит.
Версионирование и эволюция инфраструктуры
Управление версиями модулей
Модули могут быть опубликованы в реестрах (Terraform Registry, внутренние Git-репозитории) и использоваться с указанием конкретной версии. Это позволяет:
- Изолировать изменения: обновление модуля в одном проекте не затрагивает другие.
- Проводить тестирование: новая версия проверяется в staging перед переходом в prod.
- Соблюдать семантическое версионирование:
~> 2.1означает "любая версия >= 2.1.0, но < 3.0.0".
Пример:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.0.0"
# ...
}
Разбор:
- Пин
version = "20.0.0"модуля EKS — фиксированное поведение кластера. - Обновление модуля — осознанный PR с
planна staging, не случайныйinit -upgradeв проде.
Это гарантирует стабильность и предсказуемость.
Миграция и рефакторинг
Со временем инфраструктура меняется: появляются новые требования, устаревают старые компоненты. Terraform поддерживает миграцию через:
moved-блоки: позволяют переименовать или переместить ресурс без его уничтожения.- Импорт существующих ресурсов — связать уже созданный объект с конфигурацией:
terraform import aws_instance.web i-0abc123def456
Разбор:
-
terraform import— привязывает существующий ресурс в облаке к адресу в state (aws_instance.web). -
ID
i-0abc123def456— реальный идентификатор EC2 из AWS. -
После import следующий
planпокажет только drift относительно HCL. -
Ручное редактирование состояния (с осторожностью):
terraform state mv aws_instance.old_name aws_instance.new_name
terraform state rm aws_instance.orphan
Разбор:
state mv— переименование в state без destroy/create в облаке.state rm— убрать "осиротевшую" запись из state (ресурс в облаке остаётся — осторожно).- Используют при рефакторинге имён ресурсов, если
movedнедостаточно.
Пример moved:
moved {
from = aws_instance.old_name
to = aws_instance.new_name
}
Разбор:
- Блок
moved(Terraform 1.1+) — декларативный перенос адреса ресурса в state. planне предложит пересоздать EC2 при смене имени в HCL.- Предпочтительнее ручного
state mv— видно в коде и в review.
После этого plan покажет, что ресурс не будет удалён и создан заново, а просто переименован в состоянии.
Поддержка мультиоблачных и гибридных сред
Terraform одинаково эффективно управляет ресурсами в нескольких облаках одновременно. Один и тот же рабочий процесс применяется к AWS, Azure, GCP, Yandex Cloud, OpenStack и другим платформам. Это особенно ценно для организаций, использующих стратегию multi-cloud или hybrid-cloud.
Пример:
Код ITЗагрузка примера кода…
Разбор:
- Два блока
provider— один root-модуль управляет AWS и Azure одновременно. aws_s3_bucket— бакет логов в AWS;azurerm_storage_account— учётная запись хранения в Azure.- У каждого провайдера свои credentials и регион;
plan/applyходят в оба API из одного pipeline.
Такая конфигурация создаёт ресурсы в двух разных облаках, но управляется единым инструментом. Это упрощает обучение, стандартизацию и аудит.
Экосистема и сообщество
Официальные и сторонние модули
HashiCorp поддерживает набор официальных провайдеров и модулей, прошедших внутреннюю проверку. Они доступны в Terraform Registry — централизованном каталоге, где можно найти готовые решения для AWS, Azure, Google Cloud, Kubernetes и других платформ. Эти модули часто соответствуют лучшим практикам — используют версионирование, имеют документацию, примеры и тесты.
Сообщество также создаёт тысячи сторонних модулей. Они могут быть полезны для нишевых задач или новых технологий, но требуют осторожности: не все из них поддерживаются регулярно, некоторые содержат уязвимости или устаревшие подходы. Перед использованием рекомендуется проверить:
- Дату последнего обновления
- Количество звёзд и форков на GitHub
- Наличие issue-трекера и активность автора
- Соответствие текущей версии Terraform
Пример вызова популярного модуля:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = "production-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
}
Разбор:
- Повтор вызова community-модуля VPC с пином
version = "5.0.0". production-vpcи подсети вus-east-1— типичный prod-стек сети из Registry вместо ручного HCL.- Обновление модуля — отдельный PR с diff плана по десяткам вложенных ресурсов.
Такой модуль заменяет десятки строк ручной конфигурации и гарантирует соответствие рекомендациям AWS.
Инструменты расширения
Вокруг Terraform сложилась богатая экосистема инструментов:
- Terragrunt — надстройка для управления повторяющимися конфигурациями через DRY-принцип (Don’t Repeat Yourself). Позволяет выносить общие настройки вверх по иерархии каталогов.
- TFLint — линтер, проверяющий стиль, безопасность и соответствие политике.
- TFSec — сканер уязвимостей в конфигурациях (например, открытые порты, отсутствие шифрования).
- Checkov — инструмент от Bridgecrew для проверки соответствия стандартам безопасности и compliance (CIS, PCI-DSS, HIPAA).
- Atlantis — система автоматизации
planиapplyчерез pull request в Git.
Эти инструменты интегрируются в CI/CD и повышают надёжность развёртываний.
Ограничения и сценарии, неподходящие для Terraform
Императивные задачи
Terraform плохо подходит для задач, требующих последовательного выполнения шагов с условиями, циклами и логикой принятия решений в реальном времени. Например, "если сервис не отвечает, перезапусти его три раза, а потом отправь уведомление" — это задача для скриптов, Ansible или систем мониторинга, а не для Terraform.
Terraform управляет состоянием, а не процессами. Он не следит за работоспособностью ресурсов после их создания. Для этого нужны дополнительные инструменты — Prometheus, Datadog, CloudWatch.
Частые изменения состояния
Если инфраструктура меняется каждые несколько минут (например, в средах с высокой динамикой, таких как serverless-функции с переменной нагрузкой), использование Terraform может привести к конфликтам состояний и задержкам. В таких случаях лучше применять облачные нативные решения — AWS SAM, Azure Bicep, Google Cloud Deployment Manager.
Отсутствие обратной связи
Terraform не получает информацию о том, что произошло с ресурсом после его создания. Если кто-то вручную изменит параметры EC2-инстанса в консоли AWS, Terraform не узнает об этом до следующего plan. При следующем применении он вернёт ресурс к описанному состоянию, что может нарушить работу системы. Это называется "дрейфом конфигурации" (configuration drift).
Для борьбы с дрейфом используются:
- Запрет ручных изменений через IAM-политики
- Регулярные
planв CI/CD - Инструменты вроде Driftctl, которые сравнивают состояние в облаке и в Terraform
Сравнение с другими инструментами управления инфраструктурой
Ansible
Ansible — императивный инструмент, ориентированный на конфигурацию серверов и приложений. Он выполняет команды по порядку — "установи пакет, запусти службу, скопируй файл". Terraform декларативен и управляет самими ресурсами: "должен существовать сервер с этим ПО".
Ansible хорошо работает внутри уже созданных машин, Terraform — на уровне их создания. Часто они используются вместе: Terraform поднимает инфраструктуру, Ansible настраивает софт.
CloudFormation / ARM / Bicep
Эти инструменты являются нативными для конкретных облаков — AWS CloudFormation, Azure Resource Manager (ARM), Google Deployment Manager. Они тесно интегрированы с API и поддерживают все новые функции сразу. Однако они не переносимы между облаками.
Terraform обеспечивает унификацию: один язык, один рабочий процесс для всех платформ. Это особенно ценно в multi-cloud-средах.
Pulumi
Pulumi использует настоящие языки программирования (Python, TypeScript, Go) вместо HCL. Это даёт больше гибкости — можно писать циклы, классы, использовать библиотеки. Однако это усложняет чтение и аудит: код становится менее декларативным и более процедурным.
Terraform остаётся проще для команд, где не все участники — разработчики. HCL легко читается даже системными администраторами без опыта программирования.
Поддержка и развитие
Terraform активно развивается компанией HashiCorp. Новые версии выходят регулярно и включают:
- Поддержку новых API облачных провайдеров
- Улучшения производительности графа зависимостей
- Расширение возможностей языка HCL
- Усиление безопасности (например, встроенный анализ чувствительных данных)
Проект имеет открытый исходный код (Mozilla Public License 2.0), что позволяет сообществу вносить вклад, проверять код и форкать при необходимости. В 2023 году HashiCorp изменила лицензию на Business Source License (BSL), но основной функционал остаётся свободным для большинства пользователей.
Развитие движется в сторону:
- Более глубокой интеграции с CI/CD
- Улучшения работы с большими конфигурациями (модульные зависимости, частичное применение)
- Поддержки policy-as-code через Sentinel (в Enterprise-версии) или Open Policy Agent (в open-source)
Эти направления делают Terraform всё более зрелым инструментом для enterprise-сред.
См. также
- Terraform — практический путь — EC2 → ALB по шагам.
- Модули и структура репозитория —
modules/+live/. - Тестирование Terraform — Terratest и CI.
- Terraform в команде — golden rule и пайплайны.
- Pulumi — IaC на языках программирования.
- Автоматизация сборки и развёртывания — Ansible после Terraform, PITR.
- Инструменты оркестрации — CI/CD, мониторинг, сравнение с CloudFormation.
- Облачные технологии — провайдеры и модели сервисов.
- Аутентификация в CI/CD — секреты провайдеров и state backend.
- Контейнеризация — о разделе — провайдер Kubernetes в Terraform.
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.