マルチ環境の設計で最重要なのは「状態と権限の分離」です。特に本番は事故の影響範囲が大きく、設計の粗はそのままリスクになります。
本稿では、ディレクトリ分割と共通モジュールを基本に、バックエンド、変数、Terraform Cloud/Enterprise の組合せで dev/stg/prod を安全に運用する方法を解説します。試験対策として問われやすい落とし穴も明記します。
dev/stg/prod の分離は「コード」「状態ファイル」「認証情報」「クラウドアカウント(またはサブスクリプション/プロジェクト)」「ネットワーク」の5層で考えます。最低限、状態ファイルと認証情報は環境ごとに分離し、可能であればクラウドアカウントも分けます。
Terraform のワークスペースは便利ですがセキュリティ境界ではありません。同一バックエンド内の論理的な状態分割に過ぎないため、強い隔離が必要な prod をワークスペース単独で守る設計は避けます。公式ドキュメントが推奨するのは、リモートバックエンドでの確実なロックと状態分離、アクセス制御の明確化です。
よくある誤りチェック(試験対策)
NG: ワークスペースだけで prod を保護
NG: backend ブロックで var を参照して環境を切替(バックエンドは変数展開不可)
OK: -backend-config オプションで環境固有の backend 設定を注入
OK: 各環境で別の認証主体(例: IAM Role、SPN)を使用実務と試験の両面で扱いやすいのは、共通モジュールを modules/ に集約し、環境ごとの差分は envs/dev|stg|prod のオーバーレイで吸収する構成です。状態はリモートバックエンドで環境ごとにキー(もしくはストレージ)を分け、ロックを有効化します。
CI/CD では環境ディレクトリごとに別ジョブを用意し、plan と apply を明確に分離。prod はレビュー必須、apply 権限も限定します。
参照アーキテクチャ(ディレクトリ分割 + リモート状態 + TFC 連携)
Git Repo
└─ modules/
├─ network/
└─ app/
└─ envs/
├─ dev/
│ ├─ main.tf -> module呼び出し(dev用変数)
│ └─ backend.hcl -> state: s3://tfstate-nonprod/dev/...
├─ stg/
│ ├─ main.tf
│ └─ backend.hcl -> state: s3://tfstate-nonprod/stg/...
└─ prod/
├─ main.tf
└─ backend.hcl -> state: s3://tfstate-prod/prod/...(別アカウント/バケット)
CI/CD
└─ Job: plan-dev -> envs/dev -> Remote Backend (lock有)
└─ Job: plan-stg -> envs/stg -> Remote Backend (lock有)
└─ Job: plan-prod -> envs/prod -> Remote Backend (lock有, 承認必須)
Cloud Accounts/Projects
└─ nonprod-account (dev, stg)
└─ prod-account (prod)
代表的なファイル例(部分)
# envs/prod/backend.hcl(S3 例。backend は変数展開不可、-backend-config で渡す)
bucket = "tfstate-prod"
key = "proj/prod/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "tf-locks-prod"
encrypt = true
# envs/prod/main.tf(共通モジュール呼び出し)
terraform {
required_version = ">= 1.4.0"
backend "s3" {}
}
provider "aws" {
region = var.region
# prod 用のロール/プロファイルを使用
}
module "network" {
source = "../../modules/network"
cidr_block = var.cidr
tags = {
env = "prod"
}
}
# 実行例(CIなど)
# terraform -chdir=envs/prod init -backend-config=backend.hcl -upgrade
# terraform -chdir=envs/prod plan -var-file=prod.tfvarsワークスペースは軽量で便利ですが、強い隔離や権限の分離には不十分です。試験観点でも「ワークスペースはセキュリティ境界ではない」を押さえてください。実務ではディレクトリ分割+共通モジュールがバランス良好。組織の境界が明確な場合はリポジトリ分割も選択肢です。
Terraform Cloud/Enterprise を使う場合、VCS 連携で環境ディレクトリごとにワークスペースを作成し、変数セットとポリシーセットで運用ガードを掛けるのが定石です。
| パターン | 隔離/権限制御 | 運用コスト | 適合ユースケース |
|---|---|---|---|
| ワークスペース単独で環境分割 | 弱い(同一バックエンドの論理分割。RBAC は限定的) | 低 | プレビュー、短命環境、学習 |
| ディレクトリ分割 + 共通モジュール + 環境別状態 | 中〜強(状態とクレデンシャル分離。CI でガード) | 中 | 大半のプロジェクトの標準解 |
| リポジトリ分割 + モジュールレジストリ | 強(VCS/RBAC/承認も分離しやすい) | 中〜高 | 組織/予算/監査が厳密な領域間の分割 |
| Terraform Cloud: 環境ごとに Workspace + 変数/ポリシーセット | 強(RBAC、ポリシー、State Sharing 制御) | 中 | ガバナンス重視、チーム横断運用 |
ワークスペースは補助的に使う(命名やタグ付けに反映)
locals {
env = terraform.workspace
name_prefix = "proj-${terraform.workspace}"
}
resource "aws_s3_bucket" "logs" {
bucket = "${local.name_prefix}-logs"
tags = { env = local.env }
}
# 注意:これだけでは prod の隔離は不十分。状態/認証/アカウントの分離が必要環境ごとの差分は var-file(-var-file)で与えます。terraform.tfvars や *.auto.tfvars は自動読込されますが、複数環境のファイルを同居させると意図しない適用につながるため、環境実行時は -var-file=envs/prod.tfvars のように明示指定するのが安全です。
バックエンド設定は変数参照できません。-backend-config=backend.hcl 等で環境別に注入します。シークレットは VCS に置かず、環境変数 TF_VAR_xxx、Terraform Cloud の Sensitive 変数、または外部シークレット管理(Vault など)を利用します。
スタック間連携は data.terraform_remote_state を使って出力値を参照しますが、環境を跨ぐ依存は最小化し、参照権限も最小化します。
環境別 tfvars と locals マップ例
# envs/stg/variables.tfvars(例)
region = "ap-northeast-1"
cidr = "10.20.0.0/16"
# modules/app/main.tf(環境差分は var と locals で吸収)
variable "region" {}
variable "cidr" {}
locals {
tags_common = { app = "proj" }
}
resource "aws_vpc" "this" {
cidr_block = var.cidr
tags = merge(local.tags_common, { env = terraform.workspace })
}
# 実行(stg)
# terraform -chdir=envs/stg init -backend-config=backend.hcl
# terraform -chdir=envs/stg plan -var-file=variables.tfvarsTerraform Cloud/Enterprise では、環境ディレクトリごとに Workspace を作成し、VCS 連携で該当パスのみをトリガします。Variable Sets をタグで関連付け、prod は承認フローと厳格な RBAC を設定します。Policy Sets(Sentinel など)でタグやリージョン制限、リソース保護を行うと堅牢です。
Workspace 間の state 参照は remote backend を使った data.terraform_remote_state で行えます。必要最小限の読み取り権限だけを付与し、prod の state を dev から不用意に読み取らせないことが重要です。
remote backend を用いた Workspace 間の state 参照(TFC)
data "terraform_remote_state" "network_prod" {
backend = "remote"
config = {
organization = "your-org"
workspaces = { name = "proj-network-prod" }
}
}
output "vpc_id" {
value = data.terraform_remote_state.network_prod.outputs.vpc_id
}
# 注意:参照先 Workspace の state は読み取り権限が必要同一環境内では、ネットワーク → セキュリティ → プラットフォーム → アプリの順に適用するのが一般的です。Terraform の depends_on は同一計画内でのみ有効で、Workspace/状態を跨ぐ順序制御は CI/CD でオーケストレーションします。
状態の移行が必要な場合、安易な直接編集は避け、import と state rm、あるいは state mv(適切なオプション)で計画的に行います。remote backend を利用している場合はツールの制約とベストプラクティスに従い、安全な手順で移行します。
CI の実行順序とロックの意識(例)
stages: [plan, approve, apply]
job plan-dev : terraform -chdir=envs/dev plan -var-file=dev.tfvars
job apply-dev : terraform -chdir=envs/dev apply -var-file=dev.tfvars (manual)
job plan-stg : terraform -chdir=envs/stg plan -var-file=stg.tfvars (needs: apply-dev)
job approve-stg: manual approval
job apply-stg : terraform -chdir=envs/stg apply -var-file=stg.tfvars
job plan-prod : terraform -chdir=envs/prod plan -var-file=prod.tfvars (needs: apply-stg)
job approve-prd: manual approval (RBAC: 限定)
job apply-prod : terraform -chdir=envs/prod apply -var-file=prod.tfvars
# リモートバックエンドのロックにより同一状態の並行 apply は防止される本番隔離にワークスペースを単独で使う、バックエンドで変数展開しようとする、複数環境の *.auto.tfvars を同居させる、といった誤りは実務でも試験でも減点対象です。変更はモジュールと var-file で吸収し、状態と権限の分離を徹底します。
コマンド・チートシート
# 初期化(環境別 backend)
terraform -chdir=envs/prod init -backend-config=backend.hcl -upgrade
# 計画/適用(環境別 var-file)
terraform -chdir=envs/stg plan -var-file=stg.tfvars
terraform -chdir=envs/stg apply -var-file=stg.tfvars
# ワークスペース(補助的に利用)
terraform workspace list
terraform workspace new dev
terraform workspace select prodPro
問題 1
次の要件をすべて満たすマルチ環境設計として最も適切なのはどれか。1) prod は dev/stg から強固に隔離、2) コードの重複は最小化、3) 計画と適用のレビューを分離、4) 将来の監査に備え RBAC を適用可能。
正解: A
強い隔離と監査性には、状態・認証の分離と CI でのガードが必須。A は共通モジュールで重複を避けつつ、バックエンド・アカウント・RBAC・レビューを組み合わせて要件を満たす。B はワークスペースのみで隔離しており不十分。C は *.auto.tfvars の使い方が危険で、状態共有も要件違反。D は同一バケットでも運用次第で許容される場合はあるが、prod の保護としては弱く、条件分岐で重要リソースをスキップする設計は推奨されない。
ワークスペースだけで dev/stg/prod を分離できますか?
推奨しません。ワークスペースは同一バックエンド内の論理分割であり、セキュリティ境界ではありません。prod は少なくとも状態と認証を分離し、可能ならクラウドアカウントも分けてください。
backend ブロックで変数を使って環境を切り替えられますか?
できません。バックエンドは計画前に初期化されるため変数展開の対象外です。-backend-config=... で環境別ファイルを渡すか、Terraform Cloud/Enterprise の Workspace を使い分けてください。
環境ごとの差分はどう管理するのが安全ですか?
共通モジュールを作り、環境ディレクトリから var-file(-var-file)で差分を注入します。命名やタグは terraform.workspace や locals マップで吸収し、シークレットは VCS に置かないでください。
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を用いた既存リソース参照の基本、選択基準、評価順序、...