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 の歴史
| 年 | バージョン/イベント | 主な変更点 |
|---|---|---|
| 2016 | v0.1.0 | 初期リリース。Terraform のバックエンド設定の DRY 化に焦点 |
| 2017 | v0.12.x | terraform.tfvars ベースの設定体系 |
| 2019 | v0.19.x | terragrunt.hcl への設定ファイル名変更、HCL2 構文サポート |
| 2020 | v0.23.x | dependency ブロックの導入、run-all コマンドの追加 |
| 2021 | v0.31.x | generate ブロックの強化、catalog 機能の初期実装 |
| 2022 | v0.38.x | read_terragrunt_config() の安定化、パフォーマンス改善 |
| 2023 | v0.50.x | Terraform の OpenTofu フォーク対応、Engine の初期実装 |
| 2024 | v0.55.x〜v0.67.x | Terragrunt Engine(実験的)、Stacks 機能の強化 |
| 2025 | v0.70.x〜 | Engine の安定化、Terragrunt Provider の成熟 |
1.4 Terragrunt のライセンスとエコシステム
Terragrunt は MIT ライセンス で提供されるオープンソースソフトウェアであり、Gruntwork 社のエコシステムの中核をなす。
| コンポーネント | 説明 |
|---|---|
| Terragrunt | Terraform ラッパー(本ドキュメントの主題) |
| Terragrunt Engine | Terraform/OpenTofu の実行エンジン抽象化レイヤー |
| Gruntwork IaC Library | 本番品質の Terraform モジュール群(商用) |
| Gruntwork Pipelines | CI/CD パイプライン(商用) |
| Terratest | Go 言語によるインフラテストフレームワーク |
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_terragrunt | Terragrunt が生成したファイルのみ上書き。手動作成のファイルは保護される(推奨) |
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 の重要性
- 初回デプロイ時: 依存先未デプロイでも
validate/planが実行可能 - CI/CD パイプライン: 依存先ステートにアクセスできない環境でも検証可能
- ローカル開発: すべての依存先をデプロイする必要がない
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-all は dependency ブロックに基づいて依存関係グラフを構築し、正しい順序で実行する。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
| 観点 | Terraform | OpenTofu |
|---|---|---|
| ライセンス | BSL 1.1 | MPL 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 credentials | aws sts get-caller-identity で確認 |
Backend configuration changed | terragrunt init -reconfigure |
Error acquiring state lock | terragrunt 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 ブランチ戦略
- main ブランチ保護(PR 経由のみ)
- PR マージ → dev 自動 apply → staging 手動 → production 手動(承認必須)
- 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-tfpath | Terraform バイナリパス |
--terragrunt-non-interactive | 非対話モード |
--terragrunt-parallelism | 並列度 |
--terragrunt-source | モジュールソース上書き |
--terragrunt-exclude-dir | 除外ディレクトリ |
--terragrunt-log-level | ログレベル |
13.4 環境変数
| 変数 | 説明 |
|---|---|
TERRAGRUNT_TFPATH | Terraform バイナリパス |
TERRAGRUNT_LOG_LEVEL | ログレベル |
TERRAGRUNT_NON_INTERACTIVE | 非対話モード |
TERRAGRUNT_PARALLELISM | 並列度 |
TERRAGRUNT_SOURCE | モジュールソース上書き |
TERRAGRUNT_IAM_ROLE | IAM ロール ARN |
14. Terraform との比較と使い分け
14.1 比較表
| 観点 | Terraform のみ | Terraform + Terragrunt |
|---|---|---|
| 学習曲線 | 低い | 中程度 |
| DRY | 部分的 | 徹底的 |
| バックエンド管理 | 手動 | 自動 |
| 依存関係 | terraform_remote_state | dependency ブロック |
| 一括操作 | スクリプト自作 | run-all |
| 推奨規模 | 小規模 | 中〜大規模 |
14.2 類似ツール
| ツール | 概要 |
|---|---|
| Terraform Cloud/Enterprise | HashiCorp 公式マネージド |
| Terraspace | Ruby ベースフレームワーク |
| Atmos | YAML ベーススタック管理 |
| Spacelift | CI/CD + ステート管理 SaaS |
| env0 | 環境管理プラットフォーム |
15. まとめ
15.1 核心的価値
- DRY 原則の徹底 —
include,generate,_envcommonで設定重複を最小化 - 依存関係の明示化 —
dependency+run-allで正しい実行順序を保証 - スケーラブルな構造 — 階層ディレクトリと設定継承で線形スケール
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/ |
| GitHub | https://github.com/gruntwork-io/terragrunt |
| Terraform Registry | https://registry.terraform.io/ |
| OpenTofu | https://opentofu.org/ |
| Terratest | https://terratest.gruntwork.io/ |
本ドキュメントは 2025 年時点の Terragrunt の情報に基づいて作成されている。Terragrunt は活発に開発が進められており、新しいバージョンでは機能の追加や変更が行われる可能性がある。最新の情報は公式ドキュメントを参照されたい。