TerraformでVPCピアリングをコード化する設計|requesterとaccepterの分離とルートテーブル更新の依存関係

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > TerraformでVPCピアリングをコード化する設計|requesterとaccepterの分離とルートテーブル更新の依存関係
「TerraformでVPCピアリングを設定したが、別アカウントのaccepterが確立する前にルートテーブルを更新しようとしてエラーになった」「auto_accept = trueにしたのに別アカウントへの接続が確立しない」——このような問い合わせはセミナーで毎回のように受けます。

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を明示し・順序崩れを防ぐ


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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常用は非推奨
VPCピアリングのTerraform実装は「requesterとaccepterを別リソースに分けて記述し、ルートテーブル更新をaccepterに依存させる」という構造が核心です。同一アカウントでもauto_acceptの動作を正確に理解した上で設計し、別アカウント構成では2つのprovider aliasとdepends_onを組み合わせることで、1回のapplyで確実に接続を確立できます。
現場で使えるTerraformの設計パターンを体系的に身につけたい方へ、VPCピアリングからマルチアカウント設計・CI/CD連携まで、ハンズオン形式で習得できるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。