Terraformモジュール設計の「死の罠」:生産性を殺す5つのアンチパターンと極限の最適化戦略
現場でTerraformを触っていると、「モジュール化=正義」という呪いにかかったコードベースによく出会う。再利用性を追求したはずが、いざ修正しようとすると依存関係の迷宮で身動きが取れなくなる。そんな「負債のモジュール化」は今すぐやめるべきだ。
真にスケーラブルなインフラを構築するために、我々が避けるべき5つのアンチパターンと、それを打破するための実戦的テクニックを伝授する。
—
1. モジュール設計の「死の罠」5選
① 「過剰な抽象化」という名のラッパー地獄
`module “vpc” { source = “./modules/vpc” }` のような、単にAWSリソースをラップしただけの薄いモジュールを量産していないか?
- 弊害: 変更のたびに親モジュールと子モジュールを行き来し、変数の受け渡し(`variable` / `output`)の海で溺れることになる。
- 原則: 「論理的な境界」がない限り、モジュール化するな。単なる記述の省略はDRYの誤用だ。
② 「密結合なモノリス」モジュール
ネットワーク、DB、認証を一括でデプロイする「オールインワン・モジュール」は最悪のアンチパターンだ。
- 弊害: DBのパラメータを一つ変えたいだけなのに、ネットワーク層のPlanまで走り、破壊的変更のリスクを負う。
- 原則: リソースのライフサイクルが異なるものは、物理的に別モジュールに切り離せ。
③ 「条件分岐の魔境」
`count` や `for_each` を駆使し、巨大な `if-else` ロジックを詰め込んだモジュールはメンテナンス不能だ。
- 弊害: コードが複雑化し、Terraformのプラン結果が予測不可能になる。
- 原則: 複雑な条件分岐が必要なら、それは「別のモジュールを作るべきサイン」だ。
④ 「隠蔽された依存関係」
モジュール内で `data` ソースを多用し、外部リソースを暗黙的に参照する設計。
- 弊害: どこから値を参照しているか追跡できず、削除時の影響範囲が不明になる。
- 原則: 必要な値は必ず `variable` として明示的に渡せ。
⑤ 「ハードコードされた値」の散乱
環境名やドメイン名がモジュール内に直書きされている。
- 弊害: ステージングと本番で微妙な差異が生まれ、環境間不一致の温床になる。
- 原則: 構成値は `tfvars` に完全に追い出し、モジュールは「純粋な関数」として保て。
—
2. 開発スピードを劇的に高める「プロの装備」
モジュール設計以上に重要なのが、日々のオペレーションの質だ。
必須プラグイン(VS Code環境)
- HashiCorp Terraform: 公式拡張。これなしでは始まらない。
- Terraform DocGen: コメントからREADMEを自動生成する。ドキュメントを書かない言い訳は不要だ。
- TFLint: `terraform validate` では検知できない、クラウド側のベストプラクティス違反をCI前に叩く。
現場で刺さるショートカット
- `Ctrl+Shift+P` -> `Terraform: Format`: 保存時に自動実行する設定(`editor.formatOnSave`)は必須。
- `Alt+Shift+F`: コードの整形を矯正する。汚いコードはレビュー対象外にするのが規律。
—
3. 実践的ベストプラクティス:構造化の極意
設定ファイルは、人間が読みやすく、かつマシンが処理しやすい構造であるべきだ。以下は、環境別設定(`terragrunt`的な思想)の構成例だ。
main.tf (親モジュール)
モジュールは常に「疎結合」かつ「疎結合」に保つ
module “web_server” {
source = “../../modules/compute”
# 変数は明示的に渡す(隠蔽しない)
instance_type = var.instance_type
subnet_id = var.subnet_id
# 複雑な設定はJSON/YAMLから読み込むのが定石
tags = jsondecode(file(“${path.module}/config/tags.json”))
}
`config/tags.json` の構成例:
{
“Project”: “CloudMigration”,
“Environment”: “Production”,
“ManagedBy”: “Terraform”
}
- なぜJSONか: Terraformの `jsondecode` 関数と相性が良く、他のツール(PythonやGo)からも容易に読み書きできるため、CIパイプラインの自動化で圧倒的な威力を発揮する。
—
4. チーム開発における「絶対ルール」
1. Planの自動化: どのブランチにマージする場合でも、必ず `terraform plan` がCIで通り、出力結果がPRにコメントされる環境を作れ。
2. Stateの分離: `terraform.tfstate` は最小単位(アカウント単位、あるいは機能単位)で分割せよ。巨大なStateは、壊れた瞬間に地獄を見る。
3. 変数のバリデーション: `variable` ブロックには必ず `validation` を書け。
variable “instance_type” {
type = string
description = “EC2インスタンスタイプ”
# 型だけでなく「値の妥当性」を強制する
validation {
condition = can(regex(“^t3\\.”, var.instance_type))
error_message = “コスト削減のため、t3ファミリーのみ許可されています。”
}
}
最後に
Terraformは単なる「設定ファイル」ではない。インフラの設計思想そのものだ。
モジュールをただの部品として扱うな。モジュールは「インフラの疎結合なAPI」であると認識せよ。
美しいコードは、美しい設計からしか生まれない。さあ、今すぐ不要なラッパーモジュールを削除し、疎結合でテスト可能なインフラへとリファクタリングを始めよう。君のコードが、次のデプロイで悲鳴を上げないために。