ハブ記事: Terraform モジュール 完全ガイド →
設計・配布・運用まで Terraform モジュールの全体像を一望できるハブ記事
モジュールはTerraformの再利用単位ですが、真価が出るのは「合成」した時です。ネットワーク、セキュリティ、アプリ基盤など複数のモジュールを安全に組み合わせるには、依存関係の表現、プロバイダ伝播、入力・出力の設計、バージョン固定など、いくつか外せないポイントがあります。
本稿では、公式ドキュメントの挙動に基づき、壊れにくい合成パターンを解説します。試験対策として問われやすい論点(module間のdepends_on、for_eachのキー安定性、providerのエイリアス伝播、バージョン制約の書き方等)も併せて確認します。
複数モジュールの合成は通常、ルートモジュールで行います。各子モジュールは明確な境界(入力variablesと出力outputs)を持ち、ルートがそれらを配線します。入力の型を厳密に定義し、出力は後続のモジュールが使いやすい最小限の形に整形します。
モジュールのsourceはレジストリ、VCS、ローカルパスのいずれかを用い、安定運用のためversionを明示します。プロバイダはルートで初期化し、必要に応じて子へエイリアスをマッピングします。
| パターン | 概要 | 強み | 注意点 |
|---|---|---|---|
| ルート合成(Flat) | ルートで複数子モジュールを直接配線 | 見通しがよくデバッグ容易 | ルートが肥大化しやすい |
| ラッパーモジュール(Facade) | 一連の構築を1つの再利用モジュールに束ねる | 再利用性と標準化が高い | 内部差し替え時はバージョン管理を厳密に |
| 環境ごとスタック分割 | env別ディレクトリで同じ合成を繰り返す | 状態と責任分離が明確 | 定義の重複はテンプレ化や共通ラッパーで吸収 |
典型的なモジュール合成(ルートでの配線)
ルートでの合成例(sourceの固定と出力配線)
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
module "network" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = var.name
cidr = var.vpc_cidr
}
module "security" {
source = "git::https://example.com/org/security-group.git?ref=v1.4.2"
vpc_id = module.network.vpc_id
}
module "app" {
source = "./modules/app"
subnet_ids = module.network.private_subnets
security_groups = [module.security.sg_id]
}
output "app_endpoint" {
value = module.app.endpoint
}モジュール間の結合は出力→入力の受け渡しで行います。参照がある限りTerraformは暗黙依存を解決して評価順を組み立てます。中間整形はlocalsを使い、後段モジュールの入力仕様に合わせた形にします。
出力のsensitiveは表示を抑制しますが、状態に平文で保持される点は変わりません。秘匿値はプロバイダ側の機密管理や外部シークレットストアの活用を検討してください。
出力を配線するためのlocals活用
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = var.name
cidr = var.vpc_cidr
}
locals {
app_subnet_ids = slice(module.vpc.private_subnets, 0, 2)
}
module "db" {
source = "./modules/rds"
subnet_ids = local.app_subnet_ids
}
output "db_endpoint" {
value = module.db.endpoint
sensitive = true
}Terraformは参照(例: resourceまたはmoduleの出力参照)から暗黙の依存グラフを構築します。参照がないが手続き上の順序が必要な場合のみ、moduleブロックのdepends_onメタ引数で明示依存を付与できます。
データソースは読み取り専用であり、計画時に評価されます。副作用や順序制御のためにデータソースへ不必要なdepends_onを付けると複雑化の原因になります。まずは参照による暗黙依存で足りるかを検討してください。
module間の明示依存(暗黙参照がない場合)
module "iam" {
source = "./modules/iam-baseline"
}
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = var.name
# このモジュールはiamの出力を直接参照しないが、事前適用が必要
depends_on = [module.iam]
}同一モジュールを複数インスタンス化する場合、countまたはfor_eachが使用できます。安定したキーによるアドレッシングと差分制御の観点から、実務ではfor_eachを優先するのが一般的です。
for_eachのキーは将来も安定する識別子(論理名やID)を用い、並べ替えや削除時の破壊的置換を避けます。
for_eachでサブネットモジュールを複製し、出力を集約
variable "subnets" {
type = map(object({
cidr = string
az = string
}))
}
module "subnet" {
source = "./modules/subnet"
for_each = var.subnets
name = each.key
cidr = each.value.cidr
az = each.value.az
}
# 呼び出し側が使いやすいように、キー付きで出力を束ねる
output "subnet_ids" {
value = { for k, m in module.subnet : k => m.id }
}子モジュールは呼び出し元のプロバイダを継承します。エイリアス付きのプロバイダを子へ渡す場合は、moduleブロックのproviders引数でマッピングします。子側で期待するプロバイダ名(エイリアス含む)に対し、呼び出し元の定義を割り当てます。
required_providersでプロバイダのバージョン範囲を宣言し、モジュールsourceではversionを固定します。リリースの互換性ポリシーに沿って~>などの範囲指定を使い分けます。
プロバイダのエイリアス伝播とバージョン制約
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0, < 6.0"
}
}
}
provider "aws" {
alias = "use1"
region = "us-east-1"
}
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
# 子モジュールはデフォルト名awsを期待している想定
module "logs" {
source = "./modules/logs"
providers = {
aws = aws.usw2
}
}
# レジストリのモジュールはversionで固定
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.1"
name = var.name
cidr = var.vpc_cidr
}本番・検証などの環境は、別ディレクトリや別バックエンドで状態を分離するのが実務の基本です。ワークスペースは軽量なバリアントには有用ですが、環境境界や権限分離の代替にはなりません。
環境間で同一の合成を適用する場合、共通のラッパーモジュールを作り、envごとに入力値とバックエンド設定だけを変えると管理が容易です。リファクタ時はmovedブロックでリソースアドレスの移動を明示し、状態破壊を避けます。
環境ディレクトリ分割の最小例
prod/
main.tf
variables.tf
backend.hcl # prod用のremote backend設定
terraform.tfvars
staging/
main.tf
variables.tf
backend.hcl # staging用
terraform.tfvars
# 例: backend.hcl は init 時に -backend-config=backend.hcl で供給
# moved ブロックの例(リソースをラッパーへ移動する際)
moved {
from = aws_iam_role.app
to = module.security.aws_iam_role.app
}Pro
問題 1
ルートモジュールでnetwork、iam、eksの3モジュールを合成しています。eksはiamの出力を直接参照していませんが、作成順としてiamが先に適用される必要があります。Terraformのベストプラクティスとして最も適切な方法はどれですか。
正解: A
参照がないが手続き順が必要な場合は、moduleブロックのdepends_onで明示依存を付与します。ダミーの参照で擬似依存を作るのはアンチパターン、lifecycleは破壊順序の制御であり依存解決ではありません。refreshは状態更新で順序制御にはならないため不適切です。
モジュールの複製にはcountとfor_eachのどちらを使うべきですか?
安定した識別子で差分を最小化できるためfor_eachが推奨です。countはインデックスがずれやすく、挿入・削除時に意図せぬ置換を誘発します。試験でもfor_eachのキー安定性が問われやすいです。
子モジュールに別リージョンのプロバイダを使わせるにはどうしますか?
親でエイリアス付きのproviderを定義し、moduleブロックのproviders引数で子が期待するプロバイダ名へマッピングします。例: providers = { aws = aws.usw2 }。子はその名前のプロバイダを利用します。
レジストリのモジュールとプロバイダのバージョン管理はどう分けますか?
モジュールはmoduleブロックのversionで固定し、プロバイダはrequired_providersで範囲指定します。両者を明示することで再現性の高い計画が得られます。
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を用いた既存リソース参照の基本、選択基準、評価順序、...