ブランチ戦略入門 — GitFlow / GitHub Flow / trunk-based と Terraform モジュール管理を実践するハンズオン

📚 Git/GitHub × Terraform実践シリーズ — 全体ロードマップ

各記事は独立して読めますが、第1弾→第2弾→第3弾の順に進むと、ローカル Git からチーム協業、本番運用まで一本の学習パスとして体系的に習得できます。


目次

Section 1: イントロダクション・本記事で得られるもの・前提

1-1. 第2弾からの流れ — ブランチ管理という次の壁

第2弾(GitHub入門)では、リモートリポジトリへの push・Pull Request の作成・Issue によるタスク管理・Fork を使った OSS コントリビューションを習得しました。これで「個人での GitHub 活用」は一通りマスターできた状態です。

しかし、チーム開発の現場に入ると、すぐに新たな課題に直面します。

  • 「feature ブランチをいつ、どこに、どのようにマージするか」 がチームによってバラバラ
  • 誰かが main に直接 push して本番が壊れる
  • 複数の機能開発が並行している状態で、リリースタイミングの調整が難しい
  • Terraform の IaC コードを複数人で触ると tfstate の競合が頻発する

これらは「ブランチ戦略」の欠如が引き起こす典型的な問題です。ブランチ戦略とは、「どのブランチをいつ作り、どこにマージし、いつ削除するか」のチームルールです。

本記事では、業界標準として定着した 3 つのブランチ戦略(GitFlow / GitHub Flow / trunk-based development)を比較し、Terraform IaC 運用に最適なものを選択できるスキルを身に付けます。


1-2. 本記事のゴール

本記事を最後まで読むと、以下のことができるようになります。

ゴール説明
3戦略の理解GitFlow / GitHub Flow / trunk-based の仕組み・適用シーンを説明できる
選定判断チーム規模・リリース頻度・環境数から最適な戦略を選べる
Terraform特化理解tfstate 競合・本番 apply 慎重性とブランチ戦略の関係を説明できる
ハンズオン実践terraform-git-handson/ で各戦略を実際に体験できる
モジュール管理git tag によるバージョニングとプライベートモジュール参照を理解できる

1-3. 前提条件

本記事のハンズオンを実施するには、以下の前提条件を満たしている必要があります。

必須: 第1弾・第2弾の完走

習得済みスキル確認方法
git init / commit / branch / merge第1弾チェックリスト参照
GitHub push / pull / PR 作成第2弾チェックリスト参照
gh CLI による PR・Issue 操作gh auth status でログイン確認

環境要件

ツールバージョン確認コマンド
Git2.39 以上git --version
GitHub CLI (gh)2.40 以上gh --version
Terraform1.7 以上terraform --version
GitHub アカウント無料プランで可ブラウザでログイン確認

AWS アカウントは不要です。 ハンズオンはすべてローカル + GitHub の範囲で完結します。Terraform の apply は Section 7 以降でのみ必要ですが、そちらも LocalStack を使用するためクラウド費用は発生しません。

本記事で使用するリポジトリは、第1弾から継続して使用している terraform-git-handson/ です。まだクローンしていない場合は以下で準備してください。

git clone https://github.com/<your-username>/terraform-git-handson.gitcd terraform-git-handson

1-4. 習得スキルと推定所要時間

推定所要時間: 約 120 分

フェーズ内容目安時間
Section 1-3概念理解・比較30分
Section 4-5GitHub Flow / GitFlow ハンズオン40分
Section 6-7Trunk-based / モジュール git tag30分
Section 8-10環境別 tfstate・まとめ20分

習得スキル一覧(本 Section 1〜3 の範囲)

  • ブランチ戦略の定義と選定が必要な理由
  • GitFlow の 5 ブランチ構成とライフサイクル
  • GitHub Flow のシンプルな 2 層構成とその強み
  • trunk-based development の Short-lived branch 原則
  • 3 戦略の選定マトリクス(Terraform 運用観点を含む)

1-5. 本記事の進め方

本記事は「比較理解 → ハンズオン → Terraform 特化」の 3 段階で構成されています。

Section 1-3: 理論・比較フェーズ  ↓Section 4-6: ハンズオンフェーズ(各戦略を terraform-git-handson/ で体験)  ↓Section 7-9: Terraform 特化フェーズ(git tag・モジュール管理・環境別 tfstate)  ↓Section 10: まとめ・ロードマップ

概念だけ先に把握したい方は Section 1〜3 を通読した後にハンズオンへ進むのが効率的です。「とにかく手を動かしたい」という方は Section 3-4 の比較表だけ確認してから Section 4 のハンズオンに飛んでも構いません。


Section 2: ブランチ戦略とは何か — なぜチームで戦略が必要か

2-1. ブランチ戦略の定義と効果

ブランチ戦略(Branch Strategy) とは、Git リポジトリにおいて「どのブランチをどの目的で使い、いつ作成し、どこにマージし、いつ削除するか」のチームルールです。

ブランチ戦略を導入することで得られる主な効果は以下の 3 つです。

① リリース安定性の向上

本番リリース用のブランチ(main / release)を保護し、未テストの変更が本番に混入しないようにします。誰かの「ちょっとした修正」が本番障害を引き起こす、というケースを構造的に防げます。

② 並行開発の効率化

機能 A の開発チームと機能 B の開発チームが互いに干渉せずに作業できます。feature ブランチを切ることで、未完成のコードが他の開発者の作業に影響を与えません。

③ ロールバック容易性

問題が発生したリリースを明確に識別して切り戻せます。ブランチと git tag の組み合わせにより、「v1.3.2 にロールバック」という操作が安全・迅速に行えます。


2-2. 戦略なしの弊害 — 第2弾の学びと接続

第2弾では main ブランチへのブランチ保護ルールを設定し、「PR 経由でしか main にマージできない」状態を作りました。これは戦略の第一歩です。しかし、保護ルールだけでは解決できない問題があります。

よくある混乱シナリオ

# 状況: メンバー3人が同じリポジトリで作業中メンバーA: feature/add-vpc を develop にマージ(半完成)メンバーB: feature/add-rds を main にマージ(急ぎリリース)メンバーC: feature/add-s3 が develop ベースで作業中→ develop と main が乖離→ メンバーCのブランチが何をベースにすべきか不明→ リリース時に何が含まれているか誰も把握できない

このような状態では「今の main に何が入っているか」「次のリリースに何を含めるか」の管理が崩壊します。

具体的な弊害 ①: コンフリクト地獄

長期間ブランチを持ち続けると、main(または develop)との差分が膨らみ、マージ時に大量のコンフリクトが発生します。

# 30日間放置した feature ブランチをマージしようとした場合git switch maingit merge feature/long-lived-branch# 大量のコンフリクト発生Auto-merging modules/vpc/main.tfCONFLICT (content): Merge conflict in modules/vpc/main.tfAuto-merging modules/rds/variables.tfCONFLICT (content): Merge conflict in modules/rds/variables.tfAuto-merging environments/prod/main.tfCONFLICT (content): Merge conflict in environments/prod/main.tfAutomatic merge failed; fix conflicts and then commit the result.

手動でのコンフリクト解消は時間がかかるだけでなく、解消ミスが新たなバグを生むリスクがあります。

具体的な弊害 ②: リリース不能

複数の機能が「中途半端な状態」で main に混在している場合、「機能Aだけリリースしたい(機能Bはまだ準備中)」という状況で詰まります。

状況戦略なし戦略あり(GitFlow)
機能Aのみリリースしたい機能Bを手動で revert → リスク大release/x.y ブランチに機能Aのみ含める
本番緊急修正が必要main が混乱中でどこから修正すべきか不明hotfix/* ブランチを main から切る
「今の本番は何の状態か」確認git log で手動追跡 → 不確実git tag で明確に管理

具体的な弊害 ③: Terraform の main 直 apply

IaC では、「コードが main にある = 本番インフラの期待状態」という関係があります。戦略なしで main に未レビューのコードが混入すると、次の apply で意図しないインフラ変更が発生します。

# 危険なケース: 誰かが main に直 push した後の terraform plan$ terraform plan -var-file=prod.tfvars  # aws_db_instance.main will be destroyed  - resource "aws_db_instance" "main" {- identifier = "prod-mysql-01"- engine  = "mysql"... }Plan: 0 to add, 0 to change, 1 to destroy.

意図せず本番 RDS が削除される計画が生成されてしまいます。ブランチ戦略 + PR レビューでこの事故を構造的に防ぐことができます。


2-3. 選定軸 — チームの特性で最適解が変わる

ブランチ戦略は「正解が一つ」ではありません。チームの特性に合わせて選ぶ必要があります。主な選定軸は以下の 4 つです。

選定軸説明影響する戦略
チーム規模小規模(1〜5人)か大規模(10人以上)か小→GitHub Flow, 大→GitFlow/trunk-based
リリース頻度週1回か、1日に複数回か低頻度→GitFlow, 高頻度→trunk-based
デプロイ自動化成熟度CI/CD が整備されているか整備済み→trunk-based 向き
環境数dev/staging/prod の分離が必要か多環境→GitFlow 向き

戦略別の特性サマリー

特性GitFlowGitHub Flowtrunk-based
ブランチ数多い(5種類)少ない(2種類)最小限
リリース頻度低〜中(定期リリース)高(随時リリース)非常に高(1日複数回も可)
学習コスト
複雑な環境管理得意不得意条件付きで可

2-4. Terraform 特有の論点

IaC(Infrastructure as Code)である Terraform は、通常のアプリケーションコードと異なる特性を持ちます。ブランチ戦略を選ぶ際には、以下の Terraform 固有の論点を考慮する必要があります。

① tfstate の競合問題

Terraform の State ファイル(tfstate)は、リモートバックエンド(S3 + DynamoDB など)に保存されますが、複数人が同時に terraform apply を実行すると State ロックで衝突します。

# tfstate 競合の典型例(2名が同時に terraform apply した場合)Error: Error acquiring the state lockError message: ConditionalCheckFailedException: The conditional request failedLock Info:  ID:  a1b2c3d4-e5f6-7890-abcd-ef1234567890  Path:s3://my-tfstate-bucket/terraform.tfstate  Operation: OperationTypeApply  Who: alice@example.com  Version:1.7.0  Created:2026-04-15 09:30:00 +0000 UTC  Info:Terraform acquires a state lock to protect the state from being writtenby multiple users at the same time.

ブランチ戦略でワークフローを整備し、「誰がいつ apply できるか」を明確にすることで、この競合を構造的に防げます。

② 本番 apply の慎重性

アプリケーションコードの場合、誤った変更をデプロイしても「ロールバック → 再デプロイ」が比較的容易です。しかし Terraform の場合、terraform apply でリソースを削除・再作成すると、データの損失や数分〜数時間のダウンタイムが発生することがあります。

そのため、「誰でも main から直接 apply できる」という運用は Terraform では特に危険です。ブランチ戦略と PR レビューを組み合わせ、terraform plan の結果を必ずレビューしてから apply するフローが必要です。

③ モジュール再利用性とバージョン管理

Terraform モジュールを git タグでバージョン管理している場合、「どのタグが本番で使われているか」「新しいタグに切り替えるタイミング」がブランチ戦略と密接に関わります。

# modules/vpc/main.tf でのバージョン参照例module "vpc" {  source = "git::https://github.com/myorg/terraform-modules.git//vpc?ref=v1.2.0"}

このような参照がある場合、タグの更新はブランチ戦略に従ったレビュープロセスを経るべきです。Section 7 でこの点を詳しくハンズオンします。

Terraform × ブランチ戦略 選定チャート

Q1: 環境が複数ある(dev/staging/prod)?  → YES: GitFlow または環境別ブランチ戦略  → NO: 次へQ2: CI/CD が整備されていて plan/apply を自動化できる?  → YES: trunk-based も選択肢  → NO: GitHub Flow 推奨Q3: チームが 10 人以上でリリース管理が複雑?  → YES: GitFlow 推奨  → NO: GitHub Flow 推奨

Terraform モジュールリポジトリと利用リポジトリで戦略を分ける考え方

規模が大きくなると、「Terraform モジュールを管理するリポジトリ」と「実際のインフラを管理するリポジトリ」を分離することがあります。この場合、それぞれ異なる戦略を採用することも有効です。

リポジトリ種別推奨戦略理由
モジュールリポジトリ(共通部品)GitFlow + セマンティックバージョニング利用者への影響範囲が大きいため慎重な管理が必要
環境別インフラリポジトリGitHub Flow変更頻度が高く、環境ごとの tfvars で制御できる
実験・検証用リポジトリtrunk-based高速なイテレーションが目的

2-5. 3 戦略を選ぶ前の共通準備

どの戦略を選ぶにしても、以下の共通設定は必ず行うべきです。

# ① main ブランチの保護(第2弾で学習済み)# GitHub Settings → Branches → Branch protection rules# "Require a pull request before merging" を有効化# ② ローカルのデフォルトブランチを main に統一git config --global init.defaultBranch main# ③ コミットメッセージの規約(Conventional Commits 推奨)# feat: 新機能追加# fix: バグ修正# docs: ドキュメント変更# chore: ビルド・設定変更# refactor: リファクタリング

コミットメッセージの規約を統一することで、どの戦略でも CHANGELOG の自動生成や PR の文脈把握が容易になります。


Section 3: 3大ブランチ戦略の比較 — GitFlow / GitHub Flow / trunk-based

3-1. GitFlow 詳解

GitFlow は 2010 年に Vincent Driessen が提唱したブランチモデルです。明確なブランチ役割分担と厳密なマージルールで構成されており、バージョン管理が必要なソフトウェアや定期リリース製品に向いています。

5 ブランチの構成と役割

ブランチ名種別役割存在期間
main永続本番リリース済みコードのみ常時
develop永続次のリリースに向けた統合ブランチ常時
feature/*一時個別機能の開発開発期間中
release/*一時リリース準備(バグ修正・バージョン更新)リリース前
hotfix/*一時本番緊急修正修正期間中

ブランチのライフサイクル

main ←──────────────────────────── hotfix/1.0.1 ──┐  │  │  └──→ v1.0.0 tag └── (hotfix: main から分岐、修正後 main と develop にマージ)develop ←── feature/add-vpc ←── feature/add-rds ←── release/1.1.0  ││  └──────────────────────────────────────────────────── v1.1.0 tag → main

実際のコマンド操作(feature ブランチのライフサイクル)

# feature ブランチを develop から切るgit switch developgit pull origin developgit switch -c feature/add-rds-module# 開発作業vim modules/rds/main.tfgit add modules/rds/git commit -m "feat: add RDS module with multi-AZ support"# develop にマージ(--no-ff でマージコミットを残す)git switch developgit merge --no-ff feature/add-rds-modulegit push origin develop# feature ブランチを削除git branch -d feature/add-rds-modulegit push origin --delete feature/add-rds-module

release ブランチのライフサイクル

# develop が安定したら release ブランチを切るgit switch developgit switch -c release/1.1.0# バージョン番号の更新・最終バグ修正のみ(新機能追加は禁止)echo "1.1.0" > VERSIONgit commit -m "chore: bump version to 1.1.0"# main と develop の両方にマージgit switch maingit merge --no-ff release/1.1.0git tag -a v1.1.0 -m "Release version 1.1.0"git switch developgit merge --no-ff release/1.1.0# release ブランチを削除git branch -d release/1.1.0

hotfix ブランチのライフサイクル

# 本番障害発生!main から hotfix を切るgit switch maingit switch -c hotfix/1.0.1# 緊急修正vim modules/vpc/main.tfgit commit -m "fix: resolve security group rule causing outbound block"# main と develop の両方にマージgit switch maingit merge --no-ff hotfix/1.0.1git tag -a v1.0.1 -m "Hotfix: security group fix"git switch developgit merge --no-ff hotfix/1.0.1git branch -d hotfix/1.0.1

GitFlow の主なコマンドフロー(全体俯瞰)

# GitFlow フル操作の概観# [1] develop で日常開発git switch developgit switch -c feature/add-cloudwatch-alarms# ... 開発・コミット ...git switch develop && git merge --no-ff feature/add-cloudwatch-alarms# [2] リリース準備(develop が安定したら)git switch -c release/2.0.0 develop# バグ修正・バージョン更新のみgit switch main && git merge --no-ff release/2.0.0 && git tag -a v2.0.0git switch develop && git merge --no-ff release/2.0.0git branch -d release/2.0.0# [3] 本番緊急修正(main が壊れた場合)git switch main && git switch -c hotfix/2.0.1# 緊急修正git switch main && git merge --no-ff hotfix/2.0.1 && git tag -a v2.0.1git switch develop && git merge --no-ff hotfix/2.0.1git branch -d hotfix/2.0.1

GitFlow の適用シーン

向いているケース向いていないケース
定期リリース(月次・週次)1日に複数回デプロイする
バージョン番号を明確に管理したい小規模チーム(1〜3人)
QA 期間が必要な製品変化に素早く追従したい
複数バージョンを並行サポートするCI/CD が未整備
Terraform 多環境(dev/staging/prod)シンプルさを重視する

3-2. GitHub Flow 詳解

GitHub Flow は GitHub 社が 2011 年に公開したシンプルなブランチモデルです。ブランチは mainfeature/* の 2 種類のみで、継続的デリバリー(CD)を前提としています。

2 層構成のシンプルさ

main ←── PR ←── feature/add-vpc-modulemain ←── PR ←── feature/fix-sg-rulesmain ←── PR ←── feature/update-rds-instance-type

GitFlow のような developrelease ブランチは存在しません。main が常に本番リリース可能な状態であることが原則です。

GitHub Flow の基本フロー

# Step 1: main から feature ブランチを切るgit switch maingit pull origin maingit switch -c feature/add-s3-lifecycle-policy# Step 2: 変更を加えてコミットvim modules/s3/main.tfgit add modules/s3/main.tfgit commit -m "feat: add S3 lifecycle policy for cost optimization"# Step 3: GitHub に pushgit push -u origin feature/add-s3-lifecycle-policy# Step 4: PR を作成(gh CLI 推奨)gh pr create \  --title "feat: Add S3 lifecycle policy for cost optimization" \  --body "## 変更内容- S3 バケットに 30 日後に Infrequent Access、90 日後に Glacier へ移行するライフサイクルポリシーを追加- コスト最適化のため非アクティブオブジェクトの自動アーカイブを実装## terraform plan の結果\`\`\`Plan: 1 to add, 0 to change, 0 to destroy.\`\`\`## レビュー観点- ライフサイクルポリシーの期間設定は適切か- 既存データへの影響はないか" \  --base main# Step 5: レビュー → Squash merge → feature ブランチ削除# (GitHub UI または gh pr merge --squash)

継続的デリバリー向きの理由

GitHub Flow が高頻度デプロイに向いている理由は、「main = いつでもリリースできる状態」という制約にあります。この制約を守るためには、CI/CD パイプラインが必須です。

仕組み効果
PR 作成時に CI が自動実行バグを main に混入させない
main マージ後に自動デプロイマージ = リリース の一本化
Squash merge でコミット整理main の履歴を綺麗に保つ
ブランチ保護でレビュー必須化1人でも誤 push を防止

Squash merge vs Merge commit vs Rebase merge

GitHub Flow では、PR のマージ方法として Squash merge が推奨されることが多いです。

マージ方法main の履歴適用シーン
Squash merge1 PR = 1 commit(綺麗)GitHub Flow(推奨)
Merge commitブランチ構造を保持GitFlow(推奨)
Rebase merge線形履歴・ブランチ情報なし小規模・個人プロジェクト

GitHub Flow では「PR 単位で 1 コミット」にすることで、main の履歴が git log --oneline で読みやすくなります。

# gh CLI でのマージ方法指定gh pr merge 42 --squash --delete-branch# → feature ブランチを Squash merge し、ブランチを自動削除

Terraform への適用時の注意点

GitHub Flow を Terraform に適用する場合、main へのマージ後に自動 apply するパイプラインを組む必要があります。しかし Terraform の場合、以下の点に注意が必要です。

# GitHub Flow × Terraform の CI/CD フロー(例)# .github/workflows/terraform.yml# PR 作成時: terraform plan を実行して結果を PR コメントに貼り付ける# main マージ時: terraform apply を自動実行# 注意: apply の自動化は本番環境では慎重に# dev 環境は自動 apply、prod 環境は手動承認(GitHub Environment を使用)

環境が 1 つだけ(dev のみ)の場合は GitHub Flow が最適です。本番環境が存在する場合は、GitHub Flow を改変して環境別ブランチを設けるか、GitFlow に切り替えることを検討してください。


3-3. Trunk-based Development 詳解

Trunk-based Development(TBD) は、全開発者が単一のブランチ(trunk = main)に頻繁にマージするモデルです。Google・Facebook などの大規模組織で採用されており、DevOps 成熟度が高いチームに向いています。

基本原則: Short-lived Branch

# trunk-based の core rule: ブランチの寿命は原則 24 時間以内# 新機能の開発を開始git switch maingit pull origin maingit switch -c feature/vpc-flow-logs# こまめにコミット・push(大きな塊は作らない)vim modules/vpc/main.tfgit add modules/vpc/main.tfgit commit -m "feat(vpc): enable VPC flow logs to S3"git push origin feature/vpc-flow-logs# 24時間以内に main へ PR + マージgh pr create --title "feat(vpc): enable VPC flow logs" --base main# レビュー通過後すぐマージ

24 時間以内にマージする原則により、長期間のブランチが存在しなくなり、大規模マージ時のコンフリクト(「マージ地獄」)を回避できます。

Feature Flags による未完成コードの隠蔽

短期間でマージするためには、未完成の機能であっても main に入れられる仕組みが必要です。その解決策が Feature Flags(機能フラグ) です。

# terraform variables.tf でフラグを定義variable "enable_waf" {  description = "Feature flag: enable WAF for ALB"  type  = bool  default  = false  # 開発中は false のまま main に入れる}
# main.tf でフラグを参照resource "aws_wafv2_web_acl_association" "main" {  count  = var.enable_waf ? 1 : 0  resource_arn = aws_lb.main.arn  web_acl_arn  = aws_wafv2_web_acl.main[0].arn}

enable_waf = false のまま main にマージしておき、機能が完成したタイミングで true に変更する PR を出します。これにより、未完成のリソースが本番に作成されることなく、コードは常に main に蓄積できます。

CI/CD との組み合わせが前提

trunk-based では CI が通らないと main にマージできないルール(ブランチ保護 + Required status checks)が必須です。

# .github/workflows/ci.yml(trunk-based 用)# PR 作成 → CI 実行 → 通過後のみマージ可能# 1日に何度も main にマージされる前提なので、# CI の実行時間を短く保つことが重要(目安: 5分以内)

大規模組織・高頻度デプロイ向きの理由

課題trunk-based での解決策
大人数でのブランチ競合全員が main に毎日マージするため長期ブランチが存在しない
「マージ地獄」の回避Short-lived branch により差分が小さい
機能リリースの柔軟性Feature Flags で main とは独立してリリース制御できる
デプロイ頻度の最大化main = デプロイ可能状態 × CI/CD 自動化

trunk-based の難しさと限界

難しい点補足
CI/CD の整備が前提パイプラインがないと main が壊れ続ける
Feature Flags の管理コスト古い flags を消さないと技術的負債になる
コードレビューの迅速化が必要24時間以内マージには素早いレビューが必要
Terraform の apply 自動化リスクインフラ変更の自動 apply は慎重な設計が必要

3-4. 3 戦略 マスター比較表

以下の比較表は、ブランチ戦略を選定する際の総合的な参照表です。

観点GitFlowGitHub Flowtrunk-based
複雑度高(5種類のブランチ)低(2種類のみ)中(rules が重要)
リリース頻度低〜中(定期リリース)高(随時)非常に高(1日複数回)
ブランチ寿命長(weeks〜months)中(days〜weeks)短(24h以内が原則)
学習コスト
CI/CD 必須度推奨必須絶対必須
コンフリクト発生率高(長期ブランチ)低(短命ブランチ)
向いているチーム規模中〜大小〜中
リリース管理の明確さ非常に高中(main = 本番)低(自動化に依存)
バージョン管理得意(tag + release)可(tag のみ)条件付き(自動タグ推奨)
マルチ環境対応得意不得意条件付き可

OSSプロジェクトでの採用例

戦略採用プロジェクト例
GitFlow多くのライブラリ(定期バージョンリリース型)
GitHub FlowGitHub 社自身・多くのスタートアップ
trunk-basedGoogle・Facebook・Netflix(内部ツール)

移行パス — 成長に応じた戦略シフト

チームやプロダクトが成長するにつれてブランチ戦略を移行するケースもあります。

[フェーズ1] 個人開発: 戦略なし(main のみ)  ↓ チームが2〜3人になったら[フェーズ2] 小チーム: GitHub Flow 導入  ↓ 環境が複数に増え、QA が必要になったら[フェーズ3] 中規模: GitFlow 導入  ↓ CI/CD が完全自動化され、デプロイ頻度が高まったら[フェーズ4] 成熟チーム: trunk-based development

移行時に最も重要なのは「チーム全員への周知と合意」です。一部のメンバーが旧ルールのまま作業すると、ブランチ構成が混乱します。移行時は README や CONTRIBUTING.md にブランチルールを明記することを推奨します。

# CONTRIBUTING.md にブランチルールを記載する例# ブランチ命名規則# feature/<issue-number>-<short-description>  例: feature/42-add-s3-lifecycle# fix/<issue-number>-<short-description> 例: fix/73-sg-outbound-rule# release/x.y.z  例: release/1.2.0# hotfix/<issue-number>-<short-description> 例: hotfix/90-prod-db-lock

3-5. Terraform 運用での推奨マトリクス

Terraform IaC 運用に特化した戦略選定マトリクスです。

規模・環境別の推奨戦略

チーム規模環境数CI/CD 成熟度推奨戦略
1〜5人1環境(dev のみ)未整備〜初期GitHub Flow
1〜5人2環境(dev/prod)初期〜中級GitHub Flow + 環境別 tfvars
5〜15人3環境(dev/staging/prod)中級GitFlow
15人以上3環境以上成熟(自動化済み)trunk-based + Feature Flags

環境別ディレクトリ構成(GitFlow 推奨時)

# GitFlow × Terraform の推奨ディレクトリ構成terraform-git-handson/├── environments/│├── dev/││├── main.tf # develop ブランチで apply││└── terraform.tfvars│├── staging/││├── main.tf # release/* ブランチで apply││└── terraform.tfvars│└── prod/│ ├── main.tf # main ブランチ(タグ付き)で apply│ └── terraform.tfvars└── modules/ ├── vpc/ ├── rds/ └── s3/

この構成では、develop ブランチへのマージ = dev 環境への apply、release/* 作成 = staging への apply、main へのマージ + タグ付け = prod への apply というフローが明確に定義できます。

ブランチと apply 環境のマッピング

# GitFlow × Terraform の apply フロー例# ① feature → develop へのマージ後(CI/CD が自動実行)cd environments/dev && terraform apply -auto-approve# ② release/x.y.z 作成後(手動または承認付き自動化)cd environments/staging && terraform plan# レビュー確認後cd environments/staging && terraform apply# ③ release → main へのマージ + タグ付け後(必ず手動承認)git tag -a v1.2.0 -m "Release 1.2.0"cd environments/prod && terraform plan# 2名以上がレビューを承認した後に applycd environments/prod && terraform apply

本番環境(prod)への apply は必ず人間が承認するフローにすることが、Terraform 運用のベストプラクティスです。GitHub Actions の Environment 機能(Required reviewers)を使うと、この承認フローを自動化できます。

tfstate バックエンドとブランチ戦略の整合

環境別に tfstate を分離することがブランチ戦略と自然に連動します。

ブランチtfstate 保存先(S3 例)用途
develops3://tfstate/dev/terraform.tfstate開発環境
release/*s3://tfstate/staging/terraform.tfstateステージング環境
mains3://tfstate/prod/terraform.tfstate本番環境
# environments/prod/backend.tfterraform {  backend "s3" { bucket= "myorg-tfstate" key= "prod/terraform.tfstate" region= "ap-northeast-1" dynamodb_table = "terraform-lock" encrypt  = true  }}
# environments/dev/backend.tfterraform {  backend "s3" { bucket= "myorg-tfstate" key= "dev/terraform.tfstate" region= "ap-northeast-1" dynamodb_table = "terraform-lock" encrypt  = true  }}

環境ごとに独立した tfstate を持つことで、「dev 環境での terraform apply が prod に影響しない」という安全性が保証されます。ブランチ戦略と tfstate の分離は、Terraform × チーム開発における最も重要な設計判断の一つです。

GitHub Actions での自動 apply 実装イメージ(GitFlow)

# .github/workflows/terraform-apply.yml(概略)name: Terraform Applyon:  push: branches:- develop # dev 環境へ自動 apply- main # prod 環境へ(承認後のみ)jobs:  apply-dev: if: github.ref == 'refs/heads/develop' environment: dev # GitHub Environment(自動承認) runs-on: ubuntu-latest steps:- uses: actions/checkout@v4- name: Terraform Apply (dev)  run: | cd environments/dev terraform init terraform apply -auto-approve -var-file=dev.tfvars  apply-prod: if: github.ref == 'refs/heads/main' environment: production# GitHub Environment(Required reviewers 設定済み) runs-on: ubuntu-latest steps:- uses: actions/checkout@v4- name: Terraform Apply (prod)  run: | cd environments/prod terraform init terraform apply -var-file=prod.tfvars

environment: production の設定で「承認者が approve するまで apply が実行されない」という安全フェンスを設けられます。Section 9 でこの実装を詳しくハンズオンします。


Column: git-flow CLI ツール

GitFlow のブランチ操作を簡略化する git-flow CLI ツールがあります。頻繁に GitFlow 操作を行うチームでは導入を検討してください。

# インストール(macOS)brew install git-flow-avh# リポジトリを GitFlow 用に初期化git flow init# (main / develop のブランチ名などをインタラクティブに設定)# feature ブランチの開始・終了git flow feature start add-rds-modulegit flow feature finish add-rds-module# → develop への --no-ff マージ + ブランチ削除を自動化# release ブランチの開始・終了git flow release start 1.1.0git flow release finish 1.1.0# → main と develop へのマージ + タグ付けを自動化# hotfix の開始・終了git flow hotfix start 1.0.1git flow hotfix finish 1.0.1# → main と develop へのマージ + タグ付けを自動化

git flow feature finish--no-ff マージとブランチ削除を一括実行するため、GitFlow 操作のヒューマンエラーを大幅に減らせます。ただし、PR レビューの自動化はされないため、GitHub の保護ルールとの組み合わせが必要です。


Section 3 まとめ

本セクションでは 3 大ブランチ戦略を詳解しました。

戦略一言まとめ最初の選択肢として
GitFlow複雑だが多環境・定期リリースに強い5人以上 + 複数環境
GitHub Flowシンプルで即戦力。CI/CD が前提1〜5人の小チーム
trunk-based高頻度デプロイの最終形。CI/CD 必須成熟チームのゴール

Terraform 運用では GitHub Flow がエントリーポイントとして最適です。小規模チームで始めて、環境が増えたら GitFlow へ移行し、CI/CD が整備されたら trunk-based を検討する段階的アプローチをおすすめします。

Section 1〜3 で学んだこと チェックリスト

📝 Section 1〜3 理解度チェック

  • [ ] ブランチ戦略が必要な理由を 3 つ以上説明できる
  • [ ] GitFlow の 5 ブランチの名前と役割を説明できる
  • [ ] feature ブランチを develop から切り、マージする操作ができる
  • [ ] GitHub Flow の「main = いつでもリリース可能」原則を説明できる
  • [ ] trunk-based development の Short-lived branch 原則と Feature Flags を説明できる
  • [ ] 3 戦略の比較表からチームに合う戦略を選定できる
  • [ ] Terraform の tfstate 競合がブランチ戦略でどう解決されるか説明できる
  • [ ] 環境別ディレクトリ構成(dev/staging/prod)と tfstate 分離の関係を説明できる

Section 4 からは実際に terraform-git-handson/ リポジトリで各戦略をハンズオンします。まず Section 4 では GitHub Flow を使って Terraform モジュールを改善する PR ワークフローを体験します。

Section 4: [ハンズオン] GitHub Flow で Terraform モジュールを改善する

4-1. GitHub Flow 選択の判断

Section 3-5 の推奨マトリクスで確認したとおり、GitHub Flow が最適な状況は次のとおりです。

条件状態GitHub Flow の適合度
チーム規模1〜5 名◎ 最適
環境数dev + prod(またはそれ以下)◎ 最適
CI/CD 成熟度基礎的でも可○ 問題なし
リリース頻度随時〜週次◎ 最適
Terraform 経験第2弾修了レベル◎ 最適

GitHub Flow のルールは 1 つだけです。main ブランチはいつでもデプロイ可能な状態を保つ。これを守るために、すべての変更を feature ブランチ経由で main にマージします。

本セクションでは、terraform-git-handson/ リポジトリに対して S3 モジュールのタグ変数追加という実用的な改善を GitHub Flow で実施します。


4-2. [ハンズオン] feature/add-s3-tags ブランチで S3 モジュールにタグ変数追加

まず現在の S3 モジュールの構成を確認します。

ls modules/s3/# main.tf  outputs.tf  variables.tfcat modules/s3/variables.tf

現時点では tags 変数が存在しないことを確認したうえで、feature ブランチを作成します。

# main ブランチから最新状態で開始git switch maingit pull origin main# feature ブランチを作成git switch -c feature/add-s3-tags

modules/s3/variables.tftags 変数を追加します。

# modules/s3/variables.tf(既存変数の末尾に追記)variable "tags" {  type  = map(string)  default  = {}  description = "A map of tags to apply to the S3 bucket and related resources."}

次に modules/s3/main.tf を修正して、tags をリソースに適用します。

# modules/s3/main.tf(resource ブロックに tags を追加)resource "aws_s3_bucket" "this" {  bucket = var.bucket_name  tags = merge( var.tags, {ManagedBy = "terraform" }  )}resource "aws_s3_bucket_versioning" "this" {  bucket = aws_s3_bucket.this.id  versioning_configuration { status = var.enable_versioning ? "Enabled" : "Suspended"  }}

merge() 関数を使うことで、呼び出し元が渡したタグに加えて ManagedBy = "terraform" を自動付与できます。ルートモジュール側の呼び出しも更新します。

# environments/dev/main.tf(モジュール呼び出し側)module "s3_app" {  source= "../../modules/s3"  bucket_name = "my-app-dev-${var.env_suffix}"  tags = { Environment = "dev" Team  = "platform" CostCenter  = "cc-1234"  }}

変更をローカルで検証します。

# dev 環境でプラン確認cd environments/devterraform initterraform plan# 変更点が tags 追加のみであることを確認# Plan: 0 to add, 1 to change, 0 to destroy

terraform plan で既存リソースへの影響が tags 追加のみ(インプレース変更)であることを確認します。リソースの再作成(-/+ 表示)が発生する場合は設定を見直してください。

コミット前に Terraform のコード品質を確認するステップも加えておきます。

# フォーマットチェック(インデントや空白を標準形式に統一)terraform fmt -recursive ../../modules/s3/terraform fmt environments/dev/# 構文検証(プロバイダーなしでも実行可能)terraform validate# セキュリティスキャン(オプション:tfsec が入っている場合)tfsec modules/s3/

terraform fmt はチームで統一した書式を保つために PR 作成前に必ず実行する習慣をつけましょう。CI で自動チェックする設定は第5弾で行います。


4-3. コミット → PR → Squash merge の一連フロー

変更を stage して意味のあるコミットメッセージでコミットします。

# 変更ファイルを確認git status# modified:modules/s3/variables.tf# modified:modules/s3/main.tf# modified:environments/dev/main.tf# S3 モジュール関連を一括 stagegit add modules/s3/git add environments/dev/main.tf# Conventional Commits 形式でコミットgit commit -m "feat: add tags variable to S3 module- Add tags variable with default empty map- Apply tags via merge() with ManagedBy=terraform label- Update dev environment to pass Environment/Team/CostCenter tags"

リモートへプッシュし、PR を作成します。

# リモートへプッシュgit push origin feature/add-s3-tags# GitHub CLI で PR 作成(--fill でコミットメッセージを自動入力)gh pr create --fill# または詳細指定で作成gh pr create \  --title "feat: add tags variable to S3 module" \  --body "## 概要S3 モジュールに tags 変数を追加し、全環境でタグ付けを統一管理できるようにします。## 変更内容- \`modules/s3/variables.tf\`: tags 変数追加- \`modules/s3/main.tf\`: merge() で tags を付与- \`environments/dev/main.tf\`: タグ値を渡すように更新## テスト- \`terraform plan\` で既存リソースの再作成なし(1 to change のみ)を確認"

GitHub の PR 画面で Squash and merge を選択してマージします。Squash merge を選ぶ理由を確認しておきましょう。

マージ方法main の履歴適した場面
Merge commitfeature ブランチの全コミット + マージコミットが追加レビュー履歴を完全に残したい場合
Squash and mergefeature ブランチ全体が 1 コミットに圧縮作業途中のコミットを整理したい場合(GitHub Flow 推奨)
Rebase and mergefeature ブランチのコミットをそのまま linear に追加全コミットを残しつつ linear 履歴にしたい場合

GitHub Flow では Squash and merge が標準です。feature ブランチ内の細かい作業コミット(「typo fix」「wip」など)を圧縮し、main に 1 つの意味のある変更単位として記録できます。


4-4. main にマージ後の状態確認

PR をマージしたら、ローカルで main を最新化して状態を確認します。

# main へ切り替えて最新を取得git switch maingit pull# マージされたコミットを確認git log --oneline -5# a3f2c1d feat: add tags variable to S3 module (#3)# 9b1e4a2 feat: add tfstate remote backend with S3 and DynamoDB (#2)# 3c08b5f initial: add terraform-git-handson repository structure (#1)

Squash merge により、feature ブランチ内の複数コミットが 1 行にまとまっていることがわかります。

不要になった feature ブランチをローカルとリモートから削除します。

# ローカルブランチ削除git branch -d feature/add-s3-tags# リモートブランチ削除(GitHub の PR merge 後に自動削除設定がない場合)git push origin --delete feature/add-s3-tags# ブランチ一覧でクリーンアップ確認git branch -a# * main# remotes/origin/main

これで GitHub Flow の 1 サイクルが完了です。main ブランチのみが残り、クリーンな状態になっています。

GitHub の Automatically delete head branches 設定(リポジトリ Settings → General → Pull Requests)を有効にすると、PR マージ時にリモートの feature ブランチが自動削除されるため、git push origin --delete の手動実行が不要になります。

# GitHub CLI でリポジトリ設定を確認gh repo view --json deleteBranchOnMerge# { "deleteBranchOnMerge": true }# 有効化する場合gh repo edit --delete-branch-on-merge

マージ後は必ずローカルブランチも掃除する癖をつけましょう。

# マージ済みローカルブランチを一括確認git branch --merged main# feature/add-s3-tags(マージ済み)# マージ済みブランチをまとめて削除(main は除外)git branch --merged main | grep -v "^\* main" | xargs git branch -d

4-5. GitHub Flow の弱点:main = 本番前提の危うさ

GitHub Flow はシンプルさが最大の強みですが、main が常に本番デプロイ可能であることを前提とするため、以下の状況では摩擦が生じます。

問題① ステージング環境でのテストができない

GitHub Flow の前提:feature → main → 即時本番デプロイステージング環境を挟む場合:feature → main → staging apply → (QA通過後) → prod apply↑mainが即時デプロイ可能でなくなる

Terraform では environments/staging/environments/prod/ が別 tfstate を持つため、main へのマージが staging に apply されても prod への apply は手動ゲートが必要になります。この「ステージングは自動、本番は手動」という運用は GitHub Flow の想定外です。

問題② 長期間かかる変更との相性が悪い

大規模なリファクタリング(例: modules の再設計)は feature ブランチが長寿命になりがちです。GitHub Flow は短命ブランチを前提とするため、長期ブランチは main との乖離が広がりマージ時のコンフリクトが増加します。

問題③ 環境別 tfstate との摩擦

# prod 環境への誤 apply リスクcd environments/prodgit pull  # main の最新を取得terraform apply  # 意図せず prod に適用

GitHub Flow では main へのマージ = 全環境への apply 許可を意味しますが、Terraform では環境ごとに apply の意図を明示する必要があります。CI/CD パイプラインでブランチと環境を紐づけることで解決できますが、それは Section 6 および第5弾の内容です。


4-6. GitHub Flow 適用の成功条件チェックリスト

GitHub Flow を採用する前に確認するチェックリスト

  • [ ] チームは 5 名以下か(6 名以上は GitFlow の検討を)
  • [ ] 本番環境が 1 つ(または dev + prod の 2 環境のみ)か
  • [ ] main へのマージ後、24 時間以内にデプロイできる運用か
  • [ ] PR レビューを必須化する Branch Protection Rule が設定済みか
  • [ ] terraform plan を PR CI で自動実行できるか(なければ手動でも可)
  • [ ] feature ブランチの寿命を 1 週間以内に抑えられるか
  • [ ] hotfix 手順(main から直接切って即 merge)が全員に共有されているか

1 つでも「×」があれば GitFlow(5 名以上・複数環境)または trunk-based(CI/CD 必須)への移行を検討してください。


Section 5: [ハンズオン] GitFlow で develop/release/hotfix を回す

5-1. GitFlow 適用の判断

GitFlow が適切な状況は以下のとおりです。

条件GitFlow 推奨
チーム規模3 名以上(コードレビューが分業できる)
環境数staging + production の 2 環境以上
リリースサイクル週次・月次など定期リリース
QA プロセスステージングでの受け入れテストが必要
Terraform 運用develop = staging apply、main = production apply

terraform-git-handson/ では environments/dev/(develop 追跡)と environments/prod/(main 追跡)の 2 環境で GitFlow を実演します。


5-2. develop ブランチの初期化

まず main から develop ブランチを作成し、リモートにプッシュします。

# main ブランチを最新化git switch maingit pull origin main# develop ブランチを作成git switch -c develop# リモートへプッシュし、上流として設定git push -u origin develop

develop ブランチを staging 環境の apply ブランチとして位置づけます。GitHub の Branch Protection Rules で develop ブランチにも PR 必須設定を追加しておきましょう。

# GitHub CLI で develop への Branch Protection を設定gh api repos/:owner/:repo/branches/develop/protection \  --method PUT \  --field required_status_checks='{"strict":true,"contexts":[]}' \  --field enforce_admins=false \  --field required_pull_request_reviews='{"required_approving_review_count":1}' \  --field restrictions=null

ブランチ構成を確認します。

git branch -a# * develop#main#remotes/origin/develop#remotes/origin/main

5-3. feature → develop マージ

RDS モジュールの骨格を feature ブランチで作成し、develop へマージします。

# develop から feature ブランチを切る(main からではない)git switch -c feature/add-rds-module develop

modules/rds/ ディレクトリを作成し、最小構成の Terraform ファイルを用意します。

mkdir -p modules/rds
# modules/rds/main.tfresource "aws_db_instance" "this" {  identifier  = var.identifier  engine= "mysql"  engine_version = "8.0"  instance_class = var.instance_class  allocated_storage = var.allocated_storage  db_name  = var.db_name  username = var.username  password = var.password  skip_final_snapshot = true  tags = var.tags}
# modules/rds/variables.tfvariable "identifier" {  type  = string  description = "The name of the RDS instance."}variable "instance_class" {  type = string  default = "db.t3.micro"}variable "allocated_storage" {  type = number  default = 20}variable "db_name" {  type = string}variable "username" {  type = string}variable "password" {  type= string  sensitive = true}variable "tags" {  type = map(string)  default = {}}
# modules/rds/outputs.tfoutput "db_endpoint" {  value = aws_db_instance.this.endpoint  description = "The connection endpoint of the RDS instance."}output "db_name" {  value = aws_db_instance.this.db_name}

コミットして develop へマージします。

# ファイルを stage してコミットgit add modules/rds/git commit -m "feat: add RDS module skeleton"# develop へ切り替えgit switch develop# --no-ff でマージコミットを明示的に作成git merge --no-ff feature/add-rds-module \  -m "Merge feature/add-rds-module into develop"# feature ブランチを削除git branch -d feature/add-rds-module

--no-ff (no fast-forward)フラグが GitFlow では重要です。fast-forward が発生すると feature ブランチが存在したことが履歴から消えてしまいます。--no-ff によってマージコミットが残り、「どの feature をいつ develop に取り込んだか」が明確になります。

# develop のログで --no-ff マージコミットを確認git log --oneline --graph -6# *c4f8a12 Merge feature/add-rds-module into develop# |\# | * b3d7e91 feat: add RDS module skeleton# |/# * a3f2c1d feat: add tags variable to S3 module (#3)

develop をリモートへプッシュします。

git push origin develop

5-4. release/v1.0.0 切り出し → QA → main + develop へ戻す

develop の変更がテスト可能な状態になったら、release ブランチを切り出して QA を実施します。

# develop から release ブランチを切るgit switch -c release/v1.0.0 develop

release ブランチでは機能追加は行わず、バージョン番号更新・CHANGELOG 整備・バグ修正のみを行います。

# バージョン管理ファイルを更新(例: version.txt)echo "1.0.0" > version.txt# CHANGELOG.md を作成・更新cat > CHANGELOG.md << 'EOF'# Changelog## [v1.0.0] - 2026-04-15### Added- S3 module: tags variable support with merge() pattern- RDS module: skeleton with db.t3.micro configuration### Notes- All modules tested with Terraform 1.7.x- staging apply confirmed without errorsEOFgit add version.txt CHANGELOG.mdgit commit -m "chore: bump version to v1.0.0"

release ブランチを staging 環境に apply して QA を実施します。

# staging 環境で terraform apply を実施cd environments/devterraform initterraform apply -auto-approve# QA チェック(実際のプロジェクトではテスト自動化を推奨)# - S3 バケットのタグが正しく付与されているか# - RDS モジュールの plan が期待通りかcd ../..

QA が通過したら、release ブランチを main と develop の両方へマージします。

# main へマージgit switch maingit merge --no-ff release/v1.0.0 \  -m "Release v1.0.0"# タグを付与(-a で注釈付きタグを作成)git tag -a v1.0.0 -m "Release v1.0.0"# develop へも戻す(release 中のバグ修正を取り込むため)git switch developgit merge --no-ff release/v1.0.0 \  -m "Back-merge release/v1.0.0 into develop"# release ブランチを削除git branch -d release/v1.0.0# main と develop を一緒にプッシュ(--tags でタグも送信)git push origin main develop --tags

両方へのマージが GitFlow で最も重要なステップです。main だけにマージして develop を忘れると、release 中の修正が develop に取り込まれず、次の release で同じバグが再発します。

# main と develop の状態確認git log --oneline --graph main develop -8# *e5a1f23 (HEAD -> develop, origin/develop) Back-merge release/v1.0.0 into develop# *d4c9b12 (tag: v1.0.0, origin/main, main) Release v1.0.0

5-5. hotfix/v1.0.1 緊急修正

本番に重大なバグが発見された場合、develop を経由せず main から直接 hotfix ブランチを切ります。

# main から hotfix ブランチを作成git switch -c hotfix/v1.0.1 main

RDS モジュールで skip_final_snapshot = true がデフォルトになっていたことに気づき、本番では危険なため修正します。

# modules/rds/variables.tf に skip_final_snapshot を追加variable "skip_final_snapshot" {  type  = bool  default  = false  description = "Set to true only for non-production environments."}
# modules/rds/main.tf の修正resource "aws_db_instance" "this" {  identifier  = var.identifier  engine= "mysql"  engine_version = "8.0"  instance_class = var.instance_class  allocated_storage = var.allocated_storage  db_name  = var.db_name  username = var.username  password = var.password  skip_final_snapshot = var.skip_final_snapshot  # ハードコードから変数化  tags = var.tags}

CHANGELOG を更新してコミットします。

cat >> CHANGELOG.md << 'EOF'## [v1.0.1] - 2026-04-15### Fixed- RDS module: skip_final_snapshot defaults to false to prevent accidental data lossEOFgit add modules/rds/ CHANGELOG.mdgit commit -m "fix: set skip_final_snapshot default to false in RDS module"

hotfix を main と develop の両方へマージしてタグを付与します。

# main へマージgit switch maingit merge --no-ff hotfix/v1.0.1 \  -m "Hotfix v1.0.1"git tag -a v1.0.1 -m "Hotfix v1.0.1"# develop へも戻すgit switch developgit merge --no-ff hotfix/v1.0.1 \  -m "Back-merge hotfix/v1.0.1 into develop"# hotfix ブランチを削除git branch -d hotfix/v1.0.1# プッシュgit push origin main develop --tags
# タグ一覧で確認git tag -l "v*"# v1.0.0# v1.0.1

5-6. Terraform での GitFlow 運用例(develop=staging apply / main=production apply)

GitFlow のブランチと Terraform 環境を次のように対応付けます。

ブランチTerraform apply 対象タイミング
developenvironments/dev/ または environments/staging/PR merge 後に CI が自動 apply
release/*environments/staging/release ブランチ作成時に CI が apply(QA 用)
mainenvironments/prod/merge 後に CI が apply(または手動承認後)
hotfix/*environments/staging/ → 承認後に prodhotfix マージ前の最終確認

この対応付けを実現する GitHub Actions ワークフローの概略は第5弾で実装しますが、運用ルールとして明文化しておきます。

# .github/workflows/terraform-apply.yml(概略)# 第5弾で詳細実装on:  push: branches:- develop# staging apply- main# production applyjobs:  apply: environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} steps:- uses: actions/checkout@v4- name: Determine environment directory  run: | if [ "${{ github.ref }}" = "refs/heads/main" ]; thenecho "ENV_DIR=environments/prod" >> $GITHUB_ENV elseecho "ENV_DIR=environments/dev" >> $GITHUB_ENV fi- name: Terraform Apply  run: | cd $ENV_DIR terraform init terraform apply -auto-approve

5-7. GitFlow の注意点(ブランチ数肥大化・マージ地獄の回避策)

GitFlow の最大の課題はブランチ数の増加マージコンフリクトです。実際の運用で直面する問題と対策を整理します。

問題① feature ブランチの長寿命化

feature ブランチが 2 週間以上生き続けると、develop との乖離が広がり、マージ時に大量のコンフリクトが発生します。

# 危険なサイン: develop との乖離を確認git log develop..feature/long-lived-branch --oneline | wc -l# 50+ コミットの差分は要注意# 定期的に develop の変更を feature に取り込む(rebase または merge)git switch feature/long-lived-branchgit rebase develop# またはgit merge develop

問題② release ブランチの両方へのマージ忘れ

# チェックコマンド: release ブランチが develop に取り込まれているか確認git branch --merged develop | grep release# 何も表示されなければ back-merge 忘れ

問題③ hotfix の develop 反映忘れ

# hotfix が develop に含まれているか確認git log develop..hotfix/v1.0.1 --oneline# 何も表示されれば OK(空 = 全コミットが develop に含まれている)

git-flow CLI ツールによる自動化

これらの手順ミスを防ぐために git-flow CLI ツールが有効です。

# インストール(macOS)brew install git-flow-avh# リポジトリで初期化(デフォルト設定をそのまま使用)git flow init -d# feature ブランチの開始git flow feature start add-alb-module# → develop から feature/add-alb-module を自動作成# feature ブランチの終了git flow feature finish add-alb-module# → develop への --no-ff マージ + ブランチ削除を自動実行# release の開始git flow release start 1.2.0# → develop から release/1.2.0 を作成# release の終了git flow release finish 1.2.0# → main と develop へのマージ + タグ付けを自動実行# hotfix の開始・終了git flow hotfix start 1.1.1git flow hotfix finish 1.1.1# → main と develop へのマージ + タグ付けを自動実行

git flow feature finish--no-ff マージと back-merge を自動化するため、手順ミスを大幅に削減できます。

GitFlow 運用を始める前の Branch Protection 設定

GitFlow では main と develop の両方を保護ブランチとして設定します。

# develop ブランチの保護設定を確認gh api repos/:owner/:repo/branches/develop/protection# main ブランチの保護設定確認gh api repos/:owner/:repo/branches/main/protection

最低限設定すべき保護ルールを整理します。

設定項目maindevelop理由
Require PR before merging✅ 必須✅ 必須直接 push を防ぐ
Required approvals1 名以上1 名以上コードレビュー担保
Require status checks✅ 必須✅ 必須CI 通過を保証
Restrict who can pushリリース担当のみ全開発者main への誤 push 防止
Allow force pushes❌ 禁止❌ 禁止履歴書き換え防止
# GitHub CLI で main の保護ルールを設定gh api repos/:owner/:repo/branches/main/protection \  --method PUT \  --field required_status_checks='{"strict":true,"contexts":["terraform-plan"]}' \  --field enforce_admins=true \  --field required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true}' \  --field restrictions=null \  --field allow_force_pushes=false \  --field allow_deletions=false

これで main への直接 push が不可能になり、すべての変更が PR 経由となります。GitFlow の「リリースブランチだけが main にマージできる」という原則を技術的に強制できます。


Section 6: [ハンズオン] Trunk-based + Feature Flags で Terraform を継続的デプロイ

6-1. Trunk-based 適用の判断

trunk-based development が適切な状況は次のとおりです。

条件状態
CI/CD の成熟度高い(全 PR に自動テスト・plan が必須)
チームの規律高い(小さい変更を高頻度で main に merge できる)
デプロイ頻度日次〜随時
Feature Flags 基盤あり(Terraform では count/for_each で代替可能)
ブランチ寿命24 時間以内(または short-lived branches)

GitHub Flow との違いはデプロイの頻度と CI/CD の自動化度です。GitHub Flow は PR ごとにデプロイするのに対し、trunk-based では main への merge が即時 CI/CD パイプラインをトリガーして本番に反映されます。

Feature Flags が必要な理由

未完成の機能を main に直接マージするために、コードは存在するが機能は無効化する仕組みが必要です。Terraform では count または for_each を使った条件分岐で Feature Flags を実現します。


6-2. Feature Flags の Terraform 実装

Terraform における Feature Flags の基本パターンを実装します。

まず変数定義でフラグを宣言します。

# variables.tf(ルートモジュール)variable "enable_rds" {  type  = bool  default  = false  description = "Feature flag: enable RDS module deployment. Set to true when ready to provision."}variable "enable_alb" {  type  = bool  default  = false  description = "Feature flag: enable Application Load Balancer deployment."}variable "enable_cloudfront" {  type  = bool  default  = false  description = "Feature flag: enable CloudFront distribution."}

次に main.tf でフラグを参照します。

# main.tf(ルートモジュール)module "s3" {  source= "./modules/s3"  bucket_name = "my-app-${var.env}-${var.env_suffix}"  tags  = local.common_tags}# Feature Flag: RDS は enable_rds = true の場合のみデプロイmodule "rds" {  count  = var.enable_rds ? 1 : 0  source = "./modules/rds"  identifier  = "my-app-${var.env}"  instance_class = var.rds_instance_class  allocated_storage = 20  db_name  = "appdb"  username = var.db_username  password = var.db_password  tags  = local.common_tags}

count = var.enable_rds ? 1 : 0 のパターンで、enable_rds = false のとき count = 0 となりリソースが作成されません。enable_rds = true にすると count = 1 になりリソースが作成されます。

フラグが無効のときの terraform plan 出力を確認します。

# dev 環境の tfvars でフラグを確認cat environments/dev/terraform.tfvars# enable_rds = falsecd environments/devterraform plan# RDS に関するリソースが plan に現れないことを確認# No changes. Your infrastructure matches the configuration.# (S3 のみ存在する場合)

複数のフラグを locals で管理すると見通しが良くなります。

# locals.tflocals {  # Feature flags 一覧(管理しやすいように集約)  features = { rds= var.enable_rds alb= var.enable_alb cloudfront  = var.enable_cloudfront  }}

for_each を使ったより柔軟な Feature Flag パターン

count の代わりに for_each を使うと、複数リージョンへの条件付きデプロイなど、より複雑なユースケースに対応できます。

# variables.tf(for_each パターン)variable "enabled_modules" {  type  = set(string)  default  = []  description = "Set of modules to enable. Possible values: rds, alb, cloudfront"}
# main.tf(for_each パターン)module "rds" {  for_each = contains(var.enabled_modules, "rds") ? toset(["this"]) : toset([])  source= "./modules/rds"  identifier  = "my-app-${var.env}"  instance_class = "db.t3.micro"  allocated_storage = 20  db_name  = "appdb"  username = var.db_username  password = var.db_password  tags  = local.common_tags}
# environments/dev/terraform.tfvars(フラグを有効化する例)enabled_modules = ["rds", "alb"]

for_each パターンは count パターンに比べて参照構文が module.rds["this"] になる点が異なりますが、将来的に複数インスタンスへの拡張が必要な場合は for_each の方が柔軟です。小規模プロジェクトでは count パターンの方がシンプルで読みやすいため、まずは count から始めることを推奨します。


6-3. 未完成コードを main に直接マージするフロー

trunk-based では、機能が完成していなくても Feature Flag で無効化したうえで main へ merge します。

# main から short-lived ブランチを作成(24 時間以内にマージが目標)git switch maingit pullgit switch -c feat/rds-flag

RDS モジュールの Feature Flag を追加してコミットします。

# Feature Flag 付きコードを追加(上記の variables.tf / main.tf の変更)git add variables.tf main.tf locals.tfgit commit -m "feat: add RDS module behind feature flag (disabled)Feature flag: enable_rds defaults to false.RDS module code is present but not deployed until flag is enabled.This allows safe incremental development on trunk."

PR を作成してマージします。

git push origin feat/rds-flaggh pr create --fill# CI で terraform plan が自動実行されることを確認# plan の出力に RDS リソースが含まれないことを確認# GitHub UI で Squash and merge# またはgh pr merge --squash --delete-branch

マージ後に main を最新化して確認します。

git switch maingit pullgit log --oneline -3# 7f3a21c feat: add RDS module behind feature flag (disabled)# e5a1f23 Back-merge release/v1.0.0 into develop# ...

6-4. フラグ有効化のPR(enable_rds = true)

RDS の実装が完成し、ステージングでの検証も終わったら、フラグを有効化する PR を別途作成します。

git switch -c feat/enable-rds
# environments/prod/terraform.tfvars(フラグを有効化)enable_rds = true# RDS 固有の設定rds_instance_class = "db.t3.small"db_username  = "appuser"
git add environments/prod/terraform.tfvarsgit commit -m "feat: enable RDS module in productionFeature flag enable_rds set to true for prod environment.Staging validation completed: db.t3.micro tested for 2 weeks.Upgrading to db.t3.small for production capacity."git push origin feat/enable-rdsgh pr create --fill

フラグ有効化 PR のレビューポイントを明確にします。

レビュー項目確認内容
terraform plan の差分RDS リソースが新規作成(to add)になっているか
skip_final_snapshot本番では false になっているか
password の扱いtfvars にハードコードされていないか(Secrets Manager 参照を推奨)
コスト試算インスタンスクラスのコストが予算内か
バックアップ設定backup_retention_period が設定されているか

PR がレビューを通過したら main へマージし、CI/CD が自動で terraform apply を実行します。

# フラグ有効化後の terraform plan 出力イメージ# Plan: 3 to add, 0 to change, 0 to destroy.## + aws_db_instance.this[0]# + aws_db_subnet_group.this[0]# + aws_security_group.rds[0]

6-5. trunk-based の前提条件(CI/CD 必須・第5弾 GHA OIDC 布石)

trunk-based development を安全に運用するには、以下の CI/CD 基盤が必須です。

要件内容第5弾での実装
PR 時 terraform planmain への影響を事前確認GHA + OIDC 認証
main push 時 terraform apply自動デプロイ(または手動承認後)GHA Environment + OIDC
tfstate ロック並列 apply の衝突防止S3 + DynamoDB(第2弾実装済み)
ブランチ保護PR 必須・CI 通過必須GitHub Branch Protection
Secrets 管理tfvars に機密情報を書かないGitHub Actions Secrets / AWS Secrets Manager

第5弾では GitHub Actions + OIDC(OpenID Connect) で AWS に対して一時認証情報を使った安全な Terraform apply を実装します。OIDC を使うと AWS アクセスキーを GitHub Secrets に保管する必要がなく、セキュリティが大幅に向上します。

# 第5弾で実装する OIDC 認証の概略# .github/workflows/terraform.ymlpermissions:  id-token: write# OIDC トークン取得に必要  contents: readsteps:  - name: Configure AWS credentials via OIDC uses: aws-actions/configure-aws-credentials@v4 with:role-to-assume: arn:aws:iam::123456789012:role/github-actions-terraformaws-region: ap-northeast-1# アクセスキー不要。GitHub が発行した JWT を AWS が検証

trunk-based の導入ステップとして、まず GitHub Flow を安定運用し、CI/CD を整備してから trunk-based へ移行することを強く推奨します。


6-6. 3 戦略ハンズオン完了 — Terraform 運用での選定フローチャート

Section 4〜6 で 3 つのブランチ戦略をハンズオンで体験しました。実際のプロジェクトでどの戦略を選ぶかの判断フローをテーブルで整理します。

ステップ① チーム規模で絞り込む

チーム規模第一候補理由
1〜2 名GitHub Flowシンプルさが最優先。develop ブランチの維持コストが無駄
3〜5 名GitHub Flow または GitFlow環境数と QA プロセスで判断(下記 ② へ)
6 名以上GitFlowPR レビューの並列化、feature 作業の独立性が必要

ステップ② 環境数で絞り込む

環境数推奨戦略
1 環境(dev のみ)GitHub Flow 確定
2 環境(dev + prod)GitHub Flow(ステージングレスなら)
3 環境以上(dev + staging + prod)GitFlow(staging での QA が必要)

ステップ③ CI/CD 成熟度で絞り込む

CI/CD の状態推奨戦略
CI なしGitHub Flow(手動 plan/apply)
PR に terraform plan CI ありGitHub Flow または GitFlow
main push で自動 apply あり + テスト充実trunk-based 検討可

総合判断マトリクス

チーム規模環境数CI/CD推奨戦略
1〜2 名1〜2なし〜基礎GitHub Flow
3〜5 名2基礎〜中級GitHub Flow
3〜5 名3+中級GitFlow
6 名以上3+中級〜高度GitFlow
任意任意高度(自動 apply + Feature Flags 基盤)trunk-based

Terraform 運用では「GitHub Flow から始めて、チームと環境が成長したら GitFlow へ移行、CI/CD が整ったら trunk-based を検討」という段階的アプローチが最も現実的です。

📝 Section 4〜6 理解度チェック

  • [ ] feature/add-s3-tags ブランチを作成し、Squash merge で main に取り込める
  • [ ] GitHub Flow の「main = 常にデプロイ可能」原則と、その制約を説明できる
  • [ ] GitFlow で develop ブランチを初期化し、feature → develop の –no-ff マージができる
  • [ ] release ブランチを main と develop の両方へマージし、タグを付与できる
  • [ ] hotfix ブランチを main から切り、main と develop に戻せる
  • [ ] Terraform の Feature Flag パターン(count = var.enable_xxx ? 1 : 0)を実装できる
  • [ ] trunk-based development に必要な CI/CD 要件を 3 つ以上挙げられる
  • [ ] 3 戦略の選定マトリクスを参照して、自分のプロジェクトに適した戦略を選べる

Section 7 からは環境別 tfstate の設計とモジュールの git tag バージョニングに進みます。複数ブランチ・複数環境での tfstate 競合を根本から解決する設計パターンを学びます。

Section 7: Terraform 環境別ディレクトリ × ブランチ戦略 の設計

7-1. 環境分離の 3 パターン比較

Terraform で複数環境(dev / stg / prod)を管理するアプローチは主に 3 つ存在します。ブランチ戦略と組み合わせる前に、まず各パターンの特徴を把握しましょう。

パターンディレクトリ構成特徴推奨シーン
ディレクトリ分離envs/{dev,stg,prod}/明示的・安全・tfstate が独立推奨(チーム規模問わず)
Terraform Workspace単一ディレクトリ + terraform workspaceシンプルだが state 管理が複雑化しやすい小規模・単純構成のみ
ブランチ分離dev / stg / prod ブランチ危険 — アンチパターン非推奨

本記事では ディレクトリ分離 × GitHub Flow の組み合わせを推奨構成として解説します。

各パターンの Terraform 運用観点での詳細な比較は以下の通りです。

評価軸ディレクトリ分離Workspaceブランチ分離
tfstate の独立性◎ 完全分離△ workspace 単位✕ ブランチ間で混在
CI/CD との相性◎ paths フィルタで制御可△ workspace 切り替えが必要✕ ブランチ切り替えが複雑
マージ競合リスク◎ なし(main 一本)◎ なし✕ 環境間コンフリクト多発
学習コスト◎ 低い(直感的)△ 中程度△ 高い(危険さを理解要)
大規模チーム適性

7-1-b. Terraform Workspace の落とし穴

Workspace は小規模プロジェクトでは便利ですが、以下の制限を理解した上で採用してください。

# Workspace を使った環境切り替え(小規模のみ推奨)terraform workspace list# * default#dev#stg#prod# workspace 作成terraform workspace new devterraform workspace new stgterraform workspace new prod# workspace 切り替えterraform workspace select devterraform plan # dev 環境の plan# 現在の workspace を確認terraform workspace show# dev
# main.tf: workspace 名を使って環境を切り替える例locals {  env = terraform.workspace  # "dev" / "stg" / "prod"  # 環境ごとの設定をマップで定義  config = { dev  = { instance_type = "t3.micro",  min_size = 1 } stg  = { instance_type = "t3.small",  min_size = 1 } prod = { instance_type = "t3.medium", min_size = 2 }  }}resource "aws_instance" "app" {  instance_type = local.config[local.env].instance_type  # ...}

Workspace の問題点は、すべての環境が同一ディレクトリの同一コードを共有するため、環境ごとに異なるバックエンド設定(S3 バケット、DynamoDB テーブル)や異なる .tf ファイルを持てないことです。本番環境だけリソース構成を大きく変えたいケースには対応できません。

# Workspace の state ファイルは S3 上で以下のように格納される# s3://my-tfstate-bucket/env:/dev/terraform.tfstate# s3://my-tfstate-bucket/env:/stg/terraform.tfstate# s3://my-tfstate-bucket/env:/prod/terraform.tfstate# ディレクトリ分離の場合(推奨)# s3://my-tfstate-bucket/envs/dev/terraform.tfstate# s3://my-tfstate-bucket/envs/stg/terraform.tfstate# s3://my-tfstate-bucket/envs/prod/terraform.tfstate# どちらも独立しているが、ディレクトリ分離のほうが# backend 設定・providers・変数ファイルを環境ごとに完全に分離できる

7-2. ブランチ分離アンチパターン — なぜ危険か

「環境ごとにブランチを切る」アプローチは一見わかりやすいですが、Terraform 運用では深刻な問題を引き起こします。

# アンチパターン: 環境ブランチに直接 push して applygit switch prod-branchterraform apply# → main との差分が不明なまま本番に適用してしまう
# 問題① — tfstate drift の発生# dev-branch と prod-branch の .tf ファイルに差異が蓄積し、# どちらが "正" の構成か判別不能になる# 問題② — マージ地獄# 3 環境分のコンフリクトを手動解消し続けることになるgit merge dev-branch  # stg-branch 上で実施# Auto-merging main.tf# CONFLICT (content): Merge conflict in main.tf# Automatic merge failed; fix conflicts and then commit the result.# 問題③ — 本番誤 apply のリスクgit switch prod-branch  # ← dev 作業中に誤ってスイッチterraform apply# ← 未検証変更が本番に当たる
⚠️ ブランチ分離アンチパターンの本質的問題
ブランチ分離は tfstate の一貫性を破壊します。各環境の state ファイルがブランチをまたいで整合性を保つ仕組みがなく、「どのブランチの terraform.tfstate が正しいか」が誰にもわからない状態に陥ります。Terraform では コードとステートは常に 1:1 で対応していなければなりません。

7-3. 推奨構成: ディレクトリ分離 + GitHub Flow

推奨するのは「すべての環境コードを main ブランチ上で管理し、ディレクトリで環境を分離する」構成です。

terraform-git-handson/├── envs/│├── dev/││├── main.tf││├── variables.tf││├── outputs.tf││└── terraform.tfvars│├── stg/││├── main.tf││├── variables.tf││├── outputs.tf││└── terraform.tfvars│└── prod/│ ├── main.tf│ ├── variables.tf│ ├── outputs.tf│ └── terraform.tfvars└── modules/ ├── vpc/ │├── main.tf │├── variables.tf │└── outputs.tf └── s3/  ├── main.tf  ├── variables.tf  └── outputs.tf
# ディレクトリ構成を実際に作成cd terraform-git-handsonmkdir -p envs/{dev,stg,prod} modules/{vpc,s3}# 各環境に main.tf を配置(dev の例)cat > envs/dev/main.tf << 'EOF'terraform {  required_version = ">= 1.7"  required_providers { aws = {source  = "hashicorp/aws"version = "~> 5.0" }  }  backend "s3" { bucket = "my-tfstate-bucket" key = "envs/dev/terraform.tfstate" region = "ap-northeast-1"  }}module "vpc" {  source = "../../modules/vpc"  env = "dev"}EOF# terraform.tfvars で環境固有値を管理cat > envs/dev/terraform.tfvars << 'EOF'env = "dev"vpc_cidr  = "10.0.0.0/16"instance_type= "t3.micro"EOF

この構造により、各環境の tfstate は独立した S3 パス(envs/dev/terraform.tfstate / envs/stg/terraform.tfstate / envs/prod/terraform.tfstate)に保存され、競合が発生しません。

# 確認: 現在のディレクトリ構成find . -name "*.tf" | sort# ./envs/dev/main.tf# ./envs/dev/variables.tf# ./envs/prod/main.tf# ./envs/prod/variables.tf# ./envs/stg/main.tf# ./envs/stg/variables.tf# ./modules/s3/main.tf# ./modules/vpc/main.tf

7-4. Gated Promotion フロー

環境昇格(dev → stg → prod)は段階的に、かつ必ず PR を経由して実施します。

feature ブランチ ↓ PR → maindev apply(terraform plan → approve → apply) ↓ 動作確認 OKstg PR → main へのマージ後 stg apply ↓ ステージング検証 OKprod PR → main へのマージ後 prod apply
# Step 1: feature ブランチで開発git switch -c feature/add-s3-lifecycle# main.tf を編集して S3 ライフサイクルルールを追加# ... 編集 ...git add envs/dev/main.tfgit commit -m "feat(dev): add S3 lifecycle rule for log rotation"# Step 2: dev 環境で plan → applycd envs/devterraform initterraform plan -out=dev.tfplanterraform apply dev.tfplan# Step 3: PR 作成 → レビュー → main マージgh pr create \  --title "feat: add S3 lifecycle rule" \  --body "dev 確認済み。tfplan 結果を添付します。" \  --base main
# Step 4: stg 昇格(PR マージ後)cd envs/stgterraform initterraform plan -out=stg.tfplan# ← レビュアーが plan 結果を確認terraform apply stg.tfplan# Step 5: prod 昇格(stg 検証完了後)cd envs/prodterraform initterraform plan -out=prod.tfplan# ← シニアエンジニアによる最終承認terraform apply prod.tfplan

各段階で terraform plan の出力をレビュアーが確認してから apply を実行します。これにより意図しないリソース変更が本番に届くリスクを排除できます。

各環境昇格時のレビューポイント

確認項目dev → stgstg → prod
Plan: N to add, M to change, K to destroy の K1 以下推奨0 を原則とする
destroy 対象リソースの種別データリソース(RDS・S3)は要注意如何なるデータリソースも禁止
変更量(M の数)20 以下推奨10 以下推奨
実行タイミング平日日中低トラフィック時間帯
# plan 結果の変更サマリーのみを抽出して確認terraform plan 2>&1 | grep "^Plan:"# Plan: 2 to add, 1 to change, 0 to destroy.# destroy が含まれている場合は詳細を確認terraform plan 2>&1 | grep -A 5 "will be destroyed"

7-5. モノレポでの GitHub Flow 運用

terraform-git-handson/ のようなモノレポ(IaC コード + アプリコードが同一リポジトリ)では、変更対象を絞り込む工夫が必要です。

# モノレポのディレクトリ構成例terraform-git-handson/├── envs/  # Terraform IaC コード├── modules/  # 共通モジュール├── apps/  # アプリケーションコード(例)│├── api/│└── frontend/└── .github/ └── workflows/  ├── terraform-dev.yml# envs/dev/** 変更時のみ起動  ├── terraform-stg.yml# envs/stg/** 変更時のみ起動  └── terraform-prod.yml  # envs/prod/** 変更時のみ起動
# .github/workflows/terraform-dev.yml の paths フィルタ例name: Terraform Devon:  push: branches: [main] paths:- 'envs/dev/**'- 'modules/**'  pull_request: paths:- 'envs/dev/**'- 'modules/**'

paths フィルタを活用することで、envs/dev/ に関係しない変更では dev の CI が起動しないよう制御できます。これにより、アプリコードの PR が誤って Terraform ワークフローをトリガーするといった誤作動を防止できます。

# PR に変更ファイルの一覧を表示して確認gh pr diff --name-only# Terraform 変更が含まれているかを grep で確認gh pr diff --name-only | grep "^envs/"# envs/dev/main.tf

Section 7 チェックリスト

Section 7 完了チェックリスト

  • ☐ ブランチ分離アンチパターンが危険な理由(tfstate drift・マージ地獄・誤 apply)を説明できる
  • envs/{dev,stg,prod}/ + modules/ のディレクトリ構成を作成できる
  • ☐ Gated Promotion フロー(dev → stg → prod)を PR ベースで実施できる
  • ☐ モノレポでの paths フィルタによる CI スコープ制御を設定できる
  • ☐ 各環境の tfstate が独立した S3 パスに分離されていることを確認できる

Section 8 では、Terraform モジュールを git tag でバージョン管理する手法を習得します。環境ディレクトリからモジュールを参照する際に、特定のバージョンを固定することでインフラの再現性を確保します。


Section 8: Terraform モジュールのバージョン管理 — git tag と source ref

8-1. モジュールの SemVer 設計

Terraform モジュールは Semantic Versioning(SemVer) でバージョン管理するのがベストプラクティスです。変更の種別によってバージョン番号を使い分けることで、利用者が安全にアップグレードできるかを判断できるようになります。

バージョン変更変更種別具体例
v1.0.0v1.0.1patch: バグ修正・後方互換ありタグ変数のデフォルト値修正
v1.0.0v1.1.0minor: 後方互換の機能追加新オプション変数の追加
v1.0.0v2.0.0major: 破壊的変更(後方互換なし)変数名変更・出力削除
# 現在のモジュールバージョンを確認cd terraform-git-handsongit tag -l# v1.0.0# v1.0.1# v1.1.0# 最新タグを確認git describe --tags --abbrev=0# v1.1.0

8-2. git tag 打ち方と push

モジュールのリリースは 注釈付きタグ(annotated tag) を使います。軽量タグ(git tag v1.0.0)と異なり、注釈付きタグはタグ作成者・日時・メッセージを保持するため、いつ・誰が・何のためにリリースしたかを追跡できます。

# 注釈付きタグを作成(推奨)git tag -a v1.0.0 -m "Initial release: VPC module with basic configuration"# タグをリモートに pushgit push origin v1.0.0# 全タグの一覧確認git tag -l# v1.0.0# タグの詳細確認(タグ作成者・日時・メッセージを表示)git show v1.0.0# tag v1.0.0# Tagger: Your Name <you@example.com># Date:2026-04-15 09:00:00 +0900## Initial release: VPC module with basic configuration## commit abc1234 (HEAD -> main, tag: v1.0.0, origin/main)# Author: Your Name <you@example.com># ...
# 複数タグをまとめて push する場合git push origin --tags# 特定タグのみ push(推奨: 意図したタグだけを明示的に push)git push origin v1.1.0

8-3. terraform source で git tag 参照

環境ディレクトリからモジュールを参照する際に ?ref=<tag> を付与することで、特定バージョンのモジュールをバージョン固定で参照できます。

# envs/dev/main.tf — HTTPS 形式でプライベートリポジトリのモジュールを参照module "vpc" {  source = "git::https://github.com/YOUR_ORG/terraform-modules.git//modules/vpc?ref=v1.0.0"  env= var.env  vpc_cidr = var.vpc_cidr}# SSH 形式(CI/CD での deploy key 運用に推奨)module "s3" {  source = "git::git@github.com:YOUR_ORG/terraform-modules.git//modules/s3?ref=v1.1.0"  env  = var.env  bucket_prefix = var.bucket_prefix}
# terraform init で git tag を参照してモジュールをダウンロードcd envs/devterraform init# 初期化後の .terraform/modules/ 構成を確認ls .terraform/modules/# modules.json  vpc/  s3/# どのバージョンが使われているか確認cat .terraform/modules/modules.json | python3 -m json.tool | grep -A 3 '"Key"'# "Key": "vpc",# "Source": "git::https://github.com/YOUR_ORG/terraform-modules.git//modules/vpc?ref=v1.0.0",# "Version": "",# "Dir": ".terraform/modules/vpc"

ローカルのモジュール(同一リポジトリ内)を参照する場合は ../../modules/vpc のような相対パスを使用します。外部リポジトリのモジュールをバージョン固定で参照する場合にのみ git:: プレフィックスと ?ref= を使います。

# 同一リポジトリ内モジュール(バージョン指定不要)module "vpc" {  source = "../../modules/vpc"  env = var.env}# 外部リポジトリのモジュール(バージョン固定必須)module "shared_vpc" {  source = "git::git@github.com:YOUR_ORG/shared-modules.git//modules/vpc?ref=v2.1.0"  env = var.env}

8-4. プライベートリポジトリ参照時の認証

外部のプライベートリポジトリからモジュールを参照する際は、認証設定が必要です。

方法 A: SSH Deploy Key(リポジトリ単位・推奨)

# deploy key 用の SSH キーペアを生成ssh-keygen -t ed25519 -C "terraform-modules-deploy-key" -f ~/.ssh/tf_deploy_key -N ""# 公開鍵を GitHub リポジトリの Settings > Deploy keys に登録cat ~/.ssh/tf_deploy_key.pub# SSH 接続テストssh -T git@github.com# Hi YOUR_ORG/terraform-modules! You've successfully authenticated, but GitHub# does not provide shell access.# ~/.ssh/config に設定を追加cat >> ~/.ssh/config << 'EOF'Host github.com  IdentityFile ~/.ssh/tf_deploy_key  StrictHostKeyChecking accept-newEOF

方法 B: Fine-grained Personal Access Token(PAT)

第2弾 Section 3-4 で作成した PAT を再利用できます。Contents: Read-only 権限のみで十分です。

# PAT を環境変数に設定(~/.bashrc や CI の Secrets に登録)export GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"# git credential helper に PAT を渡す方法git config --global credential.helper storeecho "https://YOUR_USERNAME:${GITHUB_TOKEN}@github.com" >> ~/.git-credentials# 動作確認: プライベートリポジトリに git ls-remote でアクセスgit ls-remote https://github.com/YOUR_ORG/terraform-modules.git refs/tags/v1.0.0# abc1234...  refs/tags/v1.0.0
💡 Deploy Key vs PAT の使い分け

  • Deploy Key: 単一リポジトリへの読み取り専用アクセスに最適。鍵が漏洩しても影響範囲が 1 リポジトリに限定される
  • Fine-grained PAT: 複数リポジトリへのアクセスが必要な場合に適する。ただし PAT の有効期限管理が必要
  • CI/CD(GitHub Actions)では Secrets に格納し、環境変数として渡す。PAT をハードコードしない

8-5. タグ更新の運用ルール

一度 push したタグを強制上書きすることは 禁止です。すでにそのタグを ?ref= で参照しているユーザーのロックが解除不能になり、予期しないバージョンのコードが適用されるリスクがあります。

# ❌ 禁止: 既存タグの強制上書きgit tag -f v1.0.0  # ローカルのタグを書き換えgit push --force origin v1.0.0  # リモートのタグも強制更新# → すでに v1.0.0 を参照しているユーザーのインフラに影響する# ✅ 正しい運用: 新しいパッチバージョンとして対応git tag -a v1.0.1 -m "Fix: correct S3 bucket name variable default value"git push origin v1.0.1# ✅ 利用者側の対応: source の ref を更新# envs/dev/main.tf の ref=v1.0.0 を ref=v1.0.1 に変更し PR を出す
# タグの削除が必要な場合(ローカルのみ、push 前)git tag -d v1.0.0-rc1 # ローカルの未 push タグを削除# リモートのタグを削除する場合(やむを得ない場合のみ、必ず全利用者に周知)git push origin :refs/tags/v1.0.0-rc1# またはgit push origin --delete v1.0.0-rc1
⚠️ タグの強制上書き禁止の徹底
git push –force で既存タグを上書きすることは、チームに混乱をもたらします。 同じタグ名が異なるコミットを指すようになると、terraform init のキャッシュとリモートの実体が乖離し、「同じコードのはずなのに plan 結果が変わる」という不可解なバグを生みます。タグは イミュータブル(不変) なものとして扱い、修正は必ず新しいバージョン番号で対応してください。

8-6. CHANGELOG.md 運用(Conventional Commits 活用)

モジュールのリリースには、変更内容を記録した CHANGELOG.md の更新を必ずセットで行います。Conventional Commits 形式(feat: / fix: / docs: / chore:)を使うと、変更種別が一目でわかります。

# CHANGELOG## [1.1.0] - 2026-04-15### Added- feat: add `tags` variable to S3 module for resource tagging### Fixed- fix: correct default value for `bucket_prefix` (was empty string, now `"myapp"`)## [1.0.1] - 2026-04-10### Fixed- fix: correct S3 bucket name variable reference in outputs.tf## [1.0.0] - 2026-04-01### Added- feat: initial release of VPC module with basic configuration- feat: initial release of S3 module with versioning support
# CHANGELOG 更新 → コミット → タグ → push を一連の手順で実施# Step 1: CHANGELOG.md を編集(上記の内容を追記)vim modules/CHANGELOG.md# Step 2: 変更をコミットgit add modules/CHANGELOG.mdgit commit -m "docs: update CHANGELOG for v1.1.0"# Step 3: 注釈付きタグを作成git tag -a v1.1.0 -m "Add tags variable to S3 module + fix bucket_prefix default"# Step 4: main ブランチとタグをまとめて pushgit push origin main v1.1.0# Step 5: タグが正しく push されたことを確認git ls-remote --tags origin# abc1234  refs/tags/v1.0.0# def5678  refs/tags/v1.0.1# ghi9012  refs/tags/v1.1.0

8-7. モジュールバージョン管理の全体フロー早見表

モジュールの開発からリリース・利用者への適用までの一連の流れを早見表にまとめます。

フェーズ担当操作コマンド
1. 開発モジュール開発者feature ブランチで開発git switch -c feat/add-tags-variable
2. PRモジュール開発者PR 作成・レビュー依頼gh pr create --base main
3. マージレビュアーmain にマージPR の Merge ボタン
4. CHANGELOGモジュール開発者変更内容を記録vim CHANGELOG.md && git commit
5. タグ付けモジュール開発者SemVer タグを作成git tag -a v1.1.0 -m "..."
6. Pushモジュール開発者main + タグを pushgit push origin main v1.1.0
7. 利用者通知モジュール開発者アップグレード案内PR / Slack でアナウンス
8. 利用者更新IaC 利用者source の ref を更新ref=v1.0.0ref=v1.1.0
9. 計画確認IaC 利用者terraform plan で差分確認terraform plan -out=dev.tfplan
10. 適用IaC 利用者環境ごとに段階適用terraform apply dev.tfplan
# 利用者側: モジュールバージョン更新の典型的なワークフロー# Step 1: 更新ブランチを作成git switch -c chore/upgrade-vpc-module-v1.1.0# Step 2: envs/dev/main.tf の source ref を更新# ref=v1.0.0 → ref=v1.1.0sed -i 's/?ref=v1.0.0/?ref=v1.1.0/g' envs/dev/main.tf# Step 3: terraform init で新バージョンを取得cd envs/devterraform init -upgrade# Step 4: plan で差分確認terraform plan# 変更内容が想定通りかレビュー# Step 5: dev で apply → stg → prod と段階昇格terraform apply

8-8. バージョン更新の自動化 — Renovate / Dependabot との連携

モジュールの新バージョンが公開されるたびに手動で ref= を更新するのは手間がかかります。Renovate や GitHub Dependabot を使うと、新バージョンのタグが push されたときに自動で PR を作成できます。

# renovate.json — Terraform git source の更新を自動検出{  "$schema": "https://docs.renovatebot.com/renovate-schema.json",  "extends": ["config:base"],  "terraform": { "enabled": true  },  "packageRules": [ {"matchManagers": ["terraform"],"matchUpdateTypes": ["minor", "patch"],"automerge": false,"labels": ["terraform", "dependencies"] }  ]}
# Renovate PR の例: 自動生成されるタイトルと内容# タイトル: chore(deps): update terraform module vpc to v1.1.0# 本文:# This PR updates the following dependency:# - git::git@github.com:YOUR_ORG/terraform-modules.git//modules/vpc#from v1.0.1 to v1.1.0## Release notes: (CHANGELOG.md から自動抽出)# ## [1.1.0] - 2026-04-15# ### Added# - feat: add tags variable to S3 module# PR をレビューして計画を確認gh pr checkout 42cd envs/dev && terraform plan# Plan: 0 to add, 1 to change, 0 to destroy.# ~ resource "aws_s3_bucket" "app" { tags = {} -> { ... } }

手動更新と自動 PR 作成の使い分けは以下の通りです。

状況推奨アプローチ
patch バージョン(バグ修正のみ)Renovate 自動 PR → レビュー → マージ
minor バージョン(新機能追加)Renovate 自動 PR → 変更内容確認 → 段階昇格
major バージョン(破壊的変更)手動対応必須 → マイグレーションガイド参照
# モジュール利用者がバージョン状況を一覧確認するスクリプト例grep -rh '?ref=' envs/ | sort -u# git::git@github.com:YOUR_ORG/terraform-modules.git//modules/s3?ref=v1.0.1# git::git@github.com:YOUR_ORG/terraform-modules.git//modules/vpc?ref=v1.0.0# 最新タグと比較して古いバージョンを特定git ls-remote --tags git@github.com:YOUR_ORG/terraform-modules.git \  | grep -o 'v[0-9]*\.[0-9]*\.[0-9]*$' | sort -V | tail -5# v1.0.0# v1.0.1# v1.1.0# → modules/vpc の ref=v1.0.0 が古い(v1.1.0 に更新推奨)

Section 8 チェックリスト

Section 8 完了チェックリスト

  • ☐ SemVer の 3 段階(major / minor / patch)と Terraform モジュールへの適用を説明できる
  • git tag -a で注釈付きタグを作成し git push origin <tag> で push できる
  • source = "git::...?ref=v1.0.0" 形式でバージョン固定参照を設定できる
  • ☐ deploy key と Fine-grained PAT の使い分けを説明できる
  • ☐ タグの強制上書き(git tag -f + force push)が禁止である理由を説明できる
  • ☐ CHANGELOG.md を更新してからタグを打つ手順(Section 8-6)を実施できる
  • ☐ モジュールバージョン更新の全体フロー 10 ステップを理解している

Section 9 では、ここまで学んだ「ブランチ戦略 × 環境別 tfstate × モジュール git tag」を組み合わせた統合ハンズオンを実施します。terraform-git-handson/ リポジトリで一連のフローを端から端まで体験することで、本番運用に直結するスキルを定着させます。


Section 7〜8 まとめ — 学習の位置づけ

本記事の Section 7〜8 では、Terraform IaC 運用を「ブランチ戦略」という視点から再整理しました。

学習テーマ習得内容Section
環境分離パターンディレクトリ分離 / Workspace / ブランチ分離の比較7-1
アンチパターンブランチ分離が引き起こす tfstate drift と誤 apply7-2
推奨ディレクトリ構成envs/ + modules/ の標準レイアウト7-3
昇格フローdev → stg → prod の Gated Promotion7-4
モノレポ設計paths フィルタで CI スコープを制御7-5
SemVer 管理Terraform モジュールへの semantic versioning 適用8-1
git tag 操作注釈付きタグの作成・push・確認8-2
source ref 参照git::...?ref=vX.Y.Z によるバージョン固定8-3
認証設定deploy key と Fine-grained PAT の使い分け8-4
タグ運用ルール強制上書き禁止・新バージョンで対応8-5
CHANGELOG 管理Conventional Commits 形式での変更記録8-6
自動化Renovate による自動バージョン更新 PR8-8

次の Section 9 では、これら全要素を組み合わせた本番さながらのハンズオンを実施します。ここまでの概念的理解を「動く手順」として体験することで、チーム開発でそのまま使えるスキルとして定着させましょう。

Section 9: 応用トピック — モノレポ vs ポリレポ・ブランチ保護深掘り・第4/5弾布石

Section 1〜8 で「3大ブランチ戦略の選定と実践」「Terraform 環境別 tfstate 設計」「モジュール git tag バージョン管理」を習得しました。Section 9 では、チーム規模が拡大したときに直面する応用課題を扱います。モノレポ vs ポリレポの設計選択・ブランチ保護ルールの深掘り・CODEOWNERS による自動レビュアー割り当て、そして第4弾(セキュリティ)・第5弾(CI/CD)への布石を紹介します。


9-1. モノレポ vs ポリレポ — Terraform モジュール観点での比較

チームが成長し、Terraform モジュールが増えてくると「全モジュールを 1 つのリポジトリに集約(モノレポ)するか、モジュールごとに独立リポジトリを持つ(ポリレポ)か」という設計判断が必要になります。

観点別比較表

観点モノレポポリレポ
モジュール参照方法相対パス ../modules/vpcgit ref git::https://github.com/...?ref=v1.2.0
CI のスコープpaths フィルタで変更ディレクトリのみ実行リポジトリ単位で自動スコープ
バージョン管理モノレポ全体に 1 つの git tagモジュールごとに独立した SemVer tag
依存更新の連携1 PR で複数モジュールを同時変更可Renovate 等で自動 PR が各リポジトリに発行
アクセス制御CODEOWNERS でディレクトリ単位に設定リポジトリ単位で完結(シンプル)
推奨チーム規模小〜中規模チーム(共通基盤の共有が容易)大規模チーム / 独立性の高いモジュール
onboarding コストリポジトリが 1 つで把握しやすいリポジトリ数が増えると見通しが悪くなる
ブランチ戦略との相性trunk-based / GitHub Flow が相性◎各リポジトリで独立した戦略を選択できる

Terraform モジュールへの影響

モノレポの場合modules/ ディレクトリに複数モジュールを集約し、相対パスで参照します。

terraform-git-handson/├── envs/│├── dev/← modules/vpc を相対パスで参照│├── stg/│└── prod/└── modules/ ├── vpc/ ├── rds/ └── ecs/

各環境の main.tf は以下のように参照します。

# envs/dev/main.tf(モノレポ:相対パス参照)module "vpc" {  source = "../../modules/vpc"  # バージョン固定なし(同リポジトリのため常に最新)}

ポリレポの場合、Section 8 で学んだ git::...?ref=vX.Y.Z 形式で外部リポジトリを参照します。モジュールの更新は別 PR・別タグとして独立して管理されるため、利用側は Renovate による自動更新 PR でバージョンを追従します。

選択指針

チームが 5 名以下・モジュール数が 10 以下であればモノレポが運用しやすい出発点です。モジュール数が増え、チームが分かれ、独立したリリースサイクルが必要になった段階でポリレポへの移行を検討してください。Section 7-5 で紹介した paths フィルタを活用すれば、モノレポでも CI コストを最小化できます。

モノレポ構成のセットアップ(ハンズオン)

terraform-git-handson/ リポジトリでモノレポ構成を実際に作成してみましょう。

# terraform-git-handson/ がすでにある前提(Section 7-3 で作成済み)cd terraform-git-handson# modules/ ディレクトリに vpc / rds / ecs モジュールを追加mkdir -p modules/vpc modules/rds modules/ecs# 各モジュールの最小構成ファイルを作成for mod in vpc rds ecs; do  cat > modules/${mod}/main.tf <<'EOF'# modules/${mod}/main.tf — プレースホルダー(本番実装は第4弾以降)variable "env" {  type  = string  description = "環境名(dev / stg / prod)"}output "module_name" {  value = "${var.env}-${path.module}"}EOFdone# envs/dev/main.tf を相対パス参照に更新cat > envs/dev/main.tf <<'EOF'# envs/dev/main.tf — モノレポ:相対パスでモジュール参照module "vpc" {  source = "../../modules/vpc"  env = "dev"}module "rds" {  source = "../../modules/rds"  env = "dev"}EOF# 変更をコミットgit add modules/ envs/dev/main.tfgit commit -m "feat: add modules/vpc,rds,ecs for monorepo structure"# ディレクトリ構成確認find . -name "*.tf" | sort

find の出力で modules/vpc/main.tfmodules/rds/main.tfenvs/dev/main.tf が表示されれば、モノレポ構成の完成です。次のステップでは、Section 7-5 の paths フィルタを .github/workflows/ に追加し、変更したモジュールのみ CI が走る設定を行います(第5弾で実装)。


9-2. ブランチ保護 Required status checks — 深掘り

第2弾 Section 9-1 では Required status checks の概要を紹介しました。ここでは GitHub CLI を使った API 直接操作と、第5弾(GitHub Actions × Terraform)への布石となる具体的な設定方法を解説します。

GitHub CLI で保護ルールを API 設定

# main ブランチに Required status checks を設定# "terraform-plan" は GitHub Actions の job 名(第5弾で作成)gh api repos/YOUR_ORG/terraform-git-handson/branches/main/protection \  --method PUT \  --field required_status_checks='{"strict":true,"contexts":["terraform-plan"]}' \  --field enforce_admins=true \  --field required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true}' \  --field restrictions=null
フィールド設定値意味
required_status_checks.stricttrueベースブランチが最新でなければマージ不可
required_status_checks.contexts["terraform-plan"]CI で terraform plan が通過必須
enforce_adminstrue管理者にも同ルールを適用
required_approving_review_count1最低 1 名のレビュー承認が必要
dismiss_stale_reviewstrue新コミット後はレビュー承認を自動取り消し

status check 名の確認方法

# 直近の PR で通過した status check 名を一覧表示gh pr checks <PR番号> --repo YOUR_ORG/terraform-git-handson

第5弾では、この terraform-plan という job name を持つ GitHub Actions workflow を作成します。main ブランチへの PR 時に terraform plan を自動実行し、その結果をコメントとして PR に投稿するパイプラインを構築します。OIDC 認証により AWS クレデンシャルをリポジトリに保存せずに実行できるため、セキュリティと自動化を両立できます(第4弾・第5弾で詳解)。


9-3. CODEOWNERS × ブランチ戦略 — ディレクトリ別レビュアー自動割当

モノレポ運用でチームが分かれている場合、CODEOWNERS を活用することで「誰が何をレビューするか」を自動化できます。

CODEOWNERS の配置と書式

.github/CODEOWNERS ファイルを作成し、ディレクトリパターンと担当チームを記載します。

# .github/CODEOWNERS# 書式: <パターン> <GitHub チームまたはユーザー># 本番環境の変更は SRE チームが必須レビューenvs/prod/  @your-org/sre-team# Terraform モジュールの変更はプラットフォームチームが必須レビューmodules/ @your-org/platform-team# ステージング環境はアプリチームが担当envs/stg/@your-org/app-team# GitHub Actions workflow の変更は全員レビュー必須.github/workflows/  @your-org/sre-team @your-org/platform-team

ブランチ戦略との連携

CODEOWNERS と Required status checks・Required reviewers を組み合わせると、以下の自動ガバナンスが実現します。

フロー動作
envs/prod/ を変更する PR を作成@your-org/sre-team が自動でレビュアーに追加される
modules/vpc/ を変更する PR@your-org/platform-team のレビューなしにマージ不可
ブランチ保護 + CODEOWNERS の組合せPR 作成者がシニアエンジニアでも必ず担当チームのレビューが必要

GitFlow 運用でリリースブランチに release/* 保護を追加する場合も、CODEOWNERS で envs/prod/ に SRE チームを指定しておくことで、本番適用前の必須レビューを強制できます。


9-4. 第4弾予告 — セキュリティ: Secret Scanning / OIDC / 依存関係管理

Section 1〜9 で「ブランチ戦略の選定と実践」「Terraform IaC 運用」「モジュール管理」を習得しました。しかし、チーム開発で避けられない課題がセキュリティです。第4弾では以下を扱います。

テーマ内容本記事との関連
.gitignore による機密ファイル除外terraform.tfstate / *.tfvars / .env を絶対コミットしないSection 7-3 の推奨構成を前提に設定
GitHub Secret Scanningうっかりコミットした AWS キーを自動検出・無効化ブランチ保護と連動したセキュリティゲート
pre-commit hook による自動チェックtflint / tfsec をコミット前に強制実行GitFlow / GitHub Flow のどちらにも適用可
GitHub OIDCAWS クレデンシャル不要の安全な認証(第5弾の前提)Section 9-2 のブランチ保護設定と連動
Dependabot / Renovateモジュール・Action バージョンの自動更新 PRSection 8-8 の Renovate 設定を発展

terraform.tfstate には AWS リソースの設定値(場合によっては機密情報)が含まれます。これを誤ってコミットすると、git の履歴から完全削除することは困難です。第4弾では、この事故を防ぐための設計パターンを完全解説します。

先行準備 — .gitignore 設定(第4弾前の最重要ステップ)

第4弾を待たずに今すぐ実施すべき設定として、.gitignore に Terraform の機密ファイルを追加してください。これは「手順を読んで理解する」ではなく「今すぐコミットする」ステップです。

# terraform-git-handson/.gitignore に Terraform セキュリティパターンを追記cat >> .gitignore <<'EOF'# Terraform — 機密・自動生成ファイル(第4弾セキュリティ編の前提設定)*.tfstate*.tfstate.backup*.tfvars!terraform.tfvars.example# サンプルファイルは例外としてコミット可.terraform/crash.logoverride.tfoverride.tf.json*_override.tf*_override.tf.jsonEOF# .gitignore が既存の追跡ファイルに影響しないか確認git status# 問題なければコミットgit add .gitignoregit commit -m "chore: add terraform security patterns to .gitignore"

この設定により、terraform initterraform apply が生成するファイルが誤ってコミットされる事故を防ぎます。.tfvars には AWS アカウント ID やシークレット値が含まれる場合があるため、サンプルファイル(.tfvars.example)のみをコミットし、実際の値は環境変数や AWS Secrets Manager で管理するパターンを第4弾で詳解します。

⚠️ 重要: tfstate を誤ってコミットしてしまった場合git rm --cached terraform.tfstate でインデックスから除外し、.gitignore に追加後コミットします。しかし git の履歴に一度コミットした機密情報は完全削除が極めて困難です。git filter-repo や GitHub サポートへの依頼が必要になります。第4弾では事前防止策(pre-commit hook / Secret Scanning)を詳解します。


9-5. 第5弾予告 — CI/CD: GitHub Actions × Terraform で自動化パイプライン

第5弾では、Section 9-2 で設定した Required status checks を実際に機能させる GitHub Actions workflow を構築します。

ワークフロートリガー動作
terraform-plan.ymlPR 作成 / 更新terraform plan を実行し結果を PR コメントに投稿
terraform-apply.ymlmain ブランチへのマージterraform apply を自動実行(本番適用)
OIDC 認証全ワークフロー共通AWS クレデンシャルをリポジトリに保存せず安全に認証

OIDC(OpenID Connect)認証を使うと、GitHub Actions から AWS へのアクセスに長期クレデンシャル(Access Key / Secret Key)が不要になります。代わりに、ジョブ実行時に一時トークンを自動取得します。これにより「クレデンシャルが漏洩する」という最大のリスクを構造的に排除できます。

第4弾(セキュリティ設定)→ 第5弾(CI/CD 自動化)の順で学ぶことで、「手動 apply」から「安全な自動化パイプライン」への移行を完成させます。


Section 9 チェックリスト

Section 9 完了チェックリスト

  • ☐ モノレポ vs ポリレポの 8 観点比較表を参照し、自分のチームに適した選択を説明できる
  • gh api で Required status checks を設定するコマンドを理解している
  • .github/CODEOWNERS でディレクトリ別のレビュアーを自動割当できる
  • ☐ 第4弾(セキュリティ)の主要テーマ(Secret Scanning / pre-commit / OIDC)を説明できる
  • ☐ 第5弾(CI/CD)で構築する terraform-plan / apply ワークフローの全体像を把握している

Section 10: まとめ・スキル習得表・シリーズロードマップ・参考リンク

本記事では、Git/GitHub × Terraform 実践シリーズ第3弾として「ブランチ戦略の選定と Terraform IaC 運用への応用」を全10セクションで解説しました。Section 10 では学習の総括・習得スキルの整理・シリーズ全体のロードマップを提示します。


10-1. 本記事で習得したスキル表

#スキル習得内容Section
1ブランチ戦略の選定チーム規模・リリース頻度・Terraform 環境数から 3 戦略を評価軸で選定できる3-4, 3-5
2GitHub Flow ハンズオンfeature ブランチ → PR 作成 → Squash merge → ローカル同期の一連フローを実践4-1〜4-5
3GitFlow ハンズオンdevelop / release / hotfix ブランチを操作し、git flow CLI で自動化5-1〜5-5
4trunk-based + Feature FlagsTerraform 変数でフラグを実装し、main への直接コミット運用を体験6-1〜6-5
5Terraform 環境分離設計ディレクトリ分離 / Workspace / ブランチ分離の 3 パターンを比較・評価7-1
6Gated Promotion フローdev → stg → prod の段階的昇格と plan + destroy チェックを実践7-4
7SemVer × Terraform モジュールmajor / minor / patch の使い分けとモジュールバージョニングポリシーを設計8-1
8git tag バージョン管理注釈付きタグの作成・push・削除禁止ルール・CHANGELOG 連動を実践8-2, 8-5, 8-6
9プライベートモジュール参照git::https://...?ref=vX.Y.Z 形式 + deploy key / PAT 認証を設定8-3, 8-4
10モノレポ vs ポリレポ設計8 観点の比較表をもとにチームに適したリポジトリ構造を選択9-1
11ブランチ保護 API 操作gh api で Required status checks / Required reviewers を設定9-2
12CODEOWNERS 設定ディレクトリ別レビュアーの自動割当でコードオーナーシップを制度化9-3
13第4弾セキュリティ準備.gitignore での機密除外・Secret Scanning・pre-commit の概念を把握9-4
14第5弾 CI/CD 設計理解OIDC 認証 + terraform-plan/apply ワークフローの全体像を把握9-5

10-2. 3 戦略選定フローチャート(テキスト版総括)

本記事で解説した 3 戦略の選定基準を最終的にまとめます。

【ブランチ戦略 選定フローチャート】チームの状況を確認してください:Q1. リリースに「計画的なバージョン管理」が必要か? ├── Yes → Q2 へ └── No  → Q3 へQ2. hotfix(本番緊急修正)の発生頻度は? ├── 頻繁にある → GitFlow を採用 │└── develop / release / hotfix ブランチで厳格管理 └── ほとんどない → GitHub Flow を採用  └── feature → PR → main の軽量フローQ3. デプロイ頻度は週に何回か? ├── 毎日〜複数回 → trunk-based development を採用 │└── Feature Flags で未完成機能を隠しながら直接コミット └── 週1〜月数回 → GitHub Flow を採用(シンプルが最善)Terraform IaC 運用の追加考慮:  - 環境が 3 つ以上(dev/stg/prod)→ ディレクトリ分離 + GitHub Flow / GitFlow  - 環境が 1〜2 つ → Workspace + trunk-based も選択肢  - モジュール数 10 以上 → ポリレポ + Renovate 自動更新を検討

最初の一手: チームが 5 名以下・モジュールが少ない段階では GitHub Flow から始めることを強く推奨します。複雑な戦略は「複雑さを管理するコスト」が発生します。シンプルに始め、課題が出てから戦略を見直してください。

戦略移行のタイミング

チームが成長するにつれて、ブランチ戦略を変更する必要が生じることがあります。以下の兆候が現れたら移行を検討してください。

現在の戦略移行を検討すべき兆候次の戦略候補
GitHub Flowリリース日程を管理したい・hotfix が頻発GitFlow
GitHub Flowデプロイ頻度が週5回以上・チームが成熟trunk-based
GitFlowブランチが複雑すぎてマージコストが高いGitHub Flow(シンプル化)
trunk-basedFeature Flags の管理コストが高い・品質問題GitHub Flow(段階的 PR レビュー)

移行は段階的に行うことが鍵です。GitFlow から GitHub Flow へ移行する場合、まず develop ブランチを廃止して main への直接マージに切り替え、release ブランチは段階的に削減する手順が安全です。Section 3-4 の「移行パス4フェーズ図」(L688)を参考にしてください。

本記事の学習パスを振り返る

フェーズSection習得内容前提知識
基礎理解1〜33 戦略の概念・比較・Terraform との関係第1弾 Git 入門
GitHub Flow 実践4feature ブランチ → PR → Squash mergeSection 1〜3
GitFlow 実践5develop / release / hotfix 操作Section 1〜3
trunk-based 実践6Feature Flags + 直接コミットSection 1〜3
Terraform 環境設計7tfstate 分離・Gated PromotionSection 4〜6
モジュール管理8git tag SemVer・プライベート参照Section 7
応用・布石9モノレポ・ブランチ保護・セキュリティ準備Section 7〜8
総括10スキル整理・ロードマップ確認全 Section

10-3. 第4弾予告 — セキュリティ編

📌 次の記事(第4弾): セキュリティ — Secret Scanning / OIDC / 依存関係管理本記事で構築した Git/GitHub × Terraform の基盤に、セキュリティレイヤーを追加します。

  • 🔒 .gitignoreterraform.tfstate / *.tfvars を除外し、機密情報の流出を防ぐ
  • 🔍 GitHub Secret Scanning で誤コミットした AWS キーを即時検出・無効化する
  • ⚙️ pre-commit hook に tflint / tfsec を組み込み、コミット前に自動チェックを強制
  • 🔑 GitHub OIDC で AWS 認証を行い、長期クレデンシャルをリポジトリから完全排除
  • 🤖 Dependabot / Renovate で Terraform モジュール・GitHub Actions のバージョンを自動更新

第4弾は準備中です。公開をお楽しみに。


10-4. シリーズロードマップ

📚 Git/GitHub × Terraform実践シリーズ — 全体ロードマップ

各記事は独立して読めますが、第1弾→第2弾→第3弾の順に進むと、ローカル Git からチーム協業、本番運用まで一本の学習パスとして体系的に習得できます。第4弾・第5弾でセキュリティと自動化を追加することで、エンタープライズグレードの Terraform 運用体制が完成します。


10-5. 参考リンク

本記事の作成にあたり参照した公式ドキュメントおよび主要リソースを紹介します。

リソースURL内容
GitFlow 原典(Vincent Driessen)https://nvie.com/posts/a-successful-git-branching-model/2010 年に発表された GitFlow オリジナル論文
GitHub Flow(GitHub Docs)https://docs.github.com/ja/get-started/quickstart/github-flowGitHub 公式の GitHub Flow 解説
Trunk Based Developmenthttps://trunkbaseddevelopment.com/trunk-based development のコミュニティサイト
Terraform モジュール Registryhttps://registry.terraform.io/公式モジュールの検索・参照
Terraform Backend 設定https://developer.hashicorp.com/terraform/language/settings/backends/s3S3 バックエンドと State Lock 設定
GitHub CODEOWNERShttps://docs.github.com/ja/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-ownersCODEOWNERS 書式の公式リファレンス
Renovate 公式ドキュメントhttps://docs.renovatebot.com/Renovate による自動バージョン更新設定
Conventional Commitshttps://www.conventionalcommits.org/ja/v1.0.0/CHANGELOG 連動コミットメッセージ規約

本記事の総括

Git/GitHub × Terraform 実践シリーズ第3弾では、3 大ブランチ戦略の理解と選択から始まり、Terraform IaC 運用に最適化した環境別設計モジュールの git tag バージョン管理チーム規模拡大に備えた応用設定まで、一気通貫で習得しました。

  • GitFlow: リリースサイクルが明確・バージョン管理が必要なプロダクトに最適
  • GitHub Flow: シンプルさを武器にした軽量フロー。最初の選択肢として強く推奨
  • trunk-based: 高速デプロイ・Feature Flags による柔軟な機能制御

Terraform 運用では「ブランチ戦略とディレクトリ分離の組合せ」が鍵です。Section 7-8 で学んだ envs/dev|stg|prod 構成と Gated Promotion フロー・モジュールの SemVer tag 管理を組み合わせることで、チームが安全に本番インフラを管理できる基盤が整います。

第4弾(セキュリティ)・第5弾(CI/CD 自動化)で、この基盤にセキュリティレイヤーと自動化パイプラインを追加し、エンタープライズグレードの Terraform 運用体制を完成させましょう。

第3弾 完了チェックリスト

  • ☐ 3 戦略(GitFlow / GitHub Flow / trunk-based)の特徴と適用場面を説明できる
  • ☐ チーム規模・リリース頻度・Terraform 環境数から最適な戦略を選定できる
  • ☐ GitHub Flow / GitFlow / trunk-based の各ハンズオンを terraform-git-handson/ で実施した
  • ☐ Feature Flags を Terraform 変数で実装し、trunk-based 運用を体験した
  • ☐ Terraform 環境別ディレクトリ構成(envs/dev|stg|prod)を設計・構築できる
  • ☐ Gated Promotion フロー(dev → stg → prod 昇格)の手順を実施できる
  • ☐ SemVer に基づく git tag を作成し、モジュールをバージョン固定で参照できる
  • ☐ プライベートリポジトリモジュールを deploy key / PAT で認証して参照できる
  • ☐ CODEOWNERS でディレクトリ別レビュアーを設定し、自動割当を確認した
  • ☐ 第4弾(セキュリティ)・第5弾(CI/CD)の学習計画を立てた