Infrastructure as Code (IaC) の代表的なツールであるTerraformは非常に強力ですが、プロジェクトが大規模になり、開発、ステージング、本番といった複数の環境を管理するようになると、いくつかの課題に直面します。
特に、環境ごとに同じようなコードを繰り返し記述すること(コードの重複)や、tfstateファイルの管理の複雑化は、多くの開発者が悩むポイントです。
そんなTerraformの課題を解決し、より快適なIaCライフを実現してくれるのが、本記事で紹介する「Terragrunt」です。
Terragruntとは?
Terragruntは、Gruntwork社が開発したTerraformをより便利に使うための「ラッパーツール」です。
ラッパーツールとは、既存のツールの機能を包み込み、さらに便利な機能を追加するものです。
Terragruntは、Terraformのコマンドを内部で呼び出すため、Terraformに慣れている開発者にとっては非常に導入しやすいツールです。
Terragruntの主な目的は、TerraformのコードをDRY (Don't Repeat Yourself - 繰り返しを避ける)に保ち、複数の環境やモジュールの管理を簡素化することにあります。
Terragruntの主な魅力
Terragruntを導入することで、主に以下のメリットが得られます。
1. 設定をDRY(ドライ)に保ち、コードの重複を削減
複数環境をTerraformで管理する際、providerやbackend(tfstateの保存場所)の設定を環境ごとに記述する必要があり、コードの重複が発生しがちです。
Terragruntでは、terragrunt.hclという設定ファイルを用いて、共通の設定を親ディレクトリに集約できます。
各環境のterragrunt.hclファイルは、includeブロックを使って親の設定を継承できるため、環境固有の変数や設定を記述するだけで済み、コードの重複を劇的に削減できます。
2. tfstate(状態ファイル)の管理を自動化・簡素化
Terraformでは、tfstateをチームで共有するためにS3バケットなどのリモートバックエンドを手動で準備する必要がありますが、Terragruntは初回実行時にリモートバックエンド用のS3バケットやDynamoDBテーブルを自動で作成してくれます。
さらに、Terragruntはモジュールごとにtfstateを自動的に分割して管理します。
これにより、tfstateファイルが巨大化するのを防ぎ、terraform planやapplyの実行時間を短縮できます。
3. モジュール間の依存関係を管理し、一括実行を可能に
インフラを構築する際、VPCを先に作成し、その後にEC2インスタンスを作成するといった依存関係が存在します。
Terraform単体では、これらの実行順序を手動で管理する必要があります。
ここで、「Terraformにもdepends_onがあるのでは?」という鋭い指摘があるかもしれません。
確かにおっしゃる通り、Terraformにはdepends_onという機能があり、単一の構成(同じtfstateを共有するリソース群)内での実行順序を明示的に制御できます。
しかし、プロジェクトが大規模になると、VPC、データベース、アプリケーションといった単位でTerraformの構成を完全に分割し、それぞれが独立したtfstateを持つことが一般的です。
Terraform単体では、これら分離された構成間の実行順序を管理することはできません。
この課題を解決するのが、Terragruntのdependencyブロックです。
このブロックを使えば、分離されたモジュール間の依存関係を明示的に定義できます。
そしてterragrunt run-all applyのようなコマンドを実行すると、Terragruntが依存関係を自動で解決し、正しい順序で全てのモジュールを一括で適用してくれるのです。
簡単な実装例
Terragruntを使った典型的なディレクトリ構成と設定ファイルの例を見てみましょう。
ディレクトリ構成
環境ごとに設定を分けつつ、共通のモジュールを再利用する構成が一般的です。
├── modules/
│ └── vpc/
│ ├── main.tf
│ └── variables.tf
├── envs/
│ ├── dev/
│ │ └── vpc/
│ │ └── terragrunt.hcl # dev環境のVPC設定
│ └── prod/
│ └── vpc/
│ └── terragrunt.hcl # prod環境のVPC設定
└── terragrunt.hcl # 全環境共通のルート設定
設定ファイルの例
1. ルートの terragrunt.hcl (共通設定)
全環境で共通のremote_stateやproviderの設定を記述します。
path_relative_to_include()関数を使うことで、tfstateの保存パス(key)を動的に生成し、モジュールごとに自動で分割します。
terragrunt.hcl (ルート)
remote_state {
backend = "s3"
config = {
bucket = "my-terragrunt-tfstate-bucket"
key = "${path_relative_to_include()}/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "my-terragrunt-lock-table"
}
generate = {
path = "backend.tf"
if_exists = "overwrite_terragrunt"
}
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<-EOF
provider "aws" {
region = "ap-northeast-1"
}
EOF
}
各環境の terragrunt.hcl (個別設定)
includeブロックでルートの設定を読み込み、terraformブロックで利用するモジュールのパスを指定します。 inputsブロックには、その環境固有の変数を定義します。
# envs/dev/vpc/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/vpc"
}
inputs = {
vpc_name = "dev-vpc"
cidr_block = "10.10.0.0/16"
}
まとめ
Terraform単体でもIaCは実現できますが、プロジェクトが成長し、環境が増えるにつれて管理の複雑性が増していきます。
Terragruntは、そうしたTerraformの運用における「かゆいところ」に手が届く強力なツールです。
コードのDRY化、state管理の自動化、依存関係の解決といった機能により、インフラコードの保守性・再利用性を高め、より安全で効率的なインフラ管理を実現します。
もしあなたが複数環境の管理やコードの重複に課題を感じているなら、Terragruntの導入を検討してみてはいかがでしょうか。

