Terragruntの循環依存がサイレントに失敗する問題と検出方法
このメモはこんな人向け
- Terragruntで複数モジュールを運用している/これから導入する人
- CI/CDで
terragrunt validate --allを回している(あるいは回したい)人 - VPC Peeringやクロススタック参照など、モジュール間依存が多い構成を扱う人
はじめに
Terragruntで複数のモジュール間に循環依存(circular dependency)を作ってしまうと、どうなるのか?
今回、AWS VPC Peeringの実装を通じて、Terragruntが循環依存をサイレントに無視してハングするという重大な問題を発見しました。
この記事では、問題の再現方法、検出手段、そして汎用的な検出スクリプトの作成方法を紹介します。
TL;DR
-
terragrunt validate --allは循環依存があってもエラーを出さずにハングする - 個別実行 (
cd module && terragrunt validate) では循環依存エラーが表示される - 循環依存を確実に検出するには、全モジュールを個別にvalidateする必要がある
- 汎用的な検出スクリプトを作成して解決
実験: 循環依存の作成
構成
AWS VPC Peeringで相互参照する構成を意図的に作成しました。
infra/
├── tf/modules/
│ ├── network1/ # VPC + Peering (network2へ)
│ └── network2/ # VPC + Peering (network1へ)
└── tg/live/dev/
├── network1/terragrunt.hcl
└── network2/terragrunt.hcl
モジュール構成
modules/network1/main.tf:
resource "aws_vpc" "this" {
cidr_block = "10.1.0.0/16"
tags = { Name = "vpc-network1" }
}
resource "aws_vpc_peering_connection" "to_network2" {
vpc_id = aws_vpc.this.id
peer_vpc_id = var.peer_vpc_id # network2のVPC IDが必要
auto_accept = true
tags = { Name = "peering-network1-to-network2" }
}
modules/network2/main.tf:
resource "aws_vpc" "this" {
cidr_block = "10.2.0.0/16"
tags = { Name = "vpc-network2" }
}
resource "aws_vpc_peering_connection" "to_network1" {
vpc_id = aws_vpc.this.id
peer_vpc_id = var.peer_vpc_id # network1のVPC IDが必要
auto_accept = true
tags = { Name = "peering-network2-to-network1" }
}
Terragrunt設定で循環依存を作る
live/dev/network1/terragrunt.hcl:
terraform {
source = "../../../../tf/modules/network1"
}
# network2に依存
dependency "network2" {
config_path = "../network2"
mock_outputs = {
vpc_id = "vpc-mock-network2"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
inputs = {
peer_vpc_id = dependency.network2.outputs.vpc_id
}
live/dev/network2/terragrunt.hcl:
terraform {
source = "../../../../tf/modules/network2"
}
# network1に依存(循環!)
dependency "network1" {
config_path = "../network1"
mock_outputs = {
vpc_id = "vpc-mock-network1"
}
mock_outputs_allowed_terraform_commands = ["validate", "plan"]
}
inputs = {
peer_vpc_id = dependency.network1.outputs.vpc_id
}
これで network1 → network2 → network1 の循環依存が完成です。
問題発見: サイレントな失敗
--all オプションでハングする
$ cd infra/tg/live/dev
$ terragrunt validate --all
# ... 何も出力されず、永遠に待機 ...
エラーメッセージなし。タイムアウトまで応答なし。
これは非常に危険で、CI/CDパイプラインで使用すると、原因不明のタイムアウトに悩まされることになります。
ここで次の方法を使います!
個別実行では検出される!
$ cd infra/tg/live/dev/network1
$ terragrunt validate
ERROR: Found a dependency cycle between modules:
./terragrunt.hcl -> ../network2/terragrunt.hcl -> ./terragrunt.hcl
個別実行では明確にエラーが表示されます。
試した検出方法
様々な方法を試して、循環依存を検出できるか検証しました。
| コマンド | 結果 | 循環依存検出 |
|---|---|---|
terragrunt validate --all |
ハング | ❌ |
terragrunt dag graph |
ハング | ❌ |
terragrunt output-module-groups |
ハング | ❌ |
terragrunt validate --all --terragrunt-log-level debug |
ハング | ❌ |
cd module && terragrunt validate |
エラー表示 | ✅ |
terragrunt find --json |
成功(ユニットをリスト) | ⚠️ |
| |
有効な検出方法
✅ 方法1: 個別validate(最も確実)
cd network1
terragrunt validate
# ERROR: Found a dependency cycle between modules...
✅ 方法2: タイムアウトで検出
timeout 5 terragrunt validate --all
if [ $? -eq 124 ]; then
echo "❌ Timeout - likely circular dependency!"
fi
終了コード 124 = タイムアウト = 循環依存の可能性
✅ 方法3: すべてのモジュールを自動チェック
echo "=== network1 ===" && \
(cd network1 && terragrunt validate 2>&1) | grep -i cycle
echo "=== network2 ===" && \
(cd network2 && terragrunt validate 2>&1) | grep -i cycle
解決策: 汎用検出スクリプト
現在のディレクトリ配下のすべての terragrunt.hcl を自動検出し、循環依存をチェックするスクリプトを作成しました。
check-circular-deps.sh:
#!/bin/bash
# Terragrunt循環依存チェックスクリプト
# 使い方: ./check-circular-deps.sh [root_directory]
set -e
ROOT_DIR="${1:-.}"
CYCLE_FOUND=false
TOTAL_CHECKED=0
CYCLE_COUNT=0
echo "🔍 Checking for circular dependencies in Terragrunt modules..."
echo "Root directory: $(cd "$ROOT_DIR" && pwd)"
echo ""
# terragrunt.hclファイルを再帰的に検索
while IFS= read -r -d '' hcl_file; do
dir=$(dirname "$hcl_file")
relative_path=$(realpath --relative-to="$ROOT_DIR" "$dir")
TOTAL_CHECKED=$((TOTAL_CHECKED + 1))
echo "[$TOTAL_CHECKED] Checking: $relative_path"
# 各ディレクトリでterragrunt validateを実行
if output=$(cd "$dir" && terragrunt validate 2>&1); then
echo " ✓ No issues"
else
# エラー出力から循環依存を検出
if echo "$output" | grep -qi "cycle"; then
echo " ❌ CIRCULAR DEPENDENCY DETECTED"
echo "$output" | grep -i "cycle" | sed 's/^/ /'
CYCLE_FOUND=true
CYCLE_COUNT=$((CYCLE_COUNT + 1))
else
# その他のエラー
echo " ⚠️ Other error (not circular dependency)"
echo "$output" | head -3 | sed 's/^/ /'
fi
fi
echo ""
done < <(find "$ROOT_DIR" -name "terragrunt.hcl" -type f -print0)
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Summary:"
echo " Total modules checked: $TOTAL_CHECKED"
echo " Circular dependencies found: $CYCLE_COUNT"
echo ""
if [ "$CYCLE_FOUND" = true ]; then
echo "❌ 循環依存が見つかりました!修正が必要です。"
exit 1
else
echo "✅ 循環依存は見つかりませんでした。"
exit 0
fi
使用方法
# カレントディレクトリ以下をチェック
./check-circular-deps.sh
# 特定のディレクトリをチェック
./check-circular-deps.sh tg/live/dev
# CI/CDで使用
./check-circular-deps.sh || exit 1
実行例
🔍 Checking for circular dependencies in Terragrunt modules...
Root directory: /path/to/infra/tg/live/dev
[1] Checking: network1
❌ CIRCULAR DEPENDENCY DETECTED
* Found a dependency cycle between modules: ./terragrunt.hcl -> ../network2/terragrunt.hcl -> ./terragrunt.hcl
[2] Checking: network2
❌ CIRCULAR DEPENDENCY DETECTED
* Found a dependency cycle between modules: ./terragrunt.hcl -> ../network1/terragrunt.hcl -> ./terragrunt.hcl
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Summary:
Total modules checked: 2
Circular dependencies found: 2
❌ 循環依存が見つかりました!修正が必要です。
VPC Peeringの正しい実装パターン
循環依存を避けるには、以下のいずれかの方法を使用します:
パターン1: 2フェーズアプローチ
- Phase 1: VNetのみ作成
-
Phase 2: Peeringを別モジュールで作成
パターン2: 片方向依存
network1 (独立してVPC作成)
↑
network2 (network1のVPC IDを参照してPeering作成)
network2で両方向のPeeringを管理します。
パターン3: 専用Peeringモジュール
network1 (VPCのみ)
network2 (VPCのみ)
↓
peering (両方のVPC IDを参照)
まとめ
問題点
terragrunt validate --allは循環依存を検出できない- エラーメッセージなしでハングする
-
デバッグログを有効にしても情報が得られない
解決策
- 個別validateで確実に検出
- 汎用スクリプトで自動化
-
CI/CDに組み込んで早期発見
推奨事項
- CI/CDパイプラインに循環依存チェックを組み込む
-
--allオプションには過度に依存しない - 設計段階で依存関係を可視化する
- VPC Peeringなど相互参照が必要な場合は、専用モジュールを検討
参考リンク
あるべき状態(Definition of Done)
- 循環依存が存在する場合はCI/CDで即時に検知・失敗する
-
terragrunt validate --allのみに依存せず、全モジュール個別validateまたは検出スクリプトを組み込んでいる - 依存関係設計が可視化され、VPC Peeringなど相互参照は専用モジュール/片方向/2フェーズなど安全なパターンで実装されている
- 開発/本番いずれの環境でも循環依存検査のチェックリストが運用されている
- Terragrunt公式ドキュメント - Dependencies
-
GitHub Issue: Circular dependency detection
Environment:
- Terragrunt: latest
- Terraform: 1.0+
- AWS Provider: 6.0+
この記事が、Terragruntの循環依存問題に悩む方の助けになれば幸いです!