Представьте, что вы настраиваете облако руками: заходите в консоль 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 — это четыре команды, которые вы запускаете по очереди.

  1. terraform init — подготовка папки к работе. Скачивает указанные провайдеры (тот самый AWS-плагин) и настраивает хранение state. Запускается один раз в начале и повторно, если поменяли провайдеры или backend.
terraform init
  1. terraform plan — сухой прогон. Показывает, что именно изменится, ничего не трогая. В выводе + — создать, ~ — изменить, - — удалить. Это ваша страховка: всегда смотрите план перед применением.
terraform plan
  1. terraform apply — применяет изменения в реальном облаке. Сначала снова покажет план и спросит подтверждение — нужно ввести yes.
terraform apply
  1. 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 поднимает вычислительные мощности и управляемые базы данных.