VPCピアリングのTerraform実装は、AWSが「requester(接続要求側)」と「accepter(承認側)」の2段階ハンドシェイクで設計しているため、Terraform初学者がつまずきやすいポイントです。特に別アカウントをまたぐ構成では、provider aliasの設定と適用順序を正しく制御しないと、applyが途中で止まります。
この記事では、aws_vpc_peering_connectionとaws_vpc_peering_connection_accepterの役割分担、別アカウント構成でのprovider alias設計、ルートテーブル更新との依存関係を実装レベルで解説します。動作確認環境: Rocky Linux 9.4 / Terraform 1.9.5 / AWS プロバイダー v5.62.0。
この記事のポイント
・requesterはaws_vpc_peering_connection・accepterはaws_vpc_peering_connection_accepterが担当
・auto_acceptは同一アカウント内のみ有効・別アカウントでは必ずaccepterリソースを記述する
・別アカウント構成はprovider aliasを2つ用意し・requester apply後にaccepter applyの順序が必要
・ルートテーブル更新はaccepterリソースへのdepends_onを明示し・順序崩れを防ぐ
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
requesterとaccepterが分かれている理由——VPCピアリングの構造を理解する
VPCピアリングの接続確立には「接続要求」と「承認」の2段階が必要です。AWSコンソール上では「VPCピアリング接続の作成→承認」というUIのステップとして見えますが、Terraformではこの2段階が2つの独立したリソースとして実装されています。・aws_vpc_peering_connection:接続を要求するrequester側のリソース
・aws_vpc_peering_connection_accepter:接続を承認するaccepter側のリソース
同一アカウント内のピアリングであれば
auto_accept = trueを指定することで、aws_vpc_peering_connectionリソース単体で要求と承認を同時に完了できます。一方、別アカウントの場合はAWS APIレベルの制約として自動承認が機能しないため、accepterリソースを明示的に定義する必要があります。この設計の違いを把握していないと、同一アカウントで動いたコードをそのまま別アカウントに適用して失敗するという事故が起きます。同一アカウント内のVPCピアリング実装
1. aws_vpc_peering_connectionの基本定義
同一アカウント内で2つのVPCをピアリングする最もシンプルな構成です。# main.tf — 同一アカウント内VPCピアリング resource "aws_vpc" "requester" { cidr_block = "10.0.0.0/16" tags = { Name = "requester-vpc" } } resource "aws_vpc" "accepter" { cidr_block = "10.1.0.0/16" tags = { Name = "accepter-vpc" } } resource "aws_vpc_peering_connection" "main" { vpc_id = aws_vpc.requester.id peer_vpc_id = aws_vpc.accepter.id auto_accept = true tags = { Name = "requester-to-accepter-peering" } }
2. auto_acceptの制約と動作
auto_accept = trueを設定できるのは「requester側とaccepter側が同じAWSアカウントかつ同じリージョン」に限られます。この属性はaws_vpc_peering_connectionリソースのプロパティとして定義されていますが、内部的には接続ステータスが「pending-acceptance」になった直後に、TerraformがアカウントAのAPIを使って承認しています。別アカウントを指定した場合(
peer_owner_idに別アカウントIDを指定)、auto_accept = trueは無視されます。AWSの権限モデル上、requester側が別アカウントのVPCを自動承認することはできないからです。この場合、Terraformのplanでは問題なく通過しますが、applyが完了してもピアリングステータスは「pending-acceptance」のままになります。3. ルートテーブル更新とピアリングIDの依存関係
ピアリング接続が確立しても、対向VPCへのルートがルートテーブルに存在しなければ通信はできません。aws_routeリソースで対向VPCのCIDRブロックへのルートを追加します。# requester側ルートテーブルにaccepter VPCへのルートを追加 resource "aws_route" "requester_to_accepter" { route_table_id = aws_route_table.requester.id destination_cidr_block = "10.1.0.0/16" vpc_peering_connection_id = aws_vpc_peering_connection.main.id } # accepter側ルートテーブルにrequester VPCへのルートを追加 resource "aws_route" "accepter_to_requester" { route_table_id = aws_route_table.accepter.id destination_cidr_block = "10.0.0.0/16" vpc_peering_connection_id = aws_vpc_peering_connection.main.id }
vpc_peering_connection_id = aws_vpc_peering_connection.main.idという参照を書くことで、Terraformはピアリングリソースが作成完了した後にルートを追加する暗黙の依存関係を認識します。同一アカウント構成ではこれで十分ですが、後述の別アカウント構成ではaccepterリソースが完了した後でないとルートが機能しないため、depends_onによる明示指定が必要になります。別アカウントVPCピアリングの設計——provider aliasとapply順序の制御
1. provider aliasで2アカウントのAWS認証情報を定義する
別アカウント間のピアリングを1つのTerraformコードで管理するには、Account AとAccount B用にproviderを2つ定義します。assume_roleブロックを使って各アカウントのIAMロールをスイッチします。# providers.tf provider "aws" { alias = "account_a" region = "ap-northeast-1" assume_role { role_arn = "arn:aws:iam::111111111111:role/TerraformRole" } } provider "aws" { alias = "account_b" region = "ap-northeast-1" assume_role { role_arn = "arn:aws:iam::222222222222:role/TerraformRole" } }
TerraformRoleは、Terraformを実行するIAMユーザーまたはIAMロールからsts:AssumeRoleが許可された状態にしておく必要があります。スイッチロールの信頼ポリシー設定はIAMコンソールまたはTerraformの別モジュールで事前に設定しておきます。2. requester(Account A)側のリソース定義
Account A側のVPCからAccount B側のVPCへ接続要求を送るリソースです。provider属性でAccount AのproviderエイリアスをVPCとピアリングリソースに紐付けます。# peering_requester.tf — Account A 側 resource "aws_vpc" "account_a" { provider = aws.account_a cidr_block = "10.0.0.0/16" tags = { Name = "account-a-vpc" } } data "aws_caller_identity" "account_b" { provider = aws.account_b } resource "aws_vpc_peering_connection" "main" { provider = aws.account_a vpc_id = aws_vpc.account_a.id peer_vpc_id = aws_vpc.account_b.id peer_owner_id = data.aws_caller_identity.account_b.account_id # auto_accept は別アカウントでは機能しない。accepterリソース側で指定する tags = { Name = "account-a-to-b-peering" } }
3. accepter(Account B)側のリソース定義
Account B側でピアリング要求を承認するリソースです。vpc_peering_connection_idにrequesterリソースのIDを参照させることで、TerraformがrequesterリソースID確定後に実行することを保証します。# peering_accepter.tf — Account B 側 resource "aws_vpc" "account_b" { provider = aws.account_b cidr_block = "10.1.0.0/16" tags = { Name = "account-b-vpc" } } resource "aws_vpc_peering_connection_accepter" "main" { provider = aws.account_b vpc_peering_connection_id = aws_vpc_peering_connection.main.id auto_accept = true tags = { Name = "account-b-accepts-peering" } }
auto_accept = trueを指定するのはaws_vpc_peering_connection_accepterリソース側です。これはAccount B側のproviderが自身のVPCへの接続要求を承認する操作であり、別アカウントからの自動承認とはまったく別の概念です。4. apply順序と2段階適用の設計判断
別アカウント構成で1回のterraform applyを実行すると、Terraformは並列実行を試みてルートテーブル更新がaccepter確立より先に走り、「InvalidVpcPeeringConnectionID.NotFound」エラーになることがあります。安全に確認するための2段階applyは次の手順です。# ステップ1: requester側とaccepterリソースのみ先にapply $ terraform apply -target=aws_vpc_peering_connection.main -target=aws_vpc_peering_connection_accepter.main # ステップ2: ルートテーブルを含む残りのリソースをapply $ terraform apply
-targetを常用することはTerraformが推奨しない運用です(stateと実態が食い違うリスクがある)。後述のdepends_onを適切に設定した上で、1回のapplyで完了できる設計を目指すことを優先してください。depends_onで依存関係を明示するケース
Terraformは通常、リソース間の属性参照から依存関係を自動推論します。しかし、accepterリソースが完了した後にのみルートテーブルが更新されるべき場合、IDの参照だけでは不十分なことがあります。こういうケースにdepends_onを使います。# ルートテーブルはaccepterが完了した後に追加する resource "aws_route" "account_a_to_b" { provider = aws.account_a route_table_id = aws_route_table.account_a.id destination_cidr_block = "10.1.0.0/16" vpc_peering_connection_id = aws_vpc_peering_connection.main.id depends_on = [aws_vpc_peering_connection_accepter.main] } resource "aws_route" "account_b_to_a" { provider = aws.account_b route_table_id = aws_route_table.account_b.id destination_cidr_block = "10.0.0.0/16" vpc_peering_connection_id = aws_vpc_peering_connection.main.id depends_on = [aws_vpc_peering_connection_accepter.main] }
depends_on = [aws_vpc_peering_connection_accepter.main]を明示することで、Terraformはaccepterリソースが「active」ステータスになった後にルートを追加します。この一行があるかないかで、別アカウント構成での1回applyの成否が変わります。よくあるエラーと対処法
「InvalidVpcPeeringConnectionID.NotFound」エラーaccepterリソースよりも先にルートテーブル更新が走ったときに発生します。aws_routeリソースに
depends_on = [aws_vpc_peering_connection_accepter.main]を追加してください。まずは-targetで2段階applyで動作を確認してから、depends_onを足して1回applyに変換する手順が安全です。「Error: creating VPC Peering Connection: VpcPeeringConnectionAlreadyExists」エラー
同じVPCペア(vpc_idとpeer_vpc_idの組み合わせ)でピアリングが既に存在するときに発生します。AWSコンソールで既存のピアリング接続IDを確認し、
terraform import aws_vpc_peering_connection.main pcx-0xxxxxxxxxxでstateに取り込んでください。別アカウントapply後もステータスが「pending-acceptance」のまま
aws_vpc_peering_connection_accepterリソースが適用されていないか、accepter側のproviderの認証情報が正しくない状態です。
terraform state listでaccepterリソースがstateに登録されているか確認し、aws_vpc_peering_connection_accepter.mainが存在しない場合はterraform apply -target=aws_vpc_peering_connection_accepter.mainで個別適用してください。「Error: assuming role」でprovider初期化失敗
assume_roleのrole_arnが間違っているか、実行元のIAMエンティティにsts:AssumeRole権限がない場合です。Account BのIAMロール信頼ポリシーに、TerraformをCI/CDで実行するアカウントのIAMエンティティが許可されているか確認してください。
本記事のまとめ
| 設計パターン | 使用リソース | ポイント |
|---|---|---|
| 同一アカウント内ピアリング | aws_vpc_peering_connection | auto_accept = true で1リソースで完結 |
| 別アカウントprovider定義 | provider alias × 2 | assume_roleで各アカウントのIAMロールをスイッチ |
| 別アカウントrequester側 | aws_vpc_peering_connection | peer_owner_idに対象アカウントIDを指定・auto_acceptは不要 |
| 別アカウントaccepter側 | aws_vpc_peering_connection_accepter | accepter側providerを指定・auto_accept = trueで承認 |
| ルートテーブル更新の順序制御 | aws_route + depends_on | accepterリソース完了後にルートを追加するよう明示 |
| 安全なapply順序 | -targetで2段階・またはdepends_on設定 | accepter完了後にルート更新・-target常用は非推奨 |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Terraformのbackendブロックはinit時にしか評価されない|s3・gcs・httpで必須キーと認証情報の渡し方が変わる仕組み
- この記事の属するカテゴリ:Terraformへ戻る

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