Terraformの構成が大きくなると、すべてのリソースを1つのtfstateに押し込むのが怖くなります。ネットワーク・コンピュート・アプリケーションの3層に分離してみたものの、「VPC IDをどうやって別プロジェクトに渡すんだ?」と詰まる方は多いです。
この記事では、terraform_remote_state data sourceを使って、分離されたtfstate間でデータを安全に受け渡す設計パターンを解説します。S3バックエンドを前提に、実際のHCLコードとterraform planの実行例を交えて説明します。
この記事のポイント
・terraform_remote_stateで別tfstateのoutputを参照できる
・backendブロックのS3設定で参照先のstateを指定する
・outputにsensitive=trueで機密情報を安全に渡せる
・設計の鉄則は「一方向参照のみ」で循環参照は禁止
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜtfstateを分離し、モジュール間でデータを受け渡す必要があるのか
Terraformを使い始めると、最初は1つのディレクトリにすべてのリソース定義を並べることが多いです。VPC、EC2、RDS、S3など、プロジェクト全体を1つのtfstateで管理する構成です。しかしリソース数が増えてくると、この構成は次の問題を引き起こします。
・1回のterraform applyで全リソースがロック対象になる:RDSのパラメータグループを変更したいだけなのに、VPCやEC2にも変更の影響リスクが及ぶ
・terraform planが遅くなる:全リソースのdiffを計算するため、100リソースを超えると数分かかることもある
・チーム開発でコンフリクトが起きやすい:複数のエンジニアが同時にapplyしようとするとstateロックで詰まる
これを解決するのが「レイヤーごとのtfstate分離」です。VPCやサブネットを管理するネットワーク層、EC2を管理するコンピュート層、アプリケーション設定を管理するアプリ層に分割します。
問題は、分離した層の間でどうデータを渡すかです。コンピュート層でEC2を作るには、ネットワーク層が作ったVPC IDやサブネットIDが必要です。この「横断的なデータ受け渡し」を解決するのが
terraform_remote_stateです。terraform_remote_state data sourceの仕組みと選択基準
terraform_remote_stateは、別のTerraform設定のtfstateファイルを読み込み、そこで定義されたoutputを参照できる組み込みdata sourceです。通常のdata source(AWSのリソースを取得するもの)とは違い、providerの設定が不要で、Terraform自体の機能として動作します。1. 参照できる情報の範囲
terraform_remote_stateで取得できるのは、対象のtfstateにおいてoutputブロックで公開された値のみです。tfstate内にあるリソースの全属性をそのまま取り出すことはできません。これは意図的な設計です。「outputとして公開する」という一手間を設けることで、層間のインターフェースを明確にし、依存関係を意図的にコントロールできます。
2. terraform_remote_state vs variable(tfvars)の使い分け
「VPC IDをtfvarsに直接書けばいいのでは?」という疑問が出ることがあります。確かに動きますが、2つの問題があります。・VPC IDが変わるたびに手動更新が必要:ネットワーク層を作り直すとVPC IDが変わるが、コンピュート層のtfvarsを手動で修正しなければならない
・秘匿情報が平文でファイルに残る:RDSのエンドポイントをtfvarsに書くとリポジトリに残るリスクがある
terraform_remote_stateなら、ネットワーク層のtfstateから最新のVPC IDをapply時に自動取得します。ネットワーク層を作り直しても手動更新は不要です。
>> Terraform実践セミナーの詳細はこちら
実践:ネットワーク層のoutputをコンピュート層で参照する
実際のHCLコードで手順を確認しましょう。次のディレクトリ構成を前提にします。# ディレクトリ構成 project/ ├── network/ # ネットワーク層(VPC・サブネット) │ ├── main.tf │ ├── outputs.tf │ └── backend.tf └── compute/ # コンピュート層(EC2) ├── main.tf ├── remote_network.tf # remote_state参照 └── backend.tf
1. ネットワーク層にoutputを定義する
コンピュート層から参照したい値をoutputs.tfにoutputブロックで公開します。# network/outputs.tf output "vpc_id" { description = "作成したVPCのID" value = aws_vpc.main.id } output "private_subnet_ids" { description = "プライベートサブネットのIDリスト" value = aws_subnet.private[*].id } output "public_subnet_ids" { description = "パブリックサブネットのIDリスト" value = aws_subnet.public[*].id }
descriptionを必ず書くことです。参照側が何を取得しているか明確にする意味で、チーム開発では特に重要です。2. コンピュート層でterraform_remote_stateを定義する
data "terraform_remote_state"ブロックで参照先のバックエンド設定を指定します。# compute/remote_network.tf data "terraform_remote_state" "network" { backend = "s3" config = { bucket = "my-terraform-state-bucket" key = "network/terraform.tfstate" region = "ap-northeast-1" } }
keyには、ネットワーク層のtfstateが保存されているS3のパスを指定します。ネットワーク層のbackend.tfで設定したkeyと完全に一致させる必要があります。3. 取得したoutputをリソース定義で使う
data.terraform_remote_state.network.outputs.変数名の形で値を参照します。# compute/main.tf resource "aws_instance" "app" { ami = "ami-0c1de55ac2bffeae7" # RHEL 9 instance_type = "t3.micro" # ネットワーク層のoutputからサブネットIDを取得 subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0] vpc_security_group_ids = [aws_security_group.app.id] tags = { Name = "app-server" } } resource "aws_security_group" "app" { name = "app-sg" # ネットワーク層のoutputからVPC IDを取得 vpc_id = data.terraform_remote_state.network.outputs.vpc_id ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["10.0.0.0/8"] } }
terraform planの実行例と出力確認
コンピュート層のディレクトリでterraform planを実行すると、remote_stateからの値取得が確認できます。$ cd project/compute $ terraform init $ terraform plan data.terraform_remote_state.network: Reading... data.terraform_remote_state.network: Read complete after 1s Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_instance.app will be created + resource "aws_instance" "app" { + ami = "ami-0c1de55ac2bffeae7" + instance_type = "t3.micro" + subnet_id = "subnet-0a1b2c3d4e5f67890" ... } # aws_security_group.app will be created + resource "aws_security_group" "app" { + name = "app-sg" + vpc_id = "vpc-0123456789abcdef0" ... } Plan: 2 to add, 0 to change, 0 to destroy.
data.terraform_remote_state.network: Read complete after 1sの行が表示されれば、ネットワーク層のtfstateから正常にデータを取得できています。subnet-0a1b2c3d4e5f67890やvpc-0123456789abcdef0の部分には、ネットワーク層が実際に作成したリソースIDが自動的に入ります。設計上の注意点
1. outputのsensitiveフラグとマスク処理
RDSのエンドポイントや接続文字列など秘匿情報を渡すときは、sensitive = trueを必ず付けます。# network/outputs.tf(機密情報の例) output "db_endpoint" { description = "RDSエンドポイント" value = aws_db_instance.main.endpoint sensitive = true }
(sensitive value)と表示され、ログに平文が残りません。ただし、tfstateファイル自体には平文で書き込まれるため、S3バケットのアクセス制御と暗号化は引き続き必要です。2. 一方向参照の原則(循環参照を作らない)
terraform_remote_stateを使う際の最重要ルールは「一方向のみ」です。・NG:ネットワーク層がコンピュート層のstateを参照し、コンピュート層もネットワーク層のstateを参照(循環)
・OK:ネットワーク層 → コンピュート層 → アプリ層(上位から下位への一方向のみ)
循環参照があると、どちらの層を先にapplyすべきか判断できなくなり、鶏と卵の問題に陥ります。「基盤層は自分より上位の層を参照してはいけない」と運用規約として明文化しておきましょう。
3. 削除順序を必ず守る
ネットワーク層を削除する前に、コンピュート層のリソース(EC2・セキュリティグループ等)を先にdestroyしないとDependencyViolationエラーになります。正しい手順:
・Step 1:コンピュート層で
terraform destroy・Step 2:ネットワーク層で
terraform destroytfstateが分離されているため、Terraform側では層間の依存関係を自動検出できません。apply/destroyの順序は必ず運用手順書に記載しておきましょう。
トラブルシュート:よくあるエラーと対処法
1. Error: retrieving state: NoSuchKey
参照先のS3パスにtfstateが存在しない場合に発生するエラーです。Error: retrieving state: NoSuchKey: The specified key does not exist. status code: 404, request id: xxxxxxxxxxxx with data.terraform_remote_state.network, on remote_network.tf line 1, in data "terraform_remote_state" "network":
・ネットワーク層がまだapplyされていない:先に
cd ../network && terraform applyを完了させる・keyのパスが間違っている:参照側の
config.keyとネットワーク層のbackend.tfのkeyが一致しているか確認する・bucketが違う:両層で同じS3バケット名を指定しているか確認する
2. Error: Unsupported attribute(output not found)
参照しようとしたoutput名が対象のtfstateに存在しない場合に出るエラーです。Error: Unsupported attribute on main.tf line 9, in resource "aws_instance" "app": 9: subnet_id = data.terraform_remote_state.network.outputs.private_subnets[0] This object does not have an attribute named "private_subnets". Did you mean "private_subnet_ids"?
outputs.tfで定義したoutput名と、参照側の変数名が完全一致しているか確認します。なお、参照先のoutput名を変更した際は、コンピュート層も合わせて修正する必要があります。output名はチーム間のインターフェース契約として扱い、安易に変更しないよう規約を設けましょう。
次に読む記事
・Terraformのtfstate管理とS3バックエンド設定|チーム運用で壊さないための基礎・Terraformのstate操作コマンド実践ガイド|state list・state mv・state rmで既存インフラを安全に管理する方法
・TerraformのHCL変数設計|variable・locals・output・data sourceで構成を整理する方法
本記事のまとめ
| やりたいこと | HCL・コマンド |
|---|---|
| 別tfstateのoutputを参照する | data "terraform_remote_state" "名前" { backend = "s3"; config = {...} } |
| outputの値を使う | data.terraform_remote_state.名前.outputs.output名 |
| 機密情報をoutputで渡す | output "xxx" { value = ...; sensitive = true } |
| 参照先stateのS3パスを特定する | ネットワーク層のbackend.tfのkeyの値を確認 |
| remote_state参照エラーを調べる | terraform planのエラー文で「NoSuchKey」か「Unsupported attribute」かを確認 |
| リソースを安全に削除する | コンピュート層destroy→ ネットワーク層destroy(依存方向の逆順) |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます