ハブ記事: Terraform モジュール 完全ガイド →
設計・配布・運用まで Terraform モジュールの全体像を一望できるハブ記事
チームや事業部ごとにTerraformコードが増えると、命名、タグ、セキュリティ基準、ネットワーク構成などの差異が運用負債になります。共有モジュールを戦略的に整えることで、横断的な再利用と統制を両立できます。
本稿では、公式ドキュメントに沿った安定的な概念に基づき、モジュールの設計原則、プロバイダと依存関係の扱い、配布とバージョニング、テストとリリースを実務目線で解説します。Professionalレベルの試験で問われやすい論点も明示します。
共有モジュールの第一目標は、反復するインフラ要素を安全かつ一貫して提供することです。設計は「消費者(利用チーム)が誤りにくいAPI」を最優先にします。Terraformの安定概念(ルート/子モジュール、入力変数、出力、required_providers、バージョン制約、Private Registry)を土台に据えると、長期運用に耐えます。
試験観点では、モジュールは状態やバックエンドを内包しないこと、プロバイダはルートで定義して子に明示的に渡せること、そしてセマンティックバージョニングを用いた互換性管理が要点です。
共有モジュール最小スケルトン例
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%は拡張点(任意のタグ、ポリシードキュメント受け取り等)で吸収する構成が現実的です。
変数の型・検証・機密指定の例
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ブロックを含めないのが公式の推奨に合致します。
ルートでのプロバイダ定義と子モジュールへの受け渡し
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"
}
Terraform Cloud/EnterpriseのPrivate RegistryやVCS連携により、SemVerタグでバージョン付けされたモジュールを組織内に公開できます。命名規則(<namespace>/<name>/<provider>)を統一し、README・使用例・互換性ポリシーを必ず添付します。
リポジトリ構成はモノレポとマルチレポに大別できます。依存分離、リリース粒度、CI効率、アクセス制御の観点で選定し、どちらでもタグ駆動のバージョン管理を徹底します。
| 観点 | モノレポ(modules配下に複数) | マルチレポ(1モジュール=1リポジトリ) | 試験の押さえどころ |
|---|---|---|---|
| 変更影響の分離 | 相互影響に注意。CIで影響範囲検出が必要 | 完全分離しやすい | 互換性管理はSemVerで一貫。タグ基準で配布 |
| リリース粒度 | 一括運用。タグ戦略が複雑になりがち | モジュール単位で明快 | Private Registryはタグをバージョンとして扱う |
| CI効率 | まとめて最適化可。差分判定の工夫が必要 | 単純だがCI数が増えがち | validate/fmt/planを標準化 |
| アクセス制御 | 単位が粗い。CODEOWNERS等で補完 | きめ細かい。権限設計が容易 | 機密モジュールの露出を避ける |
共有モジュール配布の全体像
タグ駆動のリリース(例)
# 変更コミット後にSemVerタグを付与
$ git tag v1.2.0
$ git push origin v1.2.0
# VCS連携されたPrivate Registryがタグを検出し、モジュールv1.2.0を公開モジュールはセマンティックバージョニングに従い、破壊的変更をメジャーにのみ含めます。マイナーは後方互換の追加、パッチは不具合修正に限定します。CHANGELOGに差分と移行手順を書き、Deprecatedは事前予告期間を設けます。
消費側はversion引数で制約を指定します。~> は右端のみを可変にするのに便利で、>= と < を組み合わせると上限も含めて厳密に管理できます。
消費側でのバージョン制約例
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とレビューで補完します。
モジュール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モジュールの設計として、最も適切な組み合わせはどれか。
正解: B
Terraformの推奨は、プロバイダ設定をルートモジュールで行い、子モジュールはrequired_providersで依存を宣言すること。providers引数でエイリアス付きプロバイダを渡す設計が安全。配布はPrivate RegistryのSemVerタグで行い、消費側はversion制約で管理する。子モジュールにbackendを持ち込む設計やlatest固定、タグ不使用は避けるべき。
子モジュールにproviderブロックを書いてもよいですか?
原則避けます。子はterraformのrequired_providersで依存を宣言するに留め、具体的な認証・リージョン・エイリアスはルートで定義し、moduleのproviders引数で渡します。こうすることで認証情報の責務分離と再利用性が高まります。
破壊的変更をどう扱えばよいですか?
セマンティックバージョニングに従いメジャー更新でのみ導入します。CHANGELOGに影響と移行手順を記載し、Deprecatedは猶予期間をもって段階的に削除します。消費側はversion制約で上限を設け、段階的に検証・更新します。
複数クラウドやアカウント間で共通化するには?
プロバイダ固有の実装はモジュールを分け、上位の合成モジュール(composition)で共通ポリシーや命名・タグを適用します。ルートで複数プロバイダをエイリアス定義し、必要なものを各子に明示的に渡すのが基本です。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...