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/lessons/tf-advanced-01-terragrunt

lesson ── terraform-advanced ── ~18 мин ── 6 шагов

Terragrunt, DRY между dev/stage/prod

Terragrunt, обёртка Gruntwork над Terraform. Решает проблему «directory-per-env» дублирования: один terragrunt.hcl на env, общий через include. На уроке сделаешь dev и prod из одного модуля, поймёшь generate-блоки и dependency.

▶ интерактивный sandbox

Поднимется пара контейнеров: terraform 1.9 и localstack 3.8 в одной сети. В браузере откроется терминал, можно сразу terraform init. Каждый шаг проверяется автоматически. TTL 45 минут, без регистрации.

запустить sandbox →

stack ── terraform · localstack · 1 GB RAM · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Общий модуль app

    bash
    cd /home/student/tg
    cat > modules/app/main.tf <<'EOF'
    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.60"
        }
      }
    }
    resource "aws_s3_bucket" "app" {
      bucket = "linuxlab-tg-${var.env}-${var.app_name}"
      tags = {
        Environment = var.env
        App         = var.app_name
      }
    }
    output "bucket_arn" {
      value = aws_s3_bucket.app.arn
    }
    EOF
    cat > modules/app/variables.tf <<'EOF'
    variable "env" {
      type = string
    }
    variable "app_name" {
      type = string
    }
    EOF

    ✓ Общий модуль готов. Сейчас обвернём Terragrunt'ом.

  2. 02

    Root terragrunt.hcl, общее для всех env

    bash
    cat > live/terragrunt.hcl <<'EOF'
    remote_state {
      backend = "s3"
      config = {
        bucket   = "tf-state-tg-demo"
        key      = "${path_relative_to_include()}/terraform.tfstate"
        region   = "us-east-1"
        encrypt  = true
        s3_bucket_tags = {
          ManagedBy = "terragrunt"
        }
        # LocalStack-параметры, обычно в реальном AWS не нужны
        endpoints = {
          s3       = "http://localstack:4566"
          dynamodb = "http://localstack:4566"
        }
        skip_credentials_validation = true
        skip_metadata_api_check     = true
        skip_requesting_account_id  = true
        force_path_style            = true
      }
      generate = {
        path      = "backend.tf"
        if_exists = "overwrite_terragrunt"
      }
    }
    generate "provider" {
      path      = "provider.tf"
      if_exists = "overwrite_terragrunt"
      contents  = <<HCL
    provider "aws" {
      region                      = "us-east-1"
      access_key                  = "test"
      secret_key                  = "test"
      s3_use_path_style           = true
      skip_credentials_validation = true
      skip_metadata_api_check     = true
      skip_requesting_account_id  = true
      endpoints {
        s3       = "http://localstack:4566"
        iam      = "http://localstack:4566"
        sts      = "http://localstack:4566"
        dynamodb = "http://localstack:4566"
      }
    }
    HCL
    }
    EOF

    Этот файл, общий конфиг. Каждый env включит его через include.

    ✓ Root terragrunt.hcl написан. Сейчас env'ы.

  3. 03

    Per-env terragrunt.hcl

    bash
    cat > live/dev/terragrunt.hcl <<'EOF'
    include "root" {
      path = find_in_parent_folders()
    }
    terraform {
      source = "../../modules/app"
    }
    inputs = {
      env      = "dev"
      app_name = "api"
    }
    EOF
    cat > live/prod/terragrunt.hcl <<'EOF'
    include "root" {
      path = find_in_parent_folders()
    }
    terraform {
      source = "../../modules/app"
    }
    inputs = {
      env      = "prod"
      app_name = "api"
    }
    EOF
    tree live/ modules/

    Два env, одинаковая структура. Различие, только inputs.

    ✓ Env-roots готовы. Каждый указывает env-specific inputs.

  4. 04

    terragrunt apply в dev

    bash
    cd /home/student/tg/live/dev
    terragrunt apply --terragrunt-non-interactive -auto-approve 2>&1 | tail -20

    Что произошло за кулисами:

    1. Terragrunt прочитал live/dev/terragrunt.hcl.
    2. Включил root через find_in_parent_folders().
    3. Скопировал модуль из modules/app в .terragrunt-cache/.
    4. Сгенерировал backend.tf и provider.tf в кэш-папке.
    5. Запустил terraform init + apply с inputs.

    Бакет создан в LocalStack.

    bash
    aws --endpoint-url=http://localstack:4566 s3 ls

    Видишь linuxlab-tg-dev-api.

    ✓ Dev развёрнут. Один HCL, один env.

  5. 05

    Тот же конфиг, prod env

    bash
    cd /home/student/tg/live/prod
    terragrunt apply --terragrunt-non-interactive -auto-approve 2>&1 | tail -10
    aws --endpoint-url=http://localstack:4566 s3 ls

    Теперь два бакета: dev и prod. Из одного модуля, без копипасты.

    path_relative_to_include() в root'е использует относительный путь к env-директории как ключ в S3, dev/terraform.tfstate и prod/terraform.tfstate, разные state'ы, разный lock.

    ✓ Prod развёрнут. Изоляция state'ов работает.

    То же самое на OpenTofu

    OpenTofu держит CLI и state совместимыми с Terraform по командам этого шага: миграция обычно проходит через mv .terraform .terraform.bak; tofu init -upgrade. Но при первом переходе сделай backup state и прогон на feature-branch - расхождения концентрируются в новых фичах (variables в backend, state-encryption, OCI registry-backed модули). См. tf-opentofu-parity для полной матрицы.

    • → OpenTofu parity
  6. 06

    run-all для оркестрации

    Когда env'ов много, по одному запускать неудобно.

    bash
    cd /home/student/tg/live
    terragrunt run-all plan --terragrunt-non-interactive 2>&1 | tail -20

    run-all plan идёт по всем terragrunt.hcl рекурсивно, строит DAG зависимостей (если есть dependency-блоки), гоняет в правильном порядке.

    Для destroy:

    bash
    terragrunt run-all destroy --terragrunt-non-interactive -auto-approve 2>&1 | tail -5

    Snесёт обоих, в правильном reverse-порядке.

    ✓ Terragrunt оркестрация прошла. На 10+ env'ах разница ощутима.

    dependency блок, cross-stack outputs

    Реальный сценарий: stack network создаёт VPC, stack app читает его id.

    hcl
    # live/prod/app/terragrunt.hcl
    include "root" {
      path = find_in_parent_folders()
    }
    terraform {
      source = "../../../modules/app"
    }
    dependency "network" {
      config_path = "../network"
      mock_outputs = {
        vpc_id     = "mock-vpc"
        subnet_ids = ["mock-subnet"]
      }
    }
    inputs = {
      env        = "prod"
      vpc_id     = dependency.network.outputs.vpc_id
      subnet_ids = dependency.network.outputs.subnet_ids
    }

    mock_outputs, чтобы terragrunt plan работал даже если network ещё не создан. Полезно в init-фазе CI или для standalone plan'ов.

    terragrunt run-all apply сам поймёт: сначала network, потом app. DAG между stack'ами.

    • → Terragrunt целиком
    • → Иерархия state'ов

Что ты узнал

Структура: root terragrunt.hcl (общий provider, backend) + per-env live/<env>/terragrunt.hcl (inputs, include). Generate-блоки создают backend.tf/provider.tf на лету. terragrunt apply гоняет в одном env; terragrunt run-all apply, все.

команды

  • terragrunt applyapply в текущем env.
  • terragrunt run-all planplan по всем env, parallel.
  • terragrunt run-all apply --terragrunt-non-interactiveapply на все, для CI.

концепции

  • · include + find_in_parent_folders(), корневой конфиг
  • · generate { path = "backend.tf" }, создаёт backend.tf автоматически
  • · dependency "X", для cross-stack outputs

следующий →

Hello, S3: первый ресурс в Terraform

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки