Terraform

Terraform共有モジュール戦略: 組織横断の再利用を実現する実務

2026-04-19
NicheeLab編集部

ハブ記事: Terraform モジュール 完全ガイド

設計・配布・運用まで Terraform モジュールの全体像を一望できるハブ記事

チームや事業部ごとにTerraformコードが増えると、命名、タグ、セキュリティ基準、ネットワーク構成などの差異が運用負債になります。共有モジュールを戦略的に整えることで、横断的な再利用と統制を両立できます。

本稿では、公式ドキュメントに沿った安定的な概念に基づき、モジュールの設計原則、プロバイダと依存関係の扱い、配布とバージョニング、テストとリリースを実務目線で解説します。Professionalレベルの試験で問われやすい論点も明示します。

原則とゴール: 共有モジュールで解くべき問題の明確化

共有モジュールの第一目標は、反復するインフラ要素を安全かつ一貫して提供することです。設計は「消費者(利用チーム)が誤りにくいAPI」を最優先にします。Terraformの安定概念(ルート/子モジュール、入力変数、出力、required_providers、バージョン制約、Private Registry)を土台に据えると、長期運用に耐えます。

試験観点では、モジュールは状態やバックエンドを内包しないこと、プロバイダはルートで定義して子に明示的に渡せること、そしてセマンティックバージョニングを用いた互換性管理が要点です。

  • APIファースト: 変数は型と検証を定義し、デフォルトは安全側に寄せる
  • 最小権限と強制タグなどの組織ポリシーをモジュールで標準化
  • 子モジュールは状態バックエンドや認証情報を持ち込まない(ルート責務)
  • 互換性の約束(SemVer)と移行ガイド(CHANGELOG、DEPRECATION)を運用に組み込む
  • 公式機能を優先(Private Registry、required_providers、version constraints)

共有モジュール最小スケルトン例

terraform {
  required_version = ">= 1.0.0"
  required_providers {
    aws = {
      source = "hashicorp/aws"
      # バージョン制約は必要に応じて設定(例: ">= 4.0")
    }
  }
}

# main.tf
# 組織標準タグを強制する例
variable "name" {
  type        = string
  description = "リソースの論理名"
}

variable "tags" {
  type        = map(string)
  description = "追加タグ(標準タグとマージ)"
  default     = {}
}

locals {
  standard_tags = {
    owner      = "platform"
    managed-by = "terraform"
  }
  merged_tags = merge(local.standard_tags, var.tags)
}

# ここにリソースを定義し、tags = local.merged_tags を適用

output "id" {
  value       = "example-id"
  description = "作成したリソースのID"
}

インターフェース設計: 入出力、バリデーション、安定性の管理

入力は型、検証、記述を必ず定義し、利用者が値の意味と制約を即時に理解できるようにします。sensitiveフラグは機密値の誤出力を防ぎ、nullableやデフォルトの扱いは慎重に。出力は下位互換性を壊さない命名とスキーマで維持します。

マップ・オブジェクト型は将来拡張に強い一方、過剰な入れ子は学習コストを上げます。モジュールのAPIは80%ユースケースを簡単に、20%は拡張点(任意のタグ、ポリシードキュメント受け取り等)で吸収する構成が現実的です。

  • 変数に型/description/validation/sensitiveを付与
  • 安全側のデフォルト(例: 暗号化有効、公開アクセス無効)
  • 出力の互換性: 既存キーの削除・意味変更はメジャー更新でのみ実施
  • map(string)やobject({ ... })で将来拡張を確保
  • ドキュメントは変数・出力の挙動と例をセットで提示

変数の型・検証・機密指定の例

variable "cidr_block" {
  type        = string
  description = "VPCのCIDR。RFC1918内で指定"
  validation {
    condition     = can(cidrnetmask(var.cidr_block))
    error_message = "有効なCIDRを指定してください。"
  }
}

variable "enable_public_subnets" {
  type        = bool
  description = "パブリックサブネットを作成するか"
  default     = false
}

variable "admin_password" {
  type        = string
  description = "管理用パスワード"
  sensitive   = true
}

output "vpc_id" {
  value       = aws_vpc.this.id
  description = "作成したVPCのID"
}

プロバイダと依存の扱い: ルートで定義し子に渡す

子モジュールは required_providers を宣言しますが、具体的なプロバイダ設定(認証、リージョン、エイリアス)はルートモジュールで行い、providers引数で渡します。これにより、認証情報や接続先の責務がルートに集約され、再利用性と安全性が増します。

バックエンド設定は常にルートモジュールの責務です。子モジュールにbackendブロックを含めないのが公式の推奨に合致します。

  • 子: terraform { required_providers {...} } はOK、providerブロックは原則持たない
  • ルート: providerブロック定義、必要ならaliasを付与
  • moduleブロックのproviders引数でエイリアス付きインスタンスを渡す
  • データソース・外部依存は明示的に入力で受け取りやすく設計
  • backendはルート限定(子で定義しない)

ルートでのプロバイダ定義と子モジュールへの受け渡し

provider "aws" {
  region = "us-east-1"
  alias  = "use1"
}

module "network" {
  source    = "app/network/aws"
  version   = "~> 1.2"
  providers = {
    aws = aws.use1
  }
  name       = "core"
  cidr_block = "10.0.0.0/16"
}

配布とリポジトリ戦略: Private Registryとモノレポ/マルチレポ

Terraform Cloud/EnterpriseのPrivate RegistryやVCS連携により、SemVerタグでバージョン付けされたモジュールを組織内に公開できます。命名規則(<namespace>/<name>/<provider>)を統一し、README・使用例・互換性ポリシーを必ず添付します。

リポジトリ構成はモノレポとマルチレポに大別できます。依存分離、リリース粒度、CI効率、アクセス制御の観点で選定し、どちらでもタグ駆動のバージョン管理を徹底します。

  • VCSのリリースタグ(v1.2.3)でPrivate Registryに自動公開
  • 命名は <org>/<module>/<provider> を統一
  • READMEに入力/出力/例/互換性方針/サポート範囲を明記
  • 公開前にvalidate/fmt/静的チェック/例でのplanを必須化
  • 破壊的変更はメジャー更新+移行手順を同時公開
観点モノレポ(modules配下に複数)マルチレポ(1モジュール=1リポジトリ)試験の押さえどころ
変更影響の分離相互影響に注意。CIで影響範囲検出が必要完全分離しやすい互換性管理はSemVerで一貫。タグ基準で配布
リリース粒度一括運用。タグ戦略が複雑になりがちモジュール単位で明快Private Registryはタグをバージョンとして扱う
CI効率まとめて最適化可。差分判定の工夫が必要単純だがCI数が増えがちvalidate/fmt/planを標準化
アクセス制御単位が粗い。CODEOWNERS等で補完きめ細かい。権限設計が容易機密モジュールの露出を避ける

共有モジュール配布の全体像

VCS (Git)repos for modulesPrivate Module Registrypush tag v1.2.3Stack A(root)consumeStack B(root)consumeVCS → Private Module Registry → Stack A / Stack B

タグ駆動のリリース(例)

# 変更コミット後にSemVerタグを付与
$ git tag v1.2.0
$ git push origin v1.2.0
# VCS連携されたPrivate Registryがタグを検出し、モジュールv1.2.0を公開

バージョニングと互換性: SemVerと制約の使い分け

モジュールはセマンティックバージョニングに従い、破壊的変更をメジャーにのみ含めます。マイナーは後方互換の追加、パッチは不具合修正に限定します。CHANGELOGに差分と移行手順を書き、Deprecatedは事前予告期間を設けます。

消費側はversion引数で制約を指定します。~> は右端のみを可変にするのに便利で、>= と < を組み合わせると上限も含めて厳密に管理できます。

  • 破壊的変更はメジャー更新+移行ガイド必須
  • Deprecatedは2リリース程度の猶予を設定し警告を出す
  • 消費側は一気にlatestへ飛ばさず段階更新
  • モジュールの出力変更は互換層(旧出力を残す)で緩和

消費側でのバージョン制約例

module "network" {
  source  = "app/network/aws"
  version = "~> 1.4"      # 1.4.x に固定(将来2.0系は拾わない)
  # あるいは厳密に
  # version = ">= 1.4.0, < 2.0.0"
}

検証とリリース: 誰でも安全に出せる仕組み

モジュールは単体でterraform validate/fmtが通ること、examplesディレクトリでの最小使用例がplan可能であることをゲートにします。CIはlintよりもまず公式のvalidate/fmt/初期化と、例に対するplanの安定実行を重視します。

最近のTerraformにはテスト支援機能が追加されていますが、導入は組織の互換性ポリシーに従い段階的に進めてください。まずは不変の公式コマンドでの検証をパイプラインに固定し、破壊的変更の検出をCHANGELOGとレビューで補完します。

  • module直下で terraform fmt -check, terraform init -backend=false, terraform validate
  • examples/ 最小構成で terraform init/plan を実行し成功を確認
  • Registry公開はCIからのみ(手動禁止)
  • リリースノートと互換性ラベル(Added/Changed/Deprecated/Removed/Fixed/Security)を標準化
  • 消費スタックでの段階ロールアウトとロールバック手順を用意

モジュールCIの最小検証ステップ(例)

#!/usr/bin/env bash
set -euo pipefail

# 1) コード整形と基本検証
terraform -chdir=. fmt -check -diff
terraform -chdir=. init -backend=false
terraform -chdir=. validate

# 2) 最小使用例でのplan(状態は書かない)
terraform -chdir=examples/minimal init -backend=false
terraform -chdir=examples/minimal plan -input=false -lock=false -refresh=false -var "name=ci-test"

問題で確認

Pro

問題 1

組織横断で再利用するTerraformモジュールの設計として、最も適切な組み合わせはどれか。

  1. 子モジュールにbackendブロックを定義し、各チームで状態を分離できるようにする。プロバイダも子モジュール内で設定する。
  2. 子モジュールはrequired_providersのみ宣言し、プロバイダ設定はルートで行いproviders引数で渡す。バージョンはPrivate RegistryのSemVerタグで管理する。
  3. プロバイダはグローバルに1つだけ定義し、全スタックが同じリージョンを使うよう強制する。モジュールのバージョンはlatestを常に使用する。
  4. バージョン管理はGitのブランチ名で行い、タグは使わない。破壊的変更はREADMEにだけ記載する。

正解: B

Terraformの推奨は、プロバイダ設定をルートモジュールで行い、子モジュールはrequired_providersで依存を宣言すること。providers引数でエイリアス付きプロバイダを渡す設計が安全。配布はPrivate RegistryのSemVerタグで行い、消費側はversion制約で管理する。子モジュールにbackendを持ち込む設計やlatest固定、タグ不使用は避けるべき。

よくある質問

子モジュールにproviderブロックを書いてもよいですか?

原則避けます。子はterraformのrequired_providersで依存を宣言するに留め、具体的な認証・リージョン・エイリアスはルートで定義し、moduleのproviders引数で渡します。こうすることで認証情報の責務分離と再利用性が高まります。

破壊的変更をどう扱えばよいですか?

セマンティックバージョニングに従いメジャー更新でのみ導入します。CHANGELOGに影響と移行手順を記載し、Deprecatedは猶予期間をもって段階的に削除します。消費側はversion制約で上限を設け、段階的に検証・更新します。

複数クラウドやアカウント間で共通化するには?

プロバイダ固有の実装はモジュールを分け、上位の合成モジュール(composition)で共通ポリシーや命名・タグを適用します。ルートで複数プロバイダをエイリアス定義し、必要なものを各子に明示的に渡すのが基本です。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
Terraform

Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける

Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...

Terraform

Terraform Authoring & Ops Pro: 上位資格の範囲と対策

上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...

Terraform

Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす

Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...

Terraform

Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義

Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...

Terraform

Terraform Data Source徹底理解:既存リソースの参照で壊さず足す

Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...

Terraformの記事一覧 (101件)
© 2026 NicheeLab All rights reserved.