Представьте, что вы настраиваете облако руками: заходите в консоль AWS, кликаете «создать сервер», «создать хранилище», «открыть порт». Работает — пока серверов мало. Когда их десятки, а окружений несколько (тестовое, боевое), руками это уже не повторить одинаково: где-то забыли галочку, где-то регион не тот. Terraform решает эту боль: вы описываете нужную инфраструктуру текстом в файле, а Terraform сам создаёт её в облаке — одинаково, сколько угодно раз.
Terraform — это инструмент инфраструктуры как кода (infrastructure as code): вместо кликов вы пишете конфигурацию, кладёте её в git рядом с приложением и применяете командой. Его главная фишка — он работает почти с любым облаком (AWS, Google Cloud, Azure) и сотнями других систем через единый язык. Если вы только знакомитесь с тем, зачем всё это нужно, начните со статьи инфраструктура как код: основы. Здесь — про сам Terraform, с примерами на AWS.
Что такое Terraform и HCL
Terraform читает файлы с расширением .tf, написанные на языке HCL (HashiCorp Configuration Language) — это собственный язык конфигурации Terraform. HCL устроен просто: вся конфигурация состоит из блоков. Блок — это тип, опциональные метки в кавычках и тело в фигурных скобках.
тип_блока "метка1" "метка2" {
аргумент = значение
}
Главное, что нужно понять новичку: HCL — декларативный. Вы описываете не «как делать» (создай, потом измени, потом удали), а «что должно быть в итоге». Terraform сам вычисляет разницу между тем, что описано, и тем, что уже есть в облаке, и делает только нужные шаги. Это как список покупок: вы пишете, что должно лежать в холодильнике, а не маршрут по магазину.
Любой проект на Terraform обычно начинается с блока terraform {}, где указано, какие провайдеры и какая версия нужны:
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
}
}
Провайдеры
Сам по себе Terraform не знает, что такое «сервер AWS» или «бакет S3». За это отвечают провайдеры — плагины, которые переводят ваши блоки в вызовы API конкретного облака. Провайдер AWS умеет работать с AWS, провайдер Google Cloud — с Google, и так далее. Аналогия: Terraform — это пульт, а провайдер — переходник под конкретную розетку.
Провайдер настраивается отдельным блоком provider. Для AWS чаще всего достаточно указать регион:
provider "aws" {
region = "us-east-1"
}
Один и тот же провайдер можно подключить несколько раз — например, чтобы создавать ресурсы в двух регионах. Для этого второму экземпляру дают псевдоним через alias:
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "west"
region = "us-west-2"
}
Какие именно ресурсы AWS бывают и зачем они — это отдельная большая тема, начните с обзора основы AWS.
Ресурсы и зависимости
Ресурс — это конкретный объект инфраструктуры: бакет хранилища, виртуальный сервер, таблица базы данных. Описывается блоком resource с двумя метками: тип ресурса и ваше внутреннее имя (по нему вы будете ссылаться на ресурс внутри конфигурации).
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-company-app-data-2026"
tags = {
Environment = "production"
Team = "backend"
}
}
Здесь aws_s3_bucket — тип (бакет в объектном хранилище S3), my_bucket — имя для ссылок внутри кода, bucket — реальное имя бакета в AWS.
Часто один ресурс зависит от другого: например, политику доступа нельзя создать, пока нет самого бакета. Terraform определяет такие зависимости автоматически — по ссылкам. Когда вы упоминаете один ресурс внутри другого в формате тип.имя.атрибут, Terraform понимает, что сначала надо создать первый:
resource "aws_s3_bucket" "my_bucket" {
bucket = "my-company-app-data-2026"
}
resource "aws_s3_bucket_versioning" "versioning" {
bucket = aws_s3_bucket.my_bucket.id
versioning_configuration {
status = "Enabled"
}
}
Ссылка aws_s3_bucket.my_bucket.id означает «возьми id бакета my_bucket» — и заодно говорит Terraform: бакет создавай первым. Если зависимость есть, но прямой ссылки нет (скрытая связь), её указывают явно через depends_on:
resource "aws_s3_bucket_policy" "policy" {
bucket = aws_s3_bucket.my_bucket.id
policy = "..."
depends_on = [aws_s3_bucket_versioning.versioning]
}
Цикл init, plan, apply, destroy
Работа с Terraform — это четыре команды, которые вы запускаете по очереди.
terraform init— подготовка папки к работе. Скачивает указанные провайдеры (тот самый AWS-плагин) и настраивает хранение state. Запускается один раз в начале и повторно, если поменяли провайдеры или backend.
terraform init
terraform plan— сухой прогон. Показывает, что именно изменится, ничего не трогая. В выводе+— создать,~— изменить,-— удалить. Это ваша страховка: всегда смотрите план перед применением.
terraform plan
terraform apply— применяет изменения в реальном облаке. Сначала снова покажет план и спросит подтверждение — нужно ввестиyes.
terraform apply
terraform destroy— удаляет всю инфраструктуру, которой управляет эта конфигурация. Полезно для временных тестовых окружений, чтобы не платить за неиспользуемые ресурсы. Тоже спросит подтверждение.
terraform destroy
Запомнить просто: init — подготовиться, plan — посмотреть, apply — сделать, destroy — убрать.
State и где он живёт
Чтобы понимать разницу между «что описано» и «что уже создано», Terraform ведёт state (состояние) — файл, где записано, какие реальные ресурсы соответствуют вашим блокам. По умолчанию это локальный файл terraform.tfstate в папке проекта.
Локальный файл годится, пока вы один. Как только над инфраструктурой работает команда, локальный state становится проблемой: у каждого своя копия, они расходятся, а если двое запустят apply одновременно — state может повредиться. Решение — remote backend: state хранится в общем месте, а не на чьём-то ноутбуке.
Классический вариант на AWS — backend s3: сам state лежит в бакете S3, а DynamoDB используется для блокировки (lock), чтобы два человека не меняли state одновременно.
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "my-app/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Здесь bucket и key — где лежит файл состояния, dynamodb_table — таблица для блокировки (у неё должен быть ключ LockID типа String), encrypt — шифровать state в покое. Начиная с Terraform 1.10 появилась нативная блокировка прямо в S3 без DynamoDB — флаг use_lockfile = true; DynamoDB-вариант со временем уберут, но в существующих проектах он встречается повсеместно, поэтому знать его стоит. State часто содержит чувствительные данные (например, пароли), поэтому общий бакет должен быть закрыт правами — про разграничение доступа смотрите IAM в AWS.
Модули
Когда конфигурация растёт, копировать одни и те же блоки в разные окружения утомительно и опасно. Модуль — это переиспользуемый набор .tf-файлов, как функция в обычном коде: один раз описали, дальше вызываете с разными параметрами. Любая папка с .tf-файлами уже является модулем; вызывают её блоком module с обязательным аргументом source — откуда брать код.
module "app_storage" {
source = "./modules/storage"
bucket_name = "my-company-app-data-2026"
environment = "production"
}
source указывает путь: локальную папку (./modules/storage), git-репозиторий или публичный реестр Terraform Registry, где лежат тысячи готовых модулей. Так из тестового и боевого окружения вы вызываете один модуль с разными значениями — и инфраструктура гарантированно одинаковой формы.
module "test_storage" {
source = "./modules/storage"
bucket_name = "my-app-test"
environment = "test"
}
module "prod_storage" {
source = "./modules/storage"
bucket_name = "my-app-prod"
environment = "production"
}
Где это применяется
Terraform встречается почти везде, где есть облако: команды держат описание инфраструктуры в git и применяют его автоматически из конвейера сборки. Если вы настраиваете CI/CD, шаги terraform plan и terraform apply обычно встроены в пайплайн — об устройстве конвейеров читайте принципы CI/CD-пайплайна, а про выкатку — стратегии релизов. Часто Terraform поднимает кластер Kubernetes, а уже внутрь кластера приложения деплоят и конфигурируют другими инструментами.
Типичные ошибки новичков:
- Применять без
plan. Всегда сначала смотрите план —applyменяет реальное облако и стоит денег. - Не хранить state удалённо в команде. Локальный
terraform.tfstateу каждого свой — окружения разъедутся. Переходите на remote backend сразу, как только над проектом больше одного человека. - Править ресурсы руками в консоли облака. Тогда реальность расходится со state (это называют дрейфом), и следующий
applyможет всё сломать. Меняйте инфраструктуру только через Terraform. - Класть state в git. В нём бывают секреты, а блокировки git не даёт. State — в S3 с шифрованием, не в репозитории.
Что учить дальше: Terraform — не единственный инструмент инфраструктуры как кода. У AWS есть свой родной CloudFormation и более «программистский» AWS CDK. Полезно понимать управление состоянием и доставку в целом, а также сравнить подходы в обзоре инфраструктура как код: основы. Когда освоитесь с базовыми ресурсами, посмотрите, как Terraform поднимает вычислительные мощности и управляемые базы данных.