[アップデート] AWSサービスのAWS PrivateLink(インターフェース型)がクロスリージョン接続をサポートするようになりました
はじめに
皆様こんにちは、あかいけです。
AWSでは様々なアップデートがありワクワクな日々をお過ごしのことかと思いますが、
2025年11月19日に、AWSサービス向けのAWS PrivateLinkがクロスリージョン接続をサポートするようになりました。
実は、約1年前のアップデート(2024年11月26日)で、AWS PrivateLinkのクロスリージョン接続機能自体はすでにリリースされていました。
ただ、そのときは VPCエンドポイントサービス(自社サービスやサードパーティサービス)向け のみの対応でした。
そして今回のアップデートにより、
AWSが提供するサービス(S3、ECR、Lambdaなど)向けのインターフェイス型VPCエンドポイント でもクロスリージョン接続が可能になったというわけです。
2つのクロスリージョン接続の違い
混乱しやすいポイントなので、2つのアップデートの違いを整理します。
| 項目 | 2024年11月26日のアップデート | 2025年11月19日のアップデート(今回) |
|---|---|---|
| 対象 | VPCエンドポイントサービス | AWSサービス向けVPCエンドポイント(インターフェース型) |
| 具体例 | 自社で構築したNLB経由のサービス サードパーティサービス |
Amazon S3、ECR、Lambda、KMS等の AWS公式サービス |
| サービス提供者 | 自分または他のAWSアカウント | AWS |
| 用途 | 社内システムのクロスリージョン接続 パートナー連携 |
AWSマネージドサービスへの クロスリージョン接続 |
簡単に言うと、2024年のアップデートは「 自分で作ったサービス をクロスリージョンで使えるようにする機能」で、
今回のアップデートは「 AWSが提供しているサービス をクロスリージョンで使えるようにする機能」です。
多くのユーザーにとっては、今回のアップデートの方が直接的な恩恵を受けやすいのではないかと思います。
というわけで今回は、AWSサービス向けインターフェイス型VPCエンドポイントのクロスリージョン接続について、その概要、具体的なメリット、制限事項、そして実際にTerraformで検証する方法をまとめます。
※ ゲートウェイ型VPCエンドポイントは引き続き未対応です
対応サービス
なお、すべてのAWS PrivateLink対応サービスがクロスリージョン接続に対応したわけではありません。
対応サービスの最新情報は以下の公式ドキュメントで確認できます。
また、以下のAWS CLIコマンドで、特定のリージョンからアクセス可能なクロスリージョン対応サービスを確認することもできます。
aws ec2 describe-vpc-endpoint-services \
--filters Name=service-type,Values=Interface Name=owner,Values=amazon \
--region ap-northeast-1 \
--service-region us-east-1 \
--query ServiceNames
[
"com.amazonaws.iam",
"com.amazonaws.route53",
"com.amazonaws.us-east-1.ecr.api",
"com.amazonaws.us-east-1.ecr.dkr",
"com.amazonaws.us-east-1.ecs",
"com.amazonaws.us-east-1.ecs-fips",
"com.amazonaws.us-east-1.kinesis-firehose",
"com.amazonaws.us-east-1.kinesisanalytics",
"com.amazonaws.us-east-1.kinesisanalytics-fips",
"com.amazonaws.us-east-1.kms",
"com.amazonaws.us-east-1.kms-fips",
"com.amazonaws.us-east-1.lambda",
"com.amazonaws.us-east-1.s3"
]
具体的なメリット
AWSサービスへクロスリージョンで接続する場合に、どのようなメリットがあるか思いついたものを記載します。
1. セキュリティの向上
クロスリージョンでのデータ転送が必要な場合でも、インターネットを経由せずにAWSのプライベートネットワーク内で通信が完結します。
これにより、データ漏洩やセキュリティリスクを最小限に抑えることができます。
2. アーキテクチャの簡素化
従来、クロスリージョンでAWSサービスへのプライベート接続を実現するには、以下のような構成が必要でした。
- VPCピアリング
- Transit Gateway
- VPN接続の構成
今回のアップデートにより、
VPCエンドポイントを作成するだけでクロスリージョン接続が可能になり、ネットワーク構成が大幅にシンプルになります。
3. リージョンで提供されていないサービスの利用
特定のリージョンでまだ提供されていないAWSサービスや機能を、別のリージョンから利用できるようになりそうです。
これにより、各リージョンでのサービス提供状況に左右されずに、柔軟なアーキテクチャ設計が可能になります。
ただし現状で対応しているサービスはごく一部のため、今後の対応サービスの増加に伴ってメリットを感じられそうです。
4. データレジデンシー要件への対応
グローバルに分散したシステムを構築する際、データの所在地に関する法規制やコンプライアンス要件に対応する必要があります。
クロスリージョンAWS PrivateLinkを使用することで、データを特定のリージョンに保持しながら、他のリージョンから安全にアクセスできます。
制限事項と注意点
クロスリージョンAWS PrivateLinkを使用する際には、いくつかの制限事項と注意すべき点があります。
この辺りの制約事項についても以下ドキュメントに記載があります。
IAM権限の設定が必要
デフォルトでは、IAMエンティティには別のリージョンのAWSサービスへアクセスする権限がありません。
クロスリージョン接続を許可するには、vpce:AllowMultiRegionという権限専用アクション(permission-only action)を含むIAMポリシーを作成する必要があります。
また、Service Control Policy (SCP)でもこのアクションが拒否されていないことを確認する必要があります。
クロスリージョン接続機能を使用するには、IDポリシーとSCPの両方でこのアクションを許可する必要があります。
なお、VPCエンドポイント作成時にサービスリージョンとして指定できるリージョンを制御したい場合は、ec2:VpceServiceRegion条件キーを使用できます。
リージョンDNSの使用が必須
クロスリージョンでAWSサービスにアクセスする際は、リージョナルDNSを使用する必要があります。ゾーンDNS(Availability Zone固有のDNS)はサポートされていません。
一部のアベイラビリティゾーンは非対応
以下のアベイラビリティゾーンではクロスリージョン接続がサポートされていません。
use1-az3(US East 1)usw1-az2(US West 1)apne1-az3(Asia Pacific Tokyo)apne2-az2(Asia Pacific Seoul)apne2-az4(Asia Pacific Seoul)
高可用性の考慮
AWS PrivateLinkは、サービスリージョンとコンシューマーリージョンの両方でアベイラビリティゾーン間のフェイルオーバーを管理します。
ただし、リージョン間のフェイルオーバーは管理しません。
高可用性を確保するには、少なくとも2つのアベイラビリティゾーンにクロスリージョン対応のInterfaceエンドポイントを作成することが推奨されます。
なお、プロバイダー(サービス側)とコンシューマー(接続側)で同じアベイラビリティゾーンを使用する必要はありません。
検証してみる
では実際にクロスリージョンでAWS PrivateLink(インターフェース型)を使用する構成をTerraformで実装してみます。
この例では、ap-northeast-1(東京)リージョンのVPCから、us-east-1(バージニア北部)リージョンのAmazon S3へプライベートに接続する構成を作成します。
マネジメントコンソールからの見え方
まずはマネジメントコンソールからの見え方についてです。
VPCエンドポイント作成画面にて以下のように「サービスリージョン」という項目が増えており、

ここで「クロスリージョンエンドポイントを有効にする」をクリックしてリージョンを選択すると、利用できるサービスが表示されます。

また作成したVPCエンドポイントのサービス名はサービスリージョンのものとなります。

Terraformコード
以下は、クロスリージョンPrivateLink接続を検証するためのTerraformコードです。
プライベートサブネットのEC2からPrivateLinkでクロスリージョン接続が利用できる確認します。
# ==========================================
# Terraform設定
# ==========================================
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
# ==========================================
# 変数定義
# ==========================================
variable "vpc_cidr" {
description = "VPCのCIDRブロック"
type = string
default = "10.0.0.0/16"
}
variable "project_name" {
description = "プロジェクト名(リソースのタグに使用)"
type = string
default = "privatelink-cross-region-test"
}
locals {
# プライベートサブネット定義
private_subnets = {
private_1 = {
cidr_index = 1
az_index = 0
}
private_2 = {
cidr_index = 2
az_index = 1
}
}
# VPCエンドポイント定義
vpc_endpoints = {
ssm = "com.amazonaws.ap-northeast-1.ssm"
ssmmessages = "com.amazonaws.ap-northeast-1.ssmmessages"
}
# セキュリティグループ定義
security_groups = {
vpc_endpoint = {
description = "Security group for VPC endpoints"
ingress = [{
description = "HTTPS from VPC"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
}]
}
ec2 = {
description = "Security group for test EC2 instance"
ingress = []
}
}
}
# ==========================================
# データソース
# ==========================================
data "aws_ami" "amazon_linux_2023" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["al2023-ami-2023.*-x86_64"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
data "aws_availability_zones" "available" {
state = "available"
}
# ==========================================
# VPC
# ==========================================
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
Project = var.project_name
}
}
# ==========================================
# プライベートサブネット
# ==========================================
resource "aws_subnet" "private" {
for_each = local.private_subnets
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, each.value.cidr_index)
availability_zone = data.aws_availability_zones.available.names[each.value.az_index]
tags = {
Name = "${var.project_name}-${each.key}-subnet"
Project = var.project_name
}
}
# プライベートサブネット用のルートテーブル
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-private-rt"
Project = var.project_name
}
}
# プライベートサブネットとルートテーブルの関連付け
resource "aws_route_table_association" "private" {
for_each = local.private_subnets
subnet_id = aws_subnet.private[each.key].id
route_table_id = aws_route_table.private.id
}
# ==========================================
# セキュリティグループ
# ==========================================
resource "aws_security_group" "main" {
for_each = local.security_groups
name = "${var.project_name}-${each.key}-sg"
description = each.value.description
vpc_id = aws_vpc.main.id
# すべてのアウトバウンド通信を許可
egress {
description = "Allow all outbound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-${each.key}-sg"
Project = var.project_name
}
}
# セキュリティグループのIngressルール
resource "aws_security_group_rule" "ingress" {
for_each = {
for item in flatten([
for sg_key, sg in local.security_groups : [
for idx, rule in sg.ingress : {
key = "${sg_key}-${idx}"
sg_key = sg_key
description = rule.description
from_port = rule.from_port
to_port = rule.to_port
protocol = rule.protocol
cidr_blocks = rule.cidr_blocks
}
]
]) : item.key => item
}
type = "ingress"
security_group_id = aws_security_group.main[each.value.sg_key].id
description = each.value.description
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_blocks
}
# ==========================================
# クロスリージョンVPCエンドポイント
# ==========================================
resource "aws_vpc_endpoint" "s3_cross_region" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.us-east-1.s3"
vpc_endpoint_type = "Interface"
service_region = "us-east-1"
subnet_ids = values(aws_subnet.private)[*].id
security_group_ids = [aws_security_group.main["vpc_endpoint"].id]
private_dns_enabled = true
timeouts {
create = "20m"
}
tags = {
Name = "${var.project_name}-s3-cross-region-endpoint"
Project = var.project_name
}
}
# ==========================================
# SSM Session Manager用のVPCエンドポイント
# ==========================================
resource "aws_vpc_endpoint" "interface" {
for_each = local.vpc_endpoints
vpc_id = aws_vpc.main.id
service_name = each.value
vpc_endpoint_type = "Interface"
subnet_ids = values(aws_subnet.private)[*].id
security_group_ids = [aws_security_group.main["vpc_endpoint"].id]
private_dns_enabled = true
tags = {
Name = "${var.project_name}-${each.key}-endpoint"
Project = var.project_name
}
}
# ==========================================
# 検証用EC2インスタンスのIAM設定
# ==========================================
resource "aws_iam_role" "ec2_role" {
name = "ec2-privatelink-test-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}
resource "aws_iam_role_policy" "cross_region_policy" {
name = "cross-region-access-policy"
role = aws_iam_role.ec2_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "vpce:AllowMultiRegion"
Resource = "*"
},
{
Effect = "Allow"
Action = [
"s3:*"
]
Resource = "*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "ssm_policy" {
role = aws_iam_role.ec2_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "ec2_profile" {
name = "ec2-privatelink-test-profile"
role = aws_iam_role.ec2_role.name
}
# ==========================================
# 検証用EC2インスタンス
# ==========================================
resource "aws_instance" "test_instance" {
ami = data.aws_ami.amazon_linux_2023.id
instance_type = "t3.micro"
subnet_id = aws_subnet.private["private_1"].id
iam_instance_profile = aws_iam_instance_profile.ec2_profile.name
vpc_security_group_ids = [aws_security_group.main["ec2"].id]
associate_public_ip_address = false
tags = {
Name = "${var.project_name}-test-instance"
Project = var.project_name
}
}
# ==========================================
# 出力
# ==========================================
output "s3_cross_region_endpoint_id" {
description = "クロスリージョンS3 VPCエンドポイントのID"
value = aws_vpc_endpoint.s3_cross_region.id
}
output "s3_cross_region_endpoint_dns" {
description = "S3 VPCエンドポイントのDNSエントリ"
value = aws_vpc_endpoint.s3_cross_region.dns_entry
}
output "test_instance_id" {
description = "検証用EC2インスタンスのID"
value = aws_instance.test_instance.id
}
なおクロスリージョン接続のVPCエンドポイントは作成まで結構時間がかかるようで、私の環境では10数分程度かかりました。
デフォルトだとTerraformの10分のタイムアウトに引っかかったので、Terraformで実行する場合はタイムアウト時間を伸ばしてあげたほうが良さそうです。
╷
│ Error: waiting for EC2 VPC Endpoint (com.amazonaws.us-east-1.s3) create: timeout while waiting for state to become 'available, pendingacceptance' (last state: 'pending', timeout: 10m0s)
│
│ with aws_vpc_endpoint.s3_cross_region,
│ on main.tf line 197, in resource "aws_vpc_endpoint" "s3_cross_region":
│ 197: resource "aws_vpc_endpoint" "s3_cross_region" {
│
╵
╷
│ Error: Missing Resource Identity After Create: The Terraform provider unexpectedly returned no resource identity after having no errors in the resource create. This is always a problem with the provider and should be reported to the provider developer
│
│ with aws_vpc_endpoint.s3_cross_region,
│ on main.tf line 197, in resource "aws_vpc_endpoint" "s3_cross_region":
│ 197: resource "aws_vpc_endpoint" "s3_cross_region" {
│
╵
アクセス確認
Terraformで環境を構築した後、AWS Systems Manager Session Managerでプライベートサブネット内のEC2インスタンスに接続します。
# AWS CLIでSession Managerを使用して接続
aws ssm start-session --target i-xxxxxxxxx --region ap-northeast-1
EC2インスタンスに接続できたら、インターネット接続がないことを確認します。
コマンドが失敗するので、プライベート環境であることが確認できます。
# インターネット接続の確認(失敗するはず)
ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2113ms
AWS CLIでus-east-1リージョンを指定すると、VPCエンドポイント経由でus-east-1リージョンのS3にアクセスできることが確認できます。
# S3バケットの一覧を取得(us-east-1リージョン)
$ aws s3 ls --region us-east-1
2025-11-05 09:52:13 xxx-s3
2025-02-12 04:42:27 yyy-s3
2025-02-12 04:42:19 zzz-s3
そして ap-northeast-1 を指定した場合は応答が返ってきません。
$ aws s3 ls --region ap-northeast-1
デバックオプションで見てみると、
ap-northeast-1のエンドポイントを参照しているため、アクセスができていないようです。
2025-11-20 11:53:05,982 - MainThread - botocore.httpsession - DEBUG - Certificate path: /etc/pki/tls/certs/ca-bundle.crt
2025-11-20 11:53:05,982 - MainThread - urllib3.connectionpool - DEBUG - Starting new HTTPS connection (1): s3.ap-northeast-1.amazonaws.com:443
念の為、名前解決の結果を見てみるとちゃんとサービスリージョンで指定した通り、us-east-1のVPCエンドポイントを参照していることがわかります。
$ nslookup s3.us-east-1.amazonaws.com
Server: 10.0.0.2
Address: 10.0.0.2#53
Non-authoritative answer:
Name: s3.us-east-1.amazonaws.com
Address: 10.0.2.8
Name: s3.us-east-1.amazonaws.com
Address: 10.0.1.246
さいごに
以上、AWSサービスのAWS PrivateLinkがクロスリージョン接続をサポートするようになったアップデートをまとめました。
従来は複雑なネットワーク構成が必要だったクロスリージョンでのプライベート接続が、VPCエンドポイントの作成だけで実現できるようになったのは大きな進化ですね。
特にセキュリティ要件が厳しいシステムや、データレジデンシーへの対応が必要なグローバルアプリケーションの設計において、非常に有用な選択肢になると思います。
ただし、現状で対応しているAWSサービスが少なかったりサポート対象のアベイラビリティゾーンに制限があったり、
いくつか注意すべき点もあるので、実際に導入する際はしっかりと要件を確認した上で採用しましょう。






