# Terragrunt, DRY-обёртка над Terraform _Advanced · TerraformLab Knowledge Base_ **TL;DR:** Terragrunt, обёртка Gruntwork, решает проблему «directory-per-env» дублирования. Объявляешь `terragrunt.hcl` с `inputs` и `include`'ами; Terragrunt генерирует backend.tf, provider.tf и main.tf на лету и вызывает `terraform`. Альтернатива, workspaces (опасно), copy-paste (плохо), CDKTF (другой стек). Стоимость, ещё один tool в цепочке, ещё один HCL-диалект. ## Зачем Directory-per-env (см. [tf-module-basics](/terraform/kb/tf-module-basics.md) и интермедиат-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 = < # любая 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](/terraform/kb/tf-stacks.md). ## Команды ```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'ами. ## См. также - [Remote state в S3: бакет, DynamoDB lock, encryption](/terraform/kb/tf-remote-backend-s3.md) - [CDKTF, Terraform на TypeScript/Python](/terraform/kb/tf-cdktf.md) - [Большой state, иерархия, blast-radius, naming](/terraform/kb/tf-large-scale-state.md)