モジュールの出力は、親モジュールや他スタックに提供する“公開API”です。どの属性を、どの形で、どこまで公開するかで、チームの拡張性と安全性が大きく変わります。
本稿では、Terraform Associateレベルで押さえるべき安定仕様に基づき、出力の基本から設計パターン、機密値、依存関係、アンチパターンまでをまとめます。
output ブロックは、モジュール外へ値を公開する仕組みです。子モジュールの出力は親から module.<name>.<output> で参照でき、ルートモジュールの出力は terraform output コマンドで取得できます。
出力は式から型が推論されます。必要なら tolist、tomap、toset、tostring などで明示的に形を整えます。description、sensitive、depends_on を指定できます。
基本的な出力の宣言(子→親)
# modules/storage/outputs.tf
output "bucket_id" {
description = "S3 bucket ID"
value = aws_s3_bucket.this.id
}
# root/main.tf
module "storage" {
source = "./modules/storage"
}
output "app_bucket_id" {
value = module.storage.bucket_id
description = "App uses this bucket"
}出力は“安定インターフェース”として最小限かつ将来互換を意識して設計します。下位リソースの全属性を丸ごと出すのではなく、利用側が本当に必要とするキー属性(ID、エンドポイント、ARN、ポートなど)に絞るのが基本です。
複数の関連値はオブジェクトでまとめて返すと拡張が容易です。個別スカラーを乱立させるより、service.endpoint など論理名でグルーピングすると命名衝突を避けられます。
| 構造/手段 | 主な用途 | 公開範囲 | 主な注意点 |
|---|---|---|---|
| locals(非出力) | モジュール内部の計算・再利用 | 非公開(内部限定) | 外部利用を想定しない。テストしやすく保つ。 |
| 出力(スカラー) | 単一のキー属性(ID, ARN)共有 | 親モジュール | 属性増に弱い。命名が増えがち。 |
| 出力(オブジェクト) | 関連属性のまとまった公開 | 親モジュール | キー名の契約がAPI。後方互換を壊さない。 |
| data.terraform_remote_state | 別ステートからの値取得(スタック間連携) | 他ワークスペース/状態 | ステート依存で結合が強くなる。組織標準とガバナンス必須。 |
モジュール間の出力フロー(安定インターフェース)
オブジェクト出力でのインターフェース安定化
# modules/runtime/outputs.tf
output "service" {
description = "Service interface for consumers"
value = {
id = aws_ecs_service.this.id
name = aws_ecs_service.this.name
endpoint = aws_lb.this.dns_name
port = 443
}
}
# root/main.tf(利用側)
module "runtime" {
source = "./modules/runtime"
# ...inputs
}
locals {
service_endpoint = module.runtime.service.endpoint
}出力は式から型推論されます。契約(API)として形を安定させたい場合、toset/tomap/tolist などで明示的に整形し、キー付きオブジェクト(マップ)を構築します。can/try を併用して将来の拡張に備えると安全です。
数値・文字列の曖昧さを避けるため tostring、tonumber を使って表現を統一し、利用側の破壊的変更を防ぎます。
形の明示と後方互換のための整形例
# オブジェクトの形を明示的に構築
locals {
service_if = {
id = aws_ecs_service.this.id
name = aws_ecs_service.this.name
endpoint = aws_lb.this.dns_name
# まだ無いが将来に備えて try で既定値
zone_id = try(aws_lb.this.zone_id, null)
ports = toset([443])
}
}
output "service" {
value = local.service_if
description = "Stable service interface"
}
# 数値/文字列の揺れを抑える
output "replicas" {
value = tonumber(var.desired_count)
}output に sensitive = true を指定、または sensitive() で包むと、計画表示や terraform output で値が伏せられます。ただし状態ファイルには値が格納されます。リモートバックエンドのアクセス制御やワークスペース分離と併せて保護してください。
下流へは機密性が伝播します。必要に応じて nonsensitive() を使って明示的に解除しますが、漏洩リスクを十分に評価してください。
機密値の出力と伝播制御
# パスワードを機密として出力
output "db_password" {
description = "Generated DB password (avoid exposing if possible)"
value = sensitive(random_password.db.result)
sensitive = true
}
# 非機密として扱いたいメタ情報のみを公開
output "db" {
value = {
endpoint = aws_db_instance.this.address
port = aws_db_instance.this.port
engine = aws_db_instance.this.engine
}
}
# 機密の解除は慎重に
output "password_for_debug" {
value = nonsensitive(random_password.db.result)
sensitive = false
depends_on = [null_resource.allow_debug_window]
}出力は apply 時点の実値に基づいて評価されます。unknown(計画時未確定)な値は plan 段階では具体化されません。値の算出順序が重要な場合、出力にも depends_on を指定して依存関係を明示できます(Terraform 0.13以降)。
同一モジュール内のリソース間の依存は、通常は参照で自動解決されます。出力はあくまで“外向きの契約”であり、内部の計算や依存解決には locals や明示参照を使います。
出力での depends_on とスタック間参照の例
# 出力の評価順を明示(post init 処理の完了を待つ)
resource "null_resource" "post" {
triggers = {
service_id = aws_ecs_service.this.id
}
provisioner "local-exec" {
command = "echo post-init"
}
}
output "service_endpoint" {
value = aws_lb.this.dns_name
depends_on = [null_resource.post]
}
# 別ステートからの参照(root 側の例)
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "tf-states"
key = "net/terraform.tfstate"
region = "ap-northeast-1"
}
}
module "runtime" {
source = "./modules/runtime"
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
subnets = data.terraform_remote_state.network.outputs.subnets
}出力はモジュールの公開APIです。キー名や意味を変えるのは破壊的変更なので、モジュールのメジャーバージョンアップに合わせます。移行期間は旧出力を残しつつ新出力を追加し、利用側を段階的に移行します。
アンチパターンは、リソース全体をそのまま公開すること、機密値を安易に公開すること、文字列連結で不安定な値を組み立てることです。安定キーに限定し、構造化し、内部実装の漏洩を避けましょう。
非破壊移行の例(旧キーを残して新構造へ移行)
# 新API(推奨)
output "service" {
description = "Preferred aggregated interface"
value = {
id = aws_ecs_service.this.id
endpoint = aws_lb.this.dns_name
port = 443
}
}
# 旧API(非推奨: 互換用)
output "service_endpoint" {
description = "Deprecated: use output 'service.endpoint' instead"
value = aws_lb.this.dns_name
}
# 利用側は段階的に module.runtime.service.endpoint へ移行Associate
問題 1
複数チームが利用する共通モジュールで、ECSサービスのID・エンドポイント・ポートを公開したい。将来フィールドが増える可能性があり、後方互換性を維持したい。最も適切な設計はどれか?
正解: C
後方互換性と拡張性を両立するには、関連属性を1つのオブジェクト出力にまとめ、将来はキー追加で非破壊に拡張できる形にするのが最適。スカラー分割はキー増で破壊的になりやすく、リソース全体の露出や remote_state の直接参照は密結合を招く。
output で type を指定できますか?
出力ブロックに type 引数はありません。型は式から推論されます。形を安定させたい場合は tolist/tomap/toset などで明示的に整形し、固定キーのオブジェクトを構築して契約を文書化します.
sensitive = true にしても state に値は保存されますか?
はい。計画表示や terraform output で値は伏せられますが、状態ファイルには保存されます。リモートバックエンドと厳格なアクセス制御、監査で保護してください。
出力に depends_on を書く必要はありますか?
通常は不要です。参照しているリソースやモジュールへの暗黙依存で十分です。評価順を明示したい特殊なケースのみ、出力に depends_on を付けます(Terraform 0.13以降でサポート)。
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を用いた既存リソース参照の基本、選択基準、評価順序、...