Terraformモジュールは「コピペ」するな:GitHub Packagesで築く、鉄壁の社内共有基盤
Terraformモジュールの管理で、いまだに`git clone`やローカルパス参照をしていませんか?
「あのプロジェクトのVPCモジュール、どこまで修正したんだっけ?」といった会話が聞こえてくる時点で、その組織はIaCの迷宮に迷い込んでいます。
真のSREは、インフラを「コード」ではなく「プロダクト」として扱い、Registryという名の流通網に乗せる。今日は、Terraform Registryのプライベート対応をGitHub Packagesで構築し、組織のデリバリー速度を劇的に跳ね上げる「現場の作法」を伝授します。
—
1. なぜ「GitHub Packages」を選ぶのか
多くの組織がTerraform Cloud/Enterpriseのレジストリを利用しますが、コストや権限管理の観点からGitHub Packages(特にGitHub Container Registry – GHCR)を選択するケースが増えています。
選定の極意:
- 認証の統一: GitHub Actionsの`GITHUB_TOKEN`で全て完結する。
- 権限管理: GitHubのOrganization/Team権限に完全準拠。
- 不変性: OCIイメージとして管理されるため、バージョニングが確実。
—
2. 実践:プライベートRegistryの構築手順
Terraformは、`terraform init`時にTerraform Registryプロトコルを喋るサーバーにアクセスします。GitHub Packagesをこれに応答させるには、OCIレジストリとして公開するのが最短ルートです。
ステップ1:モジュールのパッケージ化(`Makefile`の活用)
モジュールを公開する際は、必ず`terragrunt`や`terraform-config-inspect`と組み合わせ、`version`を厳密に管理します。
Makefileの自動化:セマンティックバージョニングを強制する
VERSION := $(shell git describe –tags –always)
.PHONY: publish
publish:
# モジュールをOCIアーティファクトとしてパッケージングしGHCRへプッシュ
# terraform-registry-proxy等のOSSを利用するのが最も現実的
docker build -t ghcr.io/your-org/terraform-modules/vpc:$(VERSION) .
docker push ghcr.io/your-org/terraform-modules/vpc:$(VERSION)
ステップ2:認証設定(`.terraformrc`の神設定)
開発者のローカル環境で最も躓くのが認証です。`~/.terraformrc` (または `~/.terraform.d/plugins.json`) に以下を記述し、認証情報を切り分けます。
~/.terraformrc
credentials “ghcr.io” {
token = “ghp_xxxxxx” # 環境変数 TF_TOKEN_ghcr_io を使うのがベスト
}
—
3. チーム開発を加速させる「極限の知見」
神プラグイン & 設定
Terraformを書く際、以下の環境構築ができていないなら、今すぐ修正してください。
1. `terraform-ls` (Language Server): これなしでの開発は、地図なしで密林を歩くようなもの。VS Codeの`hashicorp.terraform`拡張機能は必須。
2. `tflint`: ルールセットを`.tflint.hcl`で全社配布し、CIで強制的にFailさせる。「なぜダメなのか」をコードレビューで説明する時間をゼロにします。
3. `tfsec` / `terrascan`: セキュリティは開発の初期段階で排除する。
隠れたキーボードショートカット (VS Code):
- `Ctrl + Shift + P` -> `Terraform: Format`: 保存時に実行されるよう設定済みか?(`”editor.formatOnSave”: true`)
- `Ctrl + Click`: モジュール定義へのジャンプ。これができない構成は、依存関係が破綻している証拠。
—
4. モジュール設計のベストプラクティス(YAML管理)
共通基盤として提供する際、`variables.tf`が肥大化するのは悪手です。設定値の複雑な組み合わせは、JSON/YAMLスキーマでバリデーションをかけるのがプロのやり方です。
推奨するディレクトリ構成:
.
├── main.tf
├── variables.tf
├── outputs.tf
├── examples/ # 必須!利用者のためのコピー&ペースト可能なサンプル
├── modules/ # サブモジュール群
└── schema.json # 構造化された入力値の定義
実用的なバリデーション(`variables.tf`):
variable “instance_type” {
type = string
description = “EC2インスタンスタイプ”
# 現場で震えるほど重要な「動的なバリデーション」
validation {
condition = can(regex(“^(t3|m5|c5)\\.”, var.instance_type))
error_message = “許可されていないインスタンスタイプです。コスト管理ポリシーに従ってください。”
}
}
—
5. 最後に:テックリードからの提言
GitHub Packagesをレジストリとして利用することは、単なる「共有基盤」を作る作業ではありません。「チームがインフラの書き方を学ぶための教科書」を公開する行為です。
1. ドキュメントは `README.md` ではなく `examples/` で語れ。
2. モジュールは「疎結合」に保て。 複数のリソースを詰め込んだ巨大モジュールは、ただの「負債」です。
3. CI/CDを通さない変更は「存在しないもの」として扱え。
この基盤を構築すれば、新メンバーが参画したその日に、彼らは「社内の標準的なVPC」を一行のコードで呼び出せるようになります。その瞬間こそが、エンジニアリング組織がスケールする瞬間です。
さあ、今すぐレポジトリを整理し、Registryを立ち上げましょう。コードが標準化されれば、世界はもっとシンプルになるはずです。