本記事は
IaCウィーク
4日目の記事です。
⚙️
3日目
▶▶ 本記事 ▶▶
5日目
💻

はじめに
こんにちは。加藤です。
IaCウィークということでTerraform管理構成に対する考察を運用観点からまとめていきたいと思います。今回は主にリポジトリ構成とディレクトリ構成に焦点を当てて見ていきます。
まとめ
先にまとめです。
リポジトリ構成
| メリット | デメリット | |
|---|---|---|
| Mono Repo | ・シンプルで見通しが良い ・CI/CDを統一可能 |
・検証フローがやや煩雑 ・責任分界点を定義しにくい |
| Multi Repo | ・モジュールのバージョン管理が可能 ・責任分界点を定義しやすい |
・管理運用コストが増加 ・全体の見通しが悪くなる |
ディレクトリ構成(実行ディレクトリ)
| メリット | デメリット | |
|---|---|---|
| 環境レベル | ・保守性が高い ・stateを一つにまとめることができる |
・実行時間が遅くなる可能性がある ・局所的な更新ができない |
| コンポーネントレベル(Terragrunt利用) | ・実行時間が短い ・局所的な変更も可能 |
・可読性が低い ・Terragruntの学習コストが高い |
詳細は次項以降で説明します。
Terraform運用の品質を維持していくために
Terraformを運用していく中で、システムや組織が成長することで「リポジトリが大きくなりすぎて見通しが悪い」「同じリポジトリに雑多なコードが入っており責任分界点が曖昧」「依存関係が複雑で影響範囲が不透明」「単一のstateファイルの管理リソースが多すぎて実行時間が長くなる」など、課題が出てくるかと思います。
「動いているから大丈夫」「(多分)有識者は全容を把握しているから分からなければ聞けば何とかなる」と後回しにされがちですが、品質を維持しつつ運用保守するためには、上記のような課題が障壁となり得ることを理解しておく必要があります。
このような課題に対する緩和策としてリポジトリ構成やディレクトリ構成の見直しが考えられます。本記事では、それらの具体的なアプローチについて紹介します。
リポジトリ構成
まず、Terraformコードを管理するリポジトリをどのように管理運用するか、という点です。
ここでは、大きく分けて2つの管理パターンを説明します。
Mono Repo
単一のリポジトリでTerraformコードを管理するMono Repoです。
以下に具体的な構成例を示します。

- 実行ディレクトリ
- 環境毎に配置
devとprodの二環境を想定- stateファイルの単位となる
- モジュールディレクトリを参照する
- モジュールディレクトリ
- モジュールを配置
networkやfunctionなどライフサイクル毎でリソースをまとめて定義
単一のリポジトリにシステムを構成するインフラストラクチャリソースが全て含まれているため、可視性が高く保守性が高いことやTerraformのCI/CD(検証や静的チェック、applyの方法など)を統一化できるなどの特徴があります。チーム開発においてはアクセス制限や承認フローを統一化できるため、シンプルな運用を敷くことができます。
ただ、検証観点で考えた場合に、実行ディレクトリとモジュールディレクトリが同じリポジトリで共存しているため、モジュールディレクトリを更新してしまうと全ての実行ディレクトリに影響が出てしまいます。特にCDでplan/applyを自動化している場合などでは、devで検証を終えてからprodに反映させる、という開発フローとするために、CDワークフローで条件分岐させたり、運用でカバーする必要があったり、と何かと考慮が必要となります。
また、組織によってはガバナンスを効かせるためチーム毎で管理リソースを分割する場合、単一のリポジトリにソースが入っていると制御が難しくなる可能性があります。
Multi Repo
続いてMulti Repoです。
Mono Repoとは対照に複数のリポジトリでTerraformコードを管理する方法です。
ここでは、実行ディレクトリ(A)とモジュールディレクトリ(B)を分割する、ということを考えます。(他にもシステム内のサブモジュールを目的や役割に応じて分割するアプローチもあるかと思いますが、本記事では取り扱いません。)

- Terraformリポジトリ(A)
- 実行ディレクトリのみ管理
- Terraformリポジトリ(B)をGitタグで参照
- システム担当者が管理運用
- Terraformリポジトリ(B)
- モジュールディレクトリのみ管理
- モジュール管理者が管理運用
Terraformで別リポジトリのモジュールを呼び出すためには、moduleブロックのsourceで以下のように指定する必要があります。ここではGitタグを指定することで特定のバージョンを呼び出すようにしています。
module "network" {
source = "git::<protocol>://<host>/terraform-modules-repo.git//network?ref=v1.0.0"
}
module block reference | Terraform | HashiCorp Developer
モジュールを別リポジトリで管理することで、環境毎でモジュールのバージョンを指定できるようになります。そのため開発フローに沿って環境を順番に更新していくことが容易となります。また、リポジトリの管理チームを分割することで、責任分界点も明確となるためガバナンスを効かせやすくなります。例ではモジュールを単一のリポジトリにまとめましたが、例えば「ネットワーク管理チーム」「セキュリティ管理チーム」のような棲み分けでリポジトリを分割することもできるかと思います。
また、組織内の別チームにリポジトリを展開してコードの共通化を図ることも可能です。モジュールリポジトリの変更頻度が高いと運用負荷が高くなるため、ある程度汎用的な設計としておくことが望ましいと思います。
もちろん、複数のリポジトリを管理するということはその分管理運用コストが上がるということです。リポジトリを一つ管理するだけでも「ブランチ運用はどうするか?」「PR運用はどうするか?」など考えることは多岐に渡りますし、チームを跨ぐことでコミュニケーションコストなども発生することが想定されます。また、コードの可視性が低下するため全体像がパッと分からずシステム理解の障壁となってしまう可能性もあります。
Terraformレジストリの利用
リポジトリ構成の話からは少し逸れますが、モジュールを参照する場合以下のように一般公開されているTerraformモジュールを利用することも有効です。
HCP Terraformのアカウントがあれば独自モジュールをプライベートレジストリに登録して組織内で運用することも可能です。具体的なレジストリへの登録手順は以下を参考にしてください。
ディレクトリ構成
次に、Terraformコードのディレクトリ構成についてです。
Terraformでは1つの実行ディレクトリに対して1つのstateファイルが適用されます。つまり実行ディレクトリの切り方でstateファイルの粒度が変わってくるということです。
ここでは、2つのパターンについて見ていきます。主に実行ディレクトリに焦点を当てたいため、モジュールディレクトリの構成については省略します。
環境ディレクトリ直下にtfファイルを配置
まず、以下のような構成を考えます。
. └── aws ├── dev │ ├── backend.tf │ ├── data.tf │ ├── locals.tf │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ └── versions.tf └── prod ├── backend.tf ├── data.tf ├── locals.tf ├── main.tf ├── outputs.tf ├── provider.tf └── versions.tf
- awsディレクトリ
- プロバイダーディレクトリ
- プロバイダー毎でディレクトリは分ける
- dev/prodディレクトリ
- 環境ディレクトリ
- 直下にmain.tf等を配置する
- tfファイル
- 各ファイルの目的は こちら を参照
環境毎にtfファイルを設置することで、一度のTerraform実行で全てのインフラストラクチャリソースを一括で構築できます。stateを跨がない構成であるため、同じ実行ディレクトリであればstate内のリソースを直接参照することが可能です。また、コードの更新時には実装箇所が明確であるため保守性にも優れています。
ただし、リソースの数が増えるにつれて実行時間が長くなるというデメリットがあります。また、リソースを一括管理しているため局所的な更新ができず、影響範囲が広くなりがちです。
コンポーネントディレクトリにtfファイルを配置
環境ディレクトリ配下にコンポーネントディレクトリを配置するパターンです。
.
└── aws
├── dev
│ ├── network
│ │ ├── backend.tf
│ │ ├── data.tf
│ │ ├── locals.tf
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── provider.tf
│ │ └── versions.tf
│ └── function
│ ├── backend.tf
│ ├── data.tf
│ ├── locals.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── versions.tf
└── prod
├── network
│ ├── backend.tf
│ ├── data.tf
│ ├── locals.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── provider.tf
│ └── versions.tf
└── function
├── backend.tf
├── data.tf
├── locals.tf
├── main.tf
├── outputs.tf
├── provider.tf
└── versions.tf
- コンポーネントディレクトリ
- 各コンポーネント単位(ここではモジュール単位)で分割
上記のような構成とすれば、単一のstateファイルで管理するリソース数を減らすことができ実行時間を短縮することができます。また、局所的な更新も可能です。
ただ、パッと見で分かる通り同じようなtfファイルを各ディレクトリに作成する必要があります。運用保守の観点からはあまり良くない状態なので、この辺りをDRYに保つために Terragrunt の活用が見込めます。
Terragrunt
Terragrunt は Gruntwork社 が開発しているTerraformのラッパーツールです。
先ほど紹介したディレクトリ構成をTerragruntを用いた場合は以下のようにまとめることができます。
├── root.hcl # ルート設定(共通設定)
└── aws
├── dev
│ ├── network
│ │ └── terragrunt.hcl
│ └── function
│ └── terragrunt.hcl
└── prod
├── network
│ └── terragrunt.hcl
└── function
└── terragrunt.hcl
Terragruntの特徴として上位ディレクトリの定義ファイルを継承することができます。
include "root" {
path = find_in_parent_folders("root.hcl")
}
トップディレクトリに共通設定を集中させてコードをDRYに保ちます。定義ファイルでは変数や関数を用いて各ディレクトリに適した形でファイルを作成することが可能です。
またrun --all planを実行することで実行ディレクトリ配下の全てのサブディレクトリでterraform planを実行することが可能です。例えば、devディレクトリでrun --all planを実行するとdev/networkとdev/functionのPlan結果が出力されます。
細かい設定方法は割愛しますが、このようにTerragruntを活用すればTerraformコードを良い感じにDRYに保つことができます。
ただし、動的にファイルが生成されるためコードの可読性が下がると考えています。また、特定バージョンからは裏ではOpenTofuが利用されていることも把握しておきましょう。Terraform/Terragrunt/OpenTofuのバージョン互換は下記を参照して下さい。
さいごに
今回は、運用観点でTerraformのリポジトリ構成とディレクトリ構成について見てきました。
2つの観点について振り返ります。
[再掲]リポジトリ構成
| メリット | デメリット | |
|---|---|---|
| Mono Repo | ・シンプルで見通しが良い ・CI/CDを統一可能 |
・検証フローがやや煩雑 ・責任分界点を定義しにくい |
| Multi Repo | ・モジュールのバージョン管理が可能 ・責任分界点を定義しやすい |
・管理運用コストが増加 ・全体の見通しが悪くなる |
リポジトリ構成は、まずはMono Repoで運用を開始し組織やチームの情勢を鑑みてMulti Repoへの移行を検討する、という形が良いかと思います。
[再掲]ディレクトリ構成(実行ディレクトリ)
| メリット | デメリット | |
|---|---|---|
| 環境レベル | ・保守性が高い ・stateを一つにまとめることができる |
・実行時間が遅くなる可能性がある ・局所的な更新ができない |
| コンポーネントレベル(Terragrunt利用) | ・実行時間が短い ・局所的な変更も可能 |
・可読性が低い ・Terragruntの学習コストが高い |
まずは、環境レベルでの管理として、実行時間が気になったりコンポーネントを分けた運用がしたいとなった場合にコンポーネントレベルでの構成にシフトすることを検討する、という具合かと思います。
余談ですが、私が最初にTerraformに触れた案件ではMulti Repo & Terragrunt構成となっていました。参画当初はなぜこのような構成になっているのか(見にくくて辛い)と考えていましたが、実運用をしていく中でメリット・デメリットを理解できるようになりました。
ただ、やはり初学者にはハードルが高く思えますので、まずはシンプルな構成から入門することをオススメします。

