Terragrunt

Terragrunt 総合ガイド — 機能・アーキテクチャ・設定の全容

本ドキュメントは Terragrunt の全体像を俯瞰し、アーキテクチャの深部から運用上の設定例までを網羅的に解説する。対象読者は Terraform に一定の知見を持つ SRE・インフラエンジニアを想定している。


1. はじめに — Terragrunt とは何か

1.1 概要

Terragrunt は、Gruntwork 社が開発した Terraform のラッパーツール である。Terraform 自体が持つ Infrastructure as Code (IaC) の強力な機能を活かしつつ、大規模運用における以下の課題を解決するために設計されている。

  • DRY(Don't Repeat Yourself)の徹底: 複数環境間で繰り返し記述される設定を排除し、バックエンド設定・プロバイダ設定・変数などを一元管理する
  • マルチ環境・マルチアカウント管理: dev / staging / production などの環境分離、AWS マルチアカウント戦略をコードレベルで体系化する
  • 依存関係の明示的管理: モジュール間の依存関係を宣言的に定義し、正しい順序でのデプロイを保証する
  • リモートステート管理の自動化: Terraform backend の設定を自動生成し、S3 バケットや DynamoDB テーブルの自動作成を含むステート管理を簡素化する
  • コードの再利用性向上: Terraform モジュールをバージョン管理された外部ソースから取得し、環境固有の設定のみをオーバーライドする仕組みを提供する

1.2 Terraform が抱える課題と Terragrunt の必要性

Terraform は優れた IaC ツールであるが、プロジェクトが大規模化すると以下の課題が顕在化する。

1.2.1 バックエンド設定の重複

Terraform では各モジュール(ルートモジュール)に対して backend ブロックを記述する必要がある。

# environment/dev/vpc/main.tf
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "dev/vpc/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

この設定は環境ごと・モジュールごとに必要であり、数十〜数百のモジュールを管理する場合、わずかな変更(バケット名の変更やリージョンの変更)でも大量のファイルを修正する必要がある。

1.2.2 プロバイダ設定の重複

# 各モジュールに繰り返し記述される
provider "aws" {
  region = "us-east-1"
  assume_role {
    role_arn = "arn:aws:iam::123456789012:role/TerraformRole"
  }
  default_tags {
    tags = {
      Environment = "dev"
      ManagedBy   = "terraform"
    }
  }
}

1.2.3 環境間の差分管理

dev / staging / production で同じインフラ構成を持つ場合、Terraform では以下のいずれかのアプローチを取る必要がある。

アプローチメリットデメリット
ワークスペース(workspace)管理が簡単環境間の差分表現が困難、ステートファイルが同一バケットに混在
ディレクトリコピー環境間の独立性が高いコードの重複が膨大、変更時に全環境の同期が必要
条件分岐(count/for_each)1 つのコードベースで管理複雑化しやすく、可読性が低下

1.2.4 モジュール間の依存関係

Terraform 単体では、異なるルートモジュール間の依存関係を明示的に管理する仕組みがない。VPC を先に作成し、その出力を EKS クラスタに渡すといった操作は手動で行うか、terraform_remote_state データソースに依存する必要がある。

1.3 Terragrunt の歴史

バージョン/イベント主な変更点
2016v0.1.0初期リリース。Terraform のバックエンド設定の DRY 化に焦点
2017v0.12.xterraform.tfvars ベースの設定体系
2019v0.19.xterragrunt.hcl への設定ファイル名変更、HCL2 構文サポート
2020v0.23.xdependency ブロックの導入、run-all コマンドの追加
2021v0.31.xgenerate ブロックの強化、catalog 機能の初期実装
2022v0.38.xread_terragrunt_config() の安定化、パフォーマンス改善
2023v0.50.xTerraform の OpenTofu フォーク対応、Engine の初期実装
2024v0.55.x〜v0.67.xTerragrunt Engine(実験的)、Stacks 機能の強化
2025v0.70.x〜Engine の安定化、Terragrunt Provider の成熟

1.4 Terragrunt のライセンスとエコシステム

Terragrunt は MIT ライセンス で提供されるオープンソースソフトウェアであり、Gruntwork 社のエコシステムの中核をなす。

コンポーネント説明
TerragruntTerraform ラッパー(本ドキュメントの主題)
Terragrunt EngineTerraform/OpenTofu の実行エンジン抽象化レイヤー
Gruntwork IaC Library本番品質の Terraform モジュール群(商用)
Gruntwork PipelinesCI/CD パイプライン(商用)
TerratestGo 言語によるインフラテストフレームワーク

2. アーキテクチャの全体像

2.1 ディレクトリ構造の設計思想

Terragrunt の最大の特徴は、Terraform モジュール(再利用可能なコード)環境固有の設定(ライブ設定) を完全に分離する設計思想にある。

infrastructure/
├── modules/                          # Terraform モジュール(再利用可能)
│   ├── vpc/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── eks/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── rds/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── s3/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
│
└── live/                             # Terragrunt ライブ設定(環境固有)
    ├── terragrunt.hcl                # ルート設定(全環境共通)
    ├── _envcommon/                   # 環境共通のモジュール設定
    │   ├── vpc.hcl
    │   ├── eks.hcl
    │   ├── rds.hcl
    │   └── s3.hcl
    ├── dev/
    │   ├── env.hcl                   # dev 環境の変数
    │   ├── us-east-1/
    │   │   ├── region.hcl            # リージョン固有の変数
    │   │   ├── vpc/
    │   │   │   └── terragrunt.hcl    # VPC の dev 環境設定
    │   │   ├── eks/
    │   │   │   └── terragrunt.hcl
    │   │   └── rds/
    │   │       └── terragrunt.hcl
    │   └── us-west-2/
    │       ├── region.hcl
    │       └── vpc/
    │           └── terragrunt.hcl
    ├── staging/
    │   ├── env.hcl
    │   └── us-east-1/
    │       ├── region.hcl
    │       ├── vpc/
    │       │   └── terragrunt.hcl
    │       ├── eks/
    │       │   └── terragrunt.hcl
    │       └── rds/
    │           └── terragrunt.hcl
    └── production/
        ├── env.hcl
        └── us-east-1/
            ├── region.hcl
            ├── vpc/
            │   └── terragrunt.hcl
            ├── eks/
            │   └── terragrunt.hcl
            └── rds/
                └── terragrunt.hcl

2.2 設定の階層構造と継承メカニズム

Terragrunt は設定の継承を include ブロックによって実現する。設定は階層的にマージされ、最も具体的な(リーフの)設定が最も高い優先度を持つ。

                    ┌──────────────────┐
                    │  ルート設定        │
                    │  terragrunt.hcl  │
                    │  (backend, provider│
                    │   generate 等)    │
                    └────────┬─────────┘
                             │ include
                    ┌────────┴─────────┐
                    │  _envcommon/      │
                    │  vpc.hcl 等       │
                    │  (source, 共通    │
                    │   inputs)         │
                    └────────┬─────────┘
                             │ include
              ┌──────────────┼──────────────┐
              │              │              │
     ┌────────┴───┐  ┌──────┴─────┐  ┌────┴────────┐
     │ dev/vpc/   │  │ stg/vpc/   │  │ prod/vpc/   │
     │ terragrunt │  │ terragrunt │  │ terragrunt  │
     │ .hcl       │  │ .hcl       │  │ .hcl        │
     │ (環境固有   │  │ (環境固有   │  │ (環境固有    │
     │  inputs)   │  │  inputs)   │  │  inputs)    │
     └────────────┘  └────────────┘  └─────────────┘

2.3 実行フロー

Terragrunt のコマンド実行時の内部処理フローは以下のとおりである。

1. terragrunt.hcl の解析
   │
2. include チェーンの解決・設定マージ
   │
3. dependency ブロックの解析・依存関係グラフの構築
   │
4. generate ブロックによるファイル自動生成
   │  ├── backend.tf
   │  └── provider.tf
   │
5. terraform source のダウンロード
   │  └── .terragrunt-cache/ にキャッシュ
   │
6. inputs → terraform.tfvars.json の自動生成
   │
7. before_hook の実行
   │
8. terraform コマンドの実行
   │  └── init → plan → apply
   │
9. after_hook の実行

2.4 キャッシュメカニズム

Terragrunt は .terragrunt-cache/ ディレクトリを使用してTerraform モジュールのソースコードをキャッシュする。

live/dev/us-east-1/vpc/.terragrunt-cache/
└── <hash>/
    └── <module-source>/
        ├── main.tf              # ダウンロードされたモジュール
        ├── variables.tf
        ├── outputs.tf
        ├── backend.tf           # generate で自動生成
        ├── provider.tf          # generate で自動生成
        └── terraform.tfvars.json # inputs から自動生成

キャッシュの動作:

  • terraform source の URL とバージョンに基づいてハッシュが計算される
  • キャッシュが存在し、ソースが変更されていなければ再ダウンロードをスキップする
  • terragrunt run-all init --terragrunt-no-auto-init でキャッシュの再利用を制御可能
  • --terragrunt-source-update フラグでキャッシュの強制更新が可能

3. コア機能の詳細

3.1 リモートステート管理

3.1.1 remote_state ブロック

Terragrunt の remote_state ブロックは、Terraform の backend 設定を動的に生成する。

# live/terragrunt.hcl(ルート設定)
remote_state {
  backend = "s3"
  
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }

  config = {
    bucket         = "my-company-terraform-state"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
    
    s3_bucket_tags = {
      Name        = "Terraform State"
      Environment = "shared"
      ManagedBy   = "terragrunt"
    }

    dynamodb_table_tags = {
      Name        = "Terraform Lock Table"
      Environment = "shared"
      ManagedBy   = "terragrunt"
    }
  }
}

自動作成機能: Terragrunt は remote_state ブロックで指定された S3 バケットおよび DynamoDB テーブルが存在しない場合、自動的に作成する。これにより「ステートを管理するためのインフラをどう管理するか」というブートストラップ問題を解消する。

3.1.2 path_relative_to_include() の動作

path_relative_to_include() は、現在の terragrunt.hcl からルートの terragrunt.hcl までの相対パスを返す。これにより、ステートファイルのキーが自動的に階層構造を反映する。

live/dev/us-east-1/vpc/terragrunt.hcl
→ path_relative_to_include() = "dev/us-east-1/vpc"
→ S3 key = "dev/us-east-1/vpc/terraform.tfstate"

live/production/us-east-1/eks/terragrunt.hcl
→ path_relative_to_include() = "production/us-east-1/eks"
→ S3 key = "production/us-east-1/eks/terraform.tfstate"

3.1.3 GCS バックエンドの例

remote_state {
  backend = "gcs"
  
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }

  config = {
    project  = "my-gcp-project"
    location = "us-central1"
    bucket   = "my-company-terraform-state"
    prefix   = "${path_relative_to_include()}/terraform.tfstate"
  }
}

3.2 generate ブロック

generate ブロックは、Terraform の実行前に自動的にファイルを生成する機能を提供する。バックエンド設定やプロバイダ設定の DRY 化に不可欠な機能である。

3.2.1 プロバイダ設定の自動生成

# live/terragrunt.hcl
generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "terraform"
      Project     = var.project_name
    }
  }
}
EOF
}

3.2.2 if_exists の動作

動作
overwrite_terragruntTerragrunt が生成したファイルのみ上書き。手動作成のファイルは保護される(推奨)
overwrite既存ファイルを無条件に上書きする
skipファイルが既に存在する場合は生成をスキップする
errorファイルが既に存在する場合はエラーを発生させる

3.3 include ブロック — 設定の継承

include ブロックは Terragrunt の設定継承メカニズムの中核をなす。

3.3.1 基本的な include

# live/dev/us-east-1/vpc/terragrunt.hcl
include "root" {
  path = find_in_parent_folders()
}

find_in_parent_folders() は、親ディレクトリを再帰的に探索し、最初に見つかった terragrunt.hcl のパスを返す。

3.3.2 複数 include(Multiple Includes)

Terragrunt v0.32.0 以降では、複数の include ブロックを定義可能になった。

# live/dev/us-east-1/vpc/terragrunt.hcl
include "root" {
  path = find_in_parent_folders()
}

include "envcommon" {
  path   = "${dirname(find_in_parent_folders())}/_envcommon/vpc.hcl"
  expose = true
}

inputs = {
  vpc_cidr           = "10.0.0.0/16"
  enable_nat_gateway = include.envcommon.locals.default_nat_gateway
}

3.3.3 merge_strategy

戦略動作
no_mergeマージを行わない。子の設定のみが有効
shallowトップレベルの属性のみをマージ(デフォルト)
deepネストされたマップも再帰的にマージ

deep マージの例:

# 親(_envcommon/vpc.hcl)
inputs = {
  tags = {
    Team      = "platform"
    Service   = "networking"
    ManagedBy = "terraform"
  }
  enable_dns = true
}

# 子(dev/us-east-1/vpc/terragrunt.hcl)
inputs = {
  tags = {
    Environment = "dev"          # 追加される
    Team        = "dev-platform" # 上書きされる
  }
}
# → マージ結果: tags に全4キーが含まれ、enable_dns も継承される

3.4 dependency ブロック — モジュール間の依存関係

# live/dev/us-east-1/eks/terragrunt.hcl
dependency "vpc" {
  config_path = "../vpc"
  
  mock_outputs = {
    vpc_id             = "vpc-mock-12345"
    private_subnet_ids = ["subnet-mock-1", "subnet-mock-2", "subnet-mock-3"]
  }
  mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}

dependency "iam" {
  config_path = "../../_global/iam"
  
  mock_outputs = {
    eks_cluster_role_arn = "arn:aws:iam::123456789012:role/mock-eks-role"
    node_group_role_arn  = "arn:aws:iam::123456789012:role/mock-node-role"
  }
  mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}

inputs = {
  vpc_id              = dependency.vpc.outputs.vpc_id
  private_subnet_ids  = dependency.vpc.outputs.private_subnet_ids
  cluster_role_arn    = dependency.iam.outputs.eks_cluster_role_arn
  node_group_role_arn = dependency.iam.outputs.node_group_role_arn
}

mock_outputs の重要性

  1. 初回デプロイ時: 依存先未デプロイでも validate / plan が実行可能
  2. CI/CD パイプライン: 依存先ステートにアクセスできない環境でも検証可能
  3. ローカル開発: すべての依存先をデプロイする必要がない

dependencies ブロック(出力を参照しない依存関係)

dependencies {
  paths = ["../eks", "../rds", "../elasticache"]
}

3.5 terraform ブロック — ソース管理

# Git リポジトリから
terraform {
  source = "git::ssh://git@github.com/my-company/terraform-modules.git//modules/vpc?ref=v2.3.1"
}

# Terraform Registry から
terraform {
  source = "tfr:///terraform-aws-modules/vpc/aws?version=5.1.0"
}

# ローカルパスから
terraform {
  source = "${dirname(find_in_parent_folders())}/../../modules//vpc"
}

source の動的構築

locals {
  module_versions = yamldecode(file("${dirname(find_in_parent_folders())}/module_versions.yaml"))
}

terraform {
  source = "git::ssh://git@github.com/my-company/terraform-modules.git//modules/vpc?ref=${local.module_versions.vpc}"
}

3.6 inputs ブロック — 変数の受け渡し

# live/dev/us-east-1/vpc/terragrunt.hcl
locals {
  env_vars    = read_terragrunt_config(find_in_parent_folders("env.hcl"))
  region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
  environment = local.env_vars.locals.environment
  aws_region  = local.region_vars.locals.aws_region
}

inputs = {
  environment      = local.environment
  aws_region       = local.aws_region
  vpc_name         = "${local.environment}-main-vpc"
  vpc_cidr         = "10.0.0.0/16"
  azs              = local.region_vars.locals.azs
  private_subnets  = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets   = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
  enable_nat_gateway   = true
  single_nat_gateway   = true
  enable_dns_hostnames = true
}

3.7 Built-in 関数一覧

パス関連関数

関数説明
find_in_parent_folders()親ディレクトリを再帰探索し指定ファイルのパスを返す
path_relative_to_include()include 元からの相対パス
get_terragrunt_dir()現在の terragrunt.hcl のディレクトリパス
get_repo_root()Git リポジトリのルートパス
get_original_terragrunt_dir()キャッシュ前の元ディレクトリ

環境・データ操作関数

関数説明
get_env()環境変数を取得
get_aws_account_id()AWS アカウント ID を取得
read_terragrunt_config()外部 .hcl ファイルを読み込む
run_cmd()外部コマンドを実行して結果を取得
sops_decrypt_file()SOPS 暗号化ファイルを復号

4. 高度な機能

4.1 run-all コマンド — 一括操作

cd live/dev && terragrunt run-all plan
cd live/production && terragrunt run-all apply
terragrunt run-all apply --terragrunt-parallelism 3
terragrunt run-all plan --terragrunt-exclude-dir "*/monitoring/*"

run-alldependency ブロックに基づいて依存関係グラフを構築し、正しい順序で実行する。destroy 時は逆順で実行される。

4.2 Hooks — コマンド前後のカスタム処理

terraform {
  before_hook "validate_inputs" {
    commands = ["apply", "plan"]
    execute  = ["python3", "${get_repo_root()}/scripts/validate_inputs.py"]
  }

  after_hook "notify_slack" {
    commands     = ["apply"]
    execute      = ["bash", "${get_repo_root()}/scripts/notify_slack.sh"]
    run_on_error = false
  }

  error_hook "alert_on_failure" {
    commands  = ["apply", "plan"]
    execute   = ["bash", "${get_repo_root()}/scripts/alert_failure.sh"]
    on_errors = [".*"]
  }
}

4.3 extra_arguments — Terraform への追加引数

terraform {
  extra_arguments "retry_lock" {
    commands  = get_terraform_commands_that_need_locking()
    arguments = ["-lock-timeout=20m"]
  }

  extra_arguments "parallelism" {
    commands  = ["plan", "apply", "destroy"]
    arguments = ["-parallelism=30"]
  }
}

5. 実践的な設定パターン

5.1 マルチアカウント AWS 構成

live/
├── terragrunt.hcl              # ルート設定
├── _envcommon/                 # 環境共通設定
├── management/                 # 管理アカウント
│   ├── account.hcl
│   └── us-east-1/
├── security/                   # セキュリティアカウント
├── shared-services/            # 共有サービス
├── dev/                        # 開発
├── staging/                    # ステージング
└── production/                 # 本番
    ├── us-east-1/             # プライマリ
    └── us-west-2/             # DR リージョン

ルート設定

# live/terragrunt.hcl
locals {
  account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
  region_vars  = read_terragrunt_config(find_in_parent_folders("region.hcl"))
  account_id   = local.account_vars.locals.account_id
  account_name = local.account_vars.locals.account_name
  aws_region   = local.region_vars.locals.aws_region
}

remote_state {
  backend = "s3"
  generate = { path = "backend.tf", if_exists = "overwrite_terragrunt" }
  config = {
    bucket         = "${local.account_name}-terraform-state-${local.account_id}"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = local.aws_region
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region              = "${local.aws_region}"
  allowed_account_ids = ["${local.account_id}"]
  assume_role {
    role_arn     = "arn:aws:iam::${local.account_id}:role/TerraformExecutionRole"
    session_name = "terragrunt"
  }
  default_tags {
    tags = {
      Account   = "${local.account_name}"
      Region    = "${local.aws_region}"
      ManagedBy = "terragrunt"
    }
  }
}
EOF
}

5.2 環境共通設定のパターン(_envcommon)

# _envcommon/eks.hcl — すべての環境で共通のEKS設定
locals {
  env_vars    = read_terragrunt_config(find_in_parent_folders("account.hcl"))
  region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
  environment = local.env_vars.locals.account_name
  cluster_name = "${local.environment}-eks-${local.region_vars.locals.aws_region}"
}

terraform {
  source = "git::ssh://git@github.com/my-company/terraform-modules.git//modules/eks?ref=v3.1.0"
}

dependency "vpc" {
  config_path = "../vpc"
  mock_outputs = {
    vpc_id             = "vpc-mock"
    private_subnet_ids = ["subnet-mock-1", "subnet-mock-2"]
  }
  mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}

inputs = {
  cluster_name    = local.cluster_name
  cluster_version = "1.28"
  vpc_id          = dependency.vpc.outputs.vpc_id
  subnet_ids      = dependency.vpc.outputs.private_subnet_ids
  node_groups = {
    default = {
      desired_capacity = 2
      max_capacity     = 10
      min_capacity     = 1
      instance_types   = ["t3.large"]
    }
  }
}
# live/production/us-east-1/eks/terragrunt.hcl — production 固有のオーバーライド
include "root" {
  path           = find_in_parent_folders()
  merge_strategy = "deep"
}
include "envcommon" {
  path           = "${dirname(find_in_parent_folders())}/_envcommon/eks.hcl"
  merge_strategy = "deep"
  expose         = true
}

inputs = {
  node_groups = {
    default = {
      desired_capacity = 5
      max_capacity     = 50
      min_capacity     = 3
      instance_types   = ["m5.xlarge", "m5.2xlarge"]
    }
    spot = {
      desired_capacity = 3
      max_capacity     = 20
      min_capacity     = 0
      instance_types   = ["m5.xlarge", "m5a.xlarge"]
      capacity_type    = "SPOT"
    }
  }
  cluster_enabled_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
}

5.3 シークレット管理(SOPS 統合)

locals {
  secrets = yamldecode(sops_decrypt_file("${get_terragrunt_dir()}/secrets.enc.yaml"))
}

inputs = {
  db_username = local.secrets.db_username
  db_password = local.secrets.db_password
}

5.4 GCP での構成例

remote_state {
  backend = "gcs"
  generate = { path = "backend.tf", if_exists = "overwrite_terragrunt" }
  config = {
    project  = local.project_id
    location = local.region
    bucket   = "${local.project_id}-terraform-state"
    prefix   = "${path_relative_to_include()}/terraform.tfstate"
  }
}

5.5 Azure での構成例

remote_state {
  backend = "azurerm"
  generate = { path = "backend.tf", if_exists = "overwrite_terragrunt" }
  config = {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "tfstate${local.subscription_id}"
    container_name       = "tfstate"
    key                  = "${path_relative_to_include()}/terraform.tfstate"
  }
}

6. CI/CD パイプラインとの統合

6.1 GitHub Actions との統合

name: Terragrunt CI/CD
on:
  pull_request:
    branches: [main]
    paths: ['live/**', 'modules/**']
  push:
    branches: [main]
    paths: ['live/**', 'modules/**']

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      changed_dirs: ${{ steps.changes.outputs.dirs }}
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - name: Detect changed modules
        id: changes
        run: |
          CHANGED_DIRS=$(git diff --name-only origin/main...HEAD | \
            grep '^live/' | xargs -I {} dirname {} | sort -u | \
            grep -v '_envcommon' | \
            jq -R -s 'split("\n") | map(select(length > 0))' )
          echo "dirs=$CHANGED_DIRS" >> $GITHUB_OUTPUT

  plan:
    needs: detect-changes
    if: github.event_name == 'pull_request'
    strategy:
      matrix:
        dir: ${{ fromJson(needs.detect-changes.outputs.changed_dirs) }}
    steps:
      - uses: actions/checkout@v4
      - name: Terragrunt Plan
        working-directory: ${{ matrix.dir }}
        run: terragrunt plan -no-color -out=tfplan

  apply:
    needs: detect-changes
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    strategy:
      matrix:
        dir: ${{ fromJson(needs.detect-changes.outputs.changed_dirs) }}
      max-parallel: 1
    steps:
      - uses: actions/checkout@v4
      - name: Terragrunt Apply
        working-directory: ${{ matrix.dir }}
        run: terragrunt apply -auto-approve -no-color

6.2 Atlantis との統合

# atlantis.yaml
version: 3
projects:
  - name: dev-vpc
    dir: live/dev/us-east-1/vpc
    workflow: terragrunt
    autoplan:
      when_modified: ["*.hcl", "../../_envcommon/vpc.hcl"]
      enabled: true

workflows:
  terragrunt:
    plan:
      steps:
        - run: terragrunt plan -no-color -out=$PLANFILE
    apply:
      steps:
        - run: terragrunt apply -no-color $PLANFILE

7. テスト戦略

7.1 Terratest を使用したテスト

func TestVpc(t *testing.T) {
    t.Parallel()
    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir:    "../live/dev/us-east-1/vpc",
        TerraformBinary: "terragrunt",
    })
    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)
    
    vpcId := terraform.Output(t, terraformOptions, "vpc_id")
    require.NotEmpty(t, vpcId)
    
    vpc := aws.GetVpcById(t, vpcId, "us-east-1")
    assert.Equal(t, "10.0.0.0/16", vpc.CidrBlock)
}

7.2 バリデーション戦略

terragrunt hclfmt --terragrunt-check        # HCL 構文チェック
terragrunt run-all validate                   # 全モジュール validate
trivy config --severity HIGH,CRITICAL live/   # セキュリティスキャン
conftest test --policy policy/ live/           # OPA ポリシーチェック

7.3 コスト見積もり

infracost breakdown --path live/dev/us-east-1/vpc --terraform-binary terragrunt

8. Terragrunt Engine と OpenTofu 対応

8.1 Terragrunt Engine

engine {
  source  = "github.com/gruntwork-io/terragrunt-engine-opentofu"
  version = "v1.0.0"
  type    = "rpc"
}

8.2 OpenTofu との使用

export TERRAGRUNT_TFPATH=tofu
terragrunt plan

8.3 Terraform vs OpenTofu

観点TerraformOpenTofu
ライセンスBSL 1.1MPL 2.0(完全OSS)
Terragrunt サポートフルフル(v0.52.0 以降)
State 暗号化Enterprise のみOSS で利用可能

9. パフォーマンスチューニング

9.1 最適化テクニック

# プロバイダキャッシュ
terraform {
  extra_arguments "plugin_cache" {
    commands = ["init"]
    env_vars = { TF_PLUGIN_CACHE_DIR = "${get_env("HOME")}/.terraform.d/plugin-cache" }
  }
}
# 並列度調整
terragrunt run-all apply --terragrunt-parallelism 3

9.2 トラブルシューティング

問題対処法
Error finding AWS credentialsaws sts get-caller-identity で確認
Backend configuration changedterragrunt init -reconfigure
Error acquiring state lockterragrunt force-unlock <LOCK_ID>
Cycle detected in dependency依存関係グラフの見直し
Module source has changed.terragrunt-cache の削除
# 依存関係グラフの可視化
terragrunt graph-dependencies | dot -Tpng -o dependencies.png

10. セキュリティベストプラクティス

10.1 ステートファイルの保護

  • S3 バケットのバージョニング有効化
  • KMS による暗号化
  • パブリックアクセスブロック
  • TLS 通信の強制
  • 削除防止ポリシー

10.2 .gitignore

**/.terragrunt-cache/
*.tfstate
*.tfstate.backup
**/.terraform/
**/terraform.tfvars.json
**/backend.tf
**/provider.tf
*.enc.yaml.dec
.env

10.3 OPA によるポリシー適用

package terragrunt.required_tags

deny[msg] {
    input.resource_changes[_].type == "aws_instance"
    not has_required_tags(input.resource_changes[_])
    msg := sprintf("Resource %s is missing required tags", [input.resource_changes[_].address])
}

11. 運用パターンとベストプラクティス

11.1 命名規則

  • ディレクトリ: live/{account-name}/{region}/{service}/terragrunt.hcl
  • リソース: {environment}-{service}-{component}
  • タグ: Environment, Service, ManagedBy, Team, CostCenter

11.2 ブランチ戦略

  1. main ブランチ保護(PR 経由のみ)
  2. PR マージ → dev 自動 apply → staging 手動 → production 手動(承認必須)
  3. plan 成功・レビュー承認を必須化

11.3 モジュールバージョニング

# module_versions.yaml
dev:       { vpc: v2.4.0-rc1, eks: v3.2.0-beta1 }
staging:   { vpc: v2.3.1, eks: v3.1.0 }
production: { vpc: v2.3.1, eks: v3.1.0 }

11.4 ステートマイグレーション

terraform state pull > terraform.tfstate.backup
# terragrunt.hcl 作成後
terragrunt init    # "migrate existing state?" → yes
terragrunt plan    # "No changes" を確認

12. 大規模プロジェクトでの設計パターン

12.1 モノレポ vs マルチレポ

観点モノレポマルチレポ
構成modules/ + live/ を 1 リポジトリ分離
推奨小〜中規模(1-5名)中〜大規模(5名以上)

12.2 Scaffold パターン

新しいアカウント・環境追加時にテンプレートから設定ファイルを自動生成する。

12.3 カスタムモジュールラッパー

社内標準(セキュリティ・ログ・モニタリング)を組み込んだラッパーモジュールで抽象化する。


13. コマンドリファレンス

13.1 基本コマンド

terragrunt init / plan / apply / destroy / output / validate / import / state

13.2 Terragrunt 固有コマンド

terragrunt run-all {plan|apply|destroy|validate|init}
terragrunt hclfmt [--terragrunt-check]
terragrunt graph-dependencies
terragrunt render-json
terragrunt catalog

13.3 重要なフラグ

フラグ説明
--terragrunt-tfpathTerraform バイナリパス
--terragrunt-non-interactive非対話モード
--terragrunt-parallelism並列度
--terragrunt-sourceモジュールソース上書き
--terragrunt-exclude-dir除外ディレクトリ
--terragrunt-log-levelログレベル

13.4 環境変数

変数説明
TERRAGRUNT_TFPATHTerraform バイナリパス
TERRAGRUNT_LOG_LEVELログレベル
TERRAGRUNT_NON_INTERACTIVE非対話モード
TERRAGRUNT_PARALLELISM並列度
TERRAGRUNT_SOURCEモジュールソース上書き
TERRAGRUNT_IAM_ROLEIAM ロール ARN

14. Terraform との比較と使い分け

14.1 比較表

観点Terraform のみTerraform + Terragrunt
学習曲線低い中程度
DRY部分的徹底的
バックエンド管理手動自動
依存関係terraform_remote_statedependency ブロック
一括操作スクリプト自作run-all
推奨規模小規模中〜大規模

14.2 類似ツール

ツール概要
Terraform Cloud/EnterpriseHashiCorp 公式マネージド
TerraspaceRuby ベースフレームワーク
AtmosYAML ベーススタック管理
SpaceliftCI/CD + ステート管理 SaaS
env0環境管理プラットフォーム

15. まとめ

15.1 核心的価値

  1. DRY 原則の徹底include, generate, _envcommon で設定重複を最小化
  2. 依存関係の明示化dependency + run-all で正しい実行順序を保証
  3. スケーラブルな構造 — 階層ディレクトリと設定継承で線形スケール

15.2 設計原則

原則説明
設定は上位から継承ルート → envcommon → 環境固有
差分のみ記述共通設定からの差分のみ
依存関係を明示dependency ブロックで宣言
モジュールとライブを分離物理的に分離
バージョン固定モジュール・プロバイダ・ツール
シークレット外部管理SOPS・Secrets Manager 等

15.3 導入ロードマップ

Phase 1: 基礎構築(1-2週間) — ディレクトリ設計、ルート設定、初期移行
Phase 2: DRY化(2-4週間)   — _envcommon、変数階層、dependency
Phase 3: CI/CD(1-2週間)   — パイプライン構築、PR レビュー
Phase 4: ガバナンス(2-4週間)— OPA/tflint/tfsec、トレーニング
Phase 5: 最適化(継続的)    — パフォーマンス、DR、マルチリージョン

15.4 リソース

リソースURL
公式ドキュメントhttps://terragrunt.gruntwork.io/
GitHubhttps://github.com/gruntwork-io/terragrunt
Terraform Registryhttps://registry.terraform.io/
OpenTofuhttps://opentofu.org/
Terratesthttps://terratest.gruntwork.io/

本ドキュメントは 2025 年時点の Terragrunt の情報に基づいて作成されている。Terragrunt は活発に開発が進められており、新しいバージョンでは機能の追加や変更が行われる可能性がある。最新の情報は公式ドキュメントを参照されたい。