10. モジュールバージョニング
共有モジュールを Git tag + semver でバージョン公開し、利用側は ?ref=v1.2.3 で固定する。これだけで「いつ誰が module を更新しても、すぐに本番に伝播しない」運用が成立。
なぜバージョニングが要るか
モジュールを source = "git::...?ref=main" のように main に向けると、「main が更新された瞬間に、そのモジュールを使う全プロジェクトが影響を受ける」状態になります。
- 事故の伝播: モジュール作者の小さな変更が、知らないうちに 30 プロジェクトの本番に届く
- 予測不能な plan: 同じコードでも昨日と今日の plan が違う
- 巻き戻しが困難: 「2 日前のモジュール状態」に戻すのに git の履歴を遡る必要
tag でバージョン固定すれば、利用側は 「自分が選んだバージョン」を明示的にしか取り込まない。
semver の基本
semver = Semantic Versioning。MAJOR.MINOR.PATCH の 3 数字。
| 区分 | 変更内容 | 例 |
|---|---|---|
| MAJOR | 後方互換のない破壊的変更 | 1.4.0 → 2.0.0 |
| MINOR | 後方互換のある機能追加 | 1.4.0 → 1.5.0 |
| PATCH | 後方互換のあるバグ修正 | 1.4.0 → 1.4.1 |
Terraform module の場合 「MAJOR を上げる = 利用側の variable や output を変える」 と考えると分かりやすい。
Git tag で公開
モジュール側のリポジトリで:
# 開発が一段落したら tag を打つ
git tag v1.4.0
git push origin v1.4.0
# まとめて push
git push --tags
# 削除して打ち直し(push 前なら)
git tag -d v1.4.0
v プレフィックス(v1.4.0)を付ける慣例。v なしでも機能はするが、揃えておくと UI が綺麗。
GitHub Releases(任意だが推奨)
tag に加えて GitHub Releases で changelog を書くと、利用者が変更点を一目で確認できます。
# GitHub CLI で
gh release create v1.4.0 \
--title "v1.4.0" \
--notes "## Added\n- IPv6 support for VPC module\n## Fixed\n- ..."
利用側のバージョン固定
利用側(呼び出すプロジェクト)の HCL:
module "vpc" {
source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=v1.4.0"
cidr_block = "10.0.0.0/16"
# ...
}
# モノレポでパスを使う場合
module "compute" {
source = "git::https://github.com/your-org/terraform-modules.git//compute?ref=v2.0.1"
}
Terraform Registry の場合
公開/プライベート Registry で公開している module は version 制約が使える:
module "vpc" {
source = "your-org/vpc/aws"
version = "~> 1.4" # 1.4.x だけ許容、1.5 以上は来ない
# ...
}
| 制約 | 意味 |
|---|---|
1.4.0 | 固定 |
~> 1.4 | 1.4.x の最新(1.5 を含まず) |
~> 1.4.0 | 1.4.0〜1.4.x(1.5 を含まず) |
>= 1.4, < 2.0 | 1.4 以上、2.0 未満 |
破壊的変更の扱い
例: VPC module の variable cidr_block を cidr にリネーム → これは MAJOR 変更(v1 → v2)。
- 新ブランチで variable をリネーム、内部対応も全部
- README / CHANGELOG に migration ガイド を書く(v1 → v2 で何を直すか)
- tag
v2.0.0を打つ - 利用側は段階的に v2 へ移行(一度に全プロジェクトを切り替えなくていい)
後方互換を保つテクニック
# 旧 var を残しつつ新 var を追加。優先順位を持たせる
variable "cidr_block" {
type = string
default = null
description = "Deprecated: use cidr instead"
}
variable "cidr" {
type = string
default = null
}
locals {
effective_cidr = coalesce(var.cidr, var.cidr_block)
}
Release PR フロー
「main にマージしたら自動で next version の tag が打たれる」自動化。release-please や semantic-release が代表。
運用イメージ:
- main にコミットを積んでいく(Conventional Commits 形式で)
- release-please が 「次のリリース内容をまとめた PR」 を自動生成・更新
- その PR をマージすると、自動で tag + GitHub Release が打たれる
# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
with:
release-type: terraform-module