【アップデート検証】AWS PrivateLinkで"NLBを介さずに"かつ"クロスリージョンで"、VPC内リソースに接続でき…ませんでした！！

# はじめに

お疲れ様です。矢儀 @yuki_ink です。

re:Invent 2024、楽しんでますか？
マルチモーダル生成AIモデル「Amazon Nova」の発表など、今年もアツいですね。

そんなラインナップの中では地味かもですが、個人的には以下2つのアップデートが刺さりました。

**[① AWS PrivateLinkでNLBを介さずにVPC内リソースにアクセスできるようになった！！！](https://aws.amazon.com/jp/about-aws/whats-new/2024/12/access-vpc-resources-aws-privatelink/)**
**[② AWS PrivateLink がクロスリージョン接続をサポート！！！](https://aws.amazon.com/jp/about-aws/whats-new/2024/11/aws-privatelink-across-region-connectivity/)**

ということは・・・
**AWS PrivateLinkで「NLBを介さずに」かつ「クロスリージョンで」、VPC内リソースにアクセスできるのでは？？？**

イメージはこんな感じ。

![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/b60a585c-bf94-c3da-1f3d-7fd8913f35f5.png)

やってみましょう！！

# 先に結論
**駄目でした！！！**
今時点での結論は以下のようです。
- 同一リージョン内では、NLBを介さずにVPC内リソースにアクセスできる
- NLBまたはGWLBを使用するエンドポイントサービスについては、クロスリージョンで接続可能


### 同一リージョン内では、NLBを介さずにVPC内リソースにアクセスできる
【参考】

https://dev.classmethod.jp/articles/privatelink-vpc-endpoint-direct-access-update/

NLBの代わりに(?)、Resource gatewayとResource configurationを設定する必要があります。
これらのリソースはVPC Latticeの機能のようですが、そのVPC Latticeは、現状、サービス単体ではクロスリージョン対応していません。
VPCピアリングやTransit Gatewayなどを使って繋ぐか、間にNLBなどを挟むかしないと、クロスリージョン通信はできません。

>クロスリージョンおよびオンプレミスの通信パターンについては、現在、クロスリージョン VPC ピアリング、AWS Transit Gateway、AWS Direct Connect、または AWS Cloud WAN などの AWS グローバル接続サービスを利用できます。リージョン間の接続パターンの詳細については、この[ブログ](https://aws.amazon.com/jp/blogs/networking-and-content-delivery/external-connectivity-to-amazon-vpc-lattice/)をご覧ください。
（出典）[Amazon VPC Lattice のよくある質問](https://aws.amazon.com/jp/vpc/lattice/faqs/)

この時点で、ああ、駄目そう・・・という雰囲気が漂ってきますw

AWS PrivateLinkとVPC Latticeの関係性はこちらの素晴らしい記事をご参照ください。

https://dev.classmethod.jp/articles/aws-privatelink-vpc-lattice-relation/

### NLBまたはGWLBを使用するエンドポイントサービスについては、クロスリージョンで接続可能
【参考】

https://dev.classmethod.jp/articles/aws-privatelink-across-region-connectivity/

https://iret.media/130754

のんピさんのまとめが分かりやすかったです。
>- PrivateLink ReadyパートナーのサービスやNLBとGWLBを使用するエンドポイントサービスについてクロスリージョンで接続可能
>    - エンドポイントサービスが公開されているリージョン名を指定する必要がある
> - AWSのサービスに対するGateway型VPCエンドポイント、Interface型VPCエンドポイント経由でクロスリージョンで操作することはできない
>     - 東京リージョンのS3のVPCエンドポイントを介して、大阪リージョンのS3バケットの操作を行うことはできない
>- VPCエンドポイントサービス側でサポートするリージョンをサービスリージョンとして指定する
>    - 複数指定可能
>- 追加課金要素は以下
>    - Interface型VPCエンドポイント所有者 : 追加で通常のクロスリージョンのデータ転送料金がかかる
>    - VPCエンドポイントサービスの所有者 : アクティブなリモートリージョンごとに固定の時間料金がかかる
>- クロスリージョンをサポートするVPCエンドポイントサービスはNLBが複数AZでデプロイされている必要がある





# 【供養】やってみた

この結論に至るまでに、ちょっと環境を触ってみたので供養します。
目指したのは最初のイメージ図。
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/b60a585c-bf94-c3da-1f3d-7fd8913f35f5.png)

事前準備として、東京リージョンとバージニア北部リージョンで、VPC、サブネット、EC2などのリソースを作成します。

CloudFormationでガサっと作成しました。

<details><summary>CloudFormationテンプレート</summary>

```vpc.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to create a private subnet, Security Group, VPC Endpoints, and an Amazon Linux instance with SSM role.

Parameters:
  AmazonLinuxAMI:
    Description: AMI ID for the Amazon Linux instance
    Type: String
    Default: ami-0abcdef1234567890  # Replace with a commonly used default value or leave as is for user input.

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "MyVPC-${AWS::Region}"

  PrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select 
        - 0
        - !GetAZs ''
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "MyPrivateSubnet-${AWS::Region}"

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "MyPrivateRouteTable-${AWS::Region}"

  RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref RouteTable

  VpcSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow all traffic within the VPC
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: -1  # All protocols
          CidrIp: 10.0.0.0/16  # Allow all traffic from the VPC CIDR
      SecurityGroupEgress:
        - IpProtocol: -1  # All protocols
          CidrIp: 0.0.0.0/0  # Allow all outbound traffic
      Tags:
        - Key: Name
          Value: !Sub "MyVpcSecurityGroup-${AWS::Region}"

  VPCEndpointSSM:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPC
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
      SubnetIds:
        - !Ref PrivateSubnet
      VpcEndpointType: Interface
      SecurityGroupIds:
        - !Ref VpcSecurityGroup

  VPCEndpointEC2Messages:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPC
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ec2messages"
      SubnetIds:
        - !Ref PrivateSubnet
      VpcEndpointType: Interface
      SecurityGroupIds:
        - !Ref VpcSecurityGroup

  VPCEndpointSSMMessages:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref VPC
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages"
      SubnetIds:
        - !Ref PrivateSubnet
      VpcEndpointType: Interface
      SecurityGroupIds:
        - !Ref VpcSecurityGroup

  SSMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "AmazonSSMManagedInstanceCoreRole-${AWS::Region}"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Tags:
        - Key: Name
          Value: !Sub "AmazonSSMManagedInstanceCoreRole-${AWS::Region}"

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub "AmazonSSMManagedInstanceProfile-${AWS::Region}"
      Roles:
        - !Ref SSMRole

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t3.micro
      ImageId: !Ref AmazonLinuxAMI
      SubnetId: !Ref PrivateSubnet
      SecurityGroupIds:
        - !Ref VpcSecurityGroup
      IamInstanceProfile: !Ref InstanceProfile
      Tags:
        - Key: Name
          Value: !Sub "MyAmazonLinuxInstance-${AWS::Region}"

Outputs:
  VpcId:
    Description: VPC ID
    Value: !Ref VPC

  PrivateSubnetId:
    Description: Private Subnet ID
    Value: !Ref PrivateSubnet

  SecurityGroupId:
    Description: Security Group ID
    Value: !Ref VpcSecurityGroup

  VPCEndpointSSMId:
    Description: VPC Endpoint ID for SSM
    Value: !Ref VPCEndpointSSM

  VPCEndpointEC2MessagesId:
    Description: VPC Endpoint ID for EC2 Messages
    Value: !Ref VPCEndpointEC2Messages

  VPCEndpointSSMMessagesId:
    Description: VPC Endpoint ID for SSM Messages
    Value: !Ref VPCEndpointSSMMessages

  InstanceId:
    Description: Amazon Linux Instance ID
    Value: !Ref Instance

```

</details>


CloudFormation実行時、`AmazonLinuxAMI` パラメータで、EC2に利用するAMIを選択するのがミソ。
テンプレート自体は各リージョンで同じものを使えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/3833e8ef-4dd6-6174-3fc4-f42b12641b0c.png)

:::note info
別にAmazon Linuxじゃなくてもいいんですが、Amazon LinuxにはSSMエージェントがデフォルトで入っているので便利！
:::


続いて、接続先リージョン（今回はバージニア北部リージョン）でResource gatewayとResource configurationを設定します。
先ほども触れた記事を参考にさせていただきました。

https://dev.classmethod.jp/articles/privatelink-vpc-endpoint-direct-access-update/

マネージメントコンソールからVPCの画面に行き、`PrivateLink and Lattice` > `Resource gateways` を選択し `Create resource gateway` をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/89e608fb-f8a2-addb-3099-1bae39857c41.png)

設定画面に遷移するので、以下のように設定。
VPC、サブネット、セキュリティグループは、事前準備で作成したものを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/daa378da-e50b-dc67-ff92-96541f07f61f.png)

続いて、VPC画面の `PrivateLink and Lattice` > `Resource configurations` を選択し `Create resource configuration` を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/e6391ecc-4091-8ed8-8598-807c604fffbe.png)


設定画面に遷移するので、以下のように設定。
`Resource gateway` は先ほど作成したものを選択します。
IPアドレスは、バージニア北部リージョンに作成したEC2のプライベートIPアドレスを指定しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/bc252550-a81e-a735-6e86-a7724992abd5.png)


ここまで来たら、あとは東京リージョン側でVPCエンドポイントを作ればヨシ！
・・・と思ったのですが、東京リージョンの設定画面上に、先ほどバージニア北部リージョンで設定したResouce confugurationが表示されることはなく😅
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/350f2a59-5cbe-67ee-831b-a15404671eab.png)

ここまで来て断念しました。

# 終わりに
PrivateLinkで他リージョンのVPC内リソースに接続したい場合は、今のところは大人しくNLBを間に挟むか、リージョン間のVPCピアリング（Inter-Region VPC Peering）など別の切り口を考えるかする必要があるという結論でした。
PrivateLinkに関する2つのアップデートについて、字面だけ見て、「これ組み合わせたらいけるんじゃね？？」というノリで、勢いで検証に着手しましたが、早々に断念しましたw
「手を動かしてみないと分からないところもある」ということを実感できた点では、良い経験になりました。

まだまだ始まったばかりのre:Invent！引き続き盛り上がっていきましょう！