linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • База знаний
  • Шпаргалка
  • Capstone
  • Собеседование
home/terraform/kb/Advanced/tf-terragrunt

kb/advanced ── Advanced ── advanced

Terragrunt, DRY-обёртка над Terraform

Terragrunt, обёртка Gruntwork, решает проблему «directory-per-env» дублирования. Объявляешь `terragrunt.hcl` с `inputs` и `include`'ами; Terragrunt генерирует backend.tf, provider.tf и main.tf на лету и вызывает `terraform`. Альтернатива, workspaces (опасно), copy-paste (плохо), CDKTF (другой стек). Стоимость, ещё один tool в цепочке, ещё один HCL-диалект.

view as markdownaka: terragrunt, terragrunt-hcl, terragrunt-generate

Зачем

Directory-per-env (см. tf-module-basics и интермедиат-12) даёт изоляцию, но плодит дубль:

envs/dev/main.tf      # source = "../modules/app", env = "dev"
envs/stage/main.tf    # source = "../modules/app", env = "stage"
envs/prod/main.tf     # source = "../modules/app", env = "prod"

Каждый из трёх файлов почти одинаковый. Terragrunt убирает дубль:

envs/dev/terragrunt.hcl    # include root + inputs = { env = "dev" }
envs/stage/terragrunt.hcl  # include root + inputs = { env = "stage" }
envs/prod/terragrunt.hcl   # include root + inputs = { env = "prod" }
terragrunt.hcl              # общий source, backend, provider

Десять строк на env вместо тридцати.

Минимальная структура

live/
├── terragrunt.hcl           # root, общее для всех env
├── dev/
│   └── terragrunt.hcl       # дев-специфика
├── stage/
│   └── terragrunt.hcl
└── prod/
    └── terragrunt.hcl
modules/
└── app/                     # обычный Terraform-модуль
    └── *.tf

live/terragrunt.hcl, корневой:

hcl
remote_state {
  backend = "s3"
  config = {
    bucket         = "company-tf-state"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "tf-state-lock"
  }
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
}
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
      ManagedBy = "terragrunt"
    }
  }
}
EOF
}

live/dev/terragrunt.hcl, конкретный env:

hcl
include "root" {
  path = find_in_parent_folders()
}
terraform {
  source = "../../../modules/app"
}
inputs = {
  env             = "dev"
  instance_size   = "t3.small"
  enable_logging  = false
}

Запуск:

bash
cd live/dev
terragrunt apply

Terragrunt:

  1. Идёт вверх, находит live/terragrunt.hcl, инклюдит.
  2. Генерирует backend.tf с правильным S3-key (dev/terraform.tfstate).
  3. Генерирует provider.tf.
  4. Копирует module из ../../../modules/app в кэш-папку.
  5. Запускает terraform apply с inputs из dev/terragrunt.hcl.

Generate-блоки

Главная фича Terragrunt, генерация файлов. Backend, provider, что угодно. Это убирает копипасту из Terraform-модулей.

Generate-options:

  • if_exists = "overwrite_terragrunt", перезапись своих файлов (созданных Terragrunt'ом ранее).
  • if_exists = "skip", пропустить, если файл существует.
  • if_exists = "overwrite", перезапись чего угодно (опасно).

Dependency-блок

Когда prod/network нужно поднять до prod/app (потому что app читает VPC id):

hcl
# prod/app/terragrunt.hcl
dependency "network" {
  config_path = "../network"
}
inputs = {
  vpc_id     = dependency.network.outputs.vpc_id
  subnet_ids = dependency.network.outputs.subnet_ids
}

Terragrunt сам найдёт prod/network/terragrunt.hcl, прогонит plan/apply если нужно, прочитает outputs, передаст в app'у. Это явный cross-stack dependency.

Команды

bash
terragrunt apply              # apply этого env
terragrunt plan-all           # plan по всем env (parallel)
terragrunt apply-all          # apply по всем env (последовательно с deps)
terragrunt destroy-all
terragrunt run-all <cmd>      # любая terraform-команда на всех env

apply-all гоняется по графу dependencies, сначала network, потом app. На сотне stack'ов это спасает.

Когда Terragrunt оправдан

КейсРешение
1 env, маленький проектНе нужно, overkill.
3 env (dev/stage/prod), copy-paste терпимНе нужно, KISS.
5+ env или 10+ stacks с одинаковой структуройДа, экономит код.
Cross-stack dependencies через outputsДа, dependency блок.
Хочешь стандартизировать backend для всех projectsДа, root generate.

Anti-pattern: один env, но прогрессивно используешь Terragrunt «на будущее». Получишь сложность, не сэкономишь.

Подводные камни

  • Ещё один HCL-диалект. terragrunt.hcl похож на терраформный, но другой. Функции find_in_parent_folders(), path_relative_to_include() Terragrunt-only. Учиться.

  • source поддерживает только git:: для версионирования. Локальный source = "../../modules/app" работает, но без версии. Production-best-practice: source = "git::ssh://...modules.git//app?ref=v1.2.3".

  • Cache-папка .terragrunt-cache/. Содержит копии модулей и .terraform/. Иногда «protухает», нужно terragrunt run-all init -upgrade.

  • Версии Terragrunt и Terraform: в terragrunt.hcl можно зафиксировать terraform_version_constraint и terragrunt_version_constraint. Без них разные dev'ы / CI-runner'ы получают разное.

  • apply-all без --terragrunt-non-interactive зависает в CI. По дефолту Terragrunt подтверждает «apply on N modules»; в pipeline добавляй флаг.

  • State per stack, отдельный bucket-key per env. Не выноси все env'ы в один state, потеряешь main benefit изоляции.

  • Bloat: на больших monorepo Terragrunt'ов компиляция всех terragrunt.hcl занимает время. Для 100+ stacks, это секунды на каждый run.

  • Альтернатива, Stacks (HashiCorp). С 2024 HashiCorp выпустил Terraform Stacks, который покрывает часть Terragrunt-функций нативно. Часть, не всё. Для новых проектов оценивай оба. См. tf-stacks.

§ команды

bash
terragrunt --version

В образе уже есть. Если нужна другая версия, pin'нуть в Dockerfile.

bash
terragrunt apply

В каталоге с terragrunt.hcl, apply одного env.

bash
terragrunt run-all plan

plan по всем terragrunt.hcl рекурсивно. С учётом dependencies.

bash
terragrunt graph-dependencies | dot -Tpng > deps.png

Граф dependency-блоков между stack'ами.

§ см. также

  • tf-remote-backend-s3Remote state в S3: бакет, DynamoDB lock, encryptionS3-backend хранит `terraform.tfstate` в бакете, DynamoDB-таблица даёт блокировку одного-apply-за-раз. Конфигурация, в блоке `backend "s3"` внутри `terraform { ... }`. State в S3. Это единственный source of truth, локального файла больше нет. Переезд с local на S3, через `terraform init -migrate-state`.
  • tf-cdktfCDKTF, Terraform на TypeScript/PythonCDKTF, Terraform на TypeScript/Python/Go/Java/C#. На выходе стандартный HCL + tfstate. Плюсы: типы, autocomplete, циклы и классы. Минусы: ещё одна абстракция, debug сложнее, не все фичи Terraform покрыты 1:1. Оправдан когда нужна программируемая генерация HCL (N stack'ов из CSV, например).
  • tf-large-scale-stateБольшой state, иерархия, blast-radius, namingОдин state на 5000 ресурсов = боль: refresh минуты, lock-contention, любой apply трогает всё. Решение, иерархия state'ов: network/iam отдельно, apps отдельно, между ними `terraform_remote_state`. Blast-radius, критерий разделения. Naming для бакетов и lock-таблиц предсказуемая структура.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки