新規AWSセキュリティーグループVPCアソシエーション機能検証

## はじめに
VPC内に作成するパブリック・インスタンスやプライベート・インスタンスの通信制御を行うには、AWSのセキュリティーグループを作成して制御を行います。これまで、セキュリティーグループはインスタンスの作成先のVPCでのみ使用することができました。その為、新たにVPCを用意してインスタンスを作成する時には、再度そのVPC用にセキュリティーグループを作成する必要がありました。
今回、2024年10月31日付けで、セキュリティーグループを同一AWSアカウントの複数のVPCへの関連付けを可能にする新規AWSセキュリティーグループVPCアソシエーション機能がリリースされました。

https://aws.amazon.com/jp/about-aws/whats-new/2024/10/amazon-virtual-private-cloud-security-group-sharing/

当新機能を使用することにより、セキュリティー・グループの定義本体は1つのVPCでのみ管理することができるようになり、メンテナンス性の向上が期待されます。
本記事では、筆者の個人検証環境において実施した新機能検証結果をご紹介します。

## AWSセキュリティーグループVPCアソシエーション機能
VPCのドキュメントにも関連する記述が新たに追加されています。

https://docs.aws.amazon.com/vpc/latest/userguide/security-group-assoc.html

ドキュメントによると、機能を使用する条件としては以下の点が挙げられています。
* VPCとセキュリティーグループは同一アカウントに属していること
* VPCとセキュリティーグループは同一リージョン内に存在すること
* アカウント側で作成したセキュリティーグループのみ、他VPCへの紐付けが可能
※ デフォルトのセキュリティーグループには、当機能は使用不可。

機能の使い方としては、基本的に以下の流れで実施します。
1. 複数VPCを所有している場合、どこか1つのVPCを選択し、そこでセキュリティーグループを定義する。
2. 同一AWSアカウントが所有している他のVPCに対し、作成したセキュリティー・グループを関連付ける。
※他のVPC内では、セキュリティー・グループの定義作成は不要。

当機能をサポートしているサービスは、ドキュメントによると下記9つのサービスとなっています。
* Amazon API Gateway (REST APIs only)
* AWS Auto Scaling
* AWS CloudFormation
* Amazon EC2
* Amazon EFS
* Amazon EKS
* Amazon FSx
* AWS PrivateLink
* Amazon Route 53

全てのサービスに対して検証を行うのは大変ですので、今回はEC2とEFSの2点にフォーカスして機能検証を実施しました。

## 機能検証
今回の検証構成図は下記になります。
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/18e67f9d-e24a-dfb7-7ba4-1f1e1ca26544.png)

* Main-VPC
  * 実際にセキュリティーグループを作成するVPC
  * CIDRアドレス範囲は、10.0.0.0/16
  * 以下のパブリック・サブネット、プライベート・サブネットをもつ。
    * パブリック・サブネットのCIDRアドレス範囲は、10.0.1.0/24
    * プライベート・サブネットのCIDRアドレス範囲は、10.0.1.0/24
    * 各々のサブネットに対して、EC2インスタンスが1個ずつ稼働。 
  * 下記3つのセキュリティーグループを定義
    * EC2-Public-Security-Group：パブリック・インスタンス用のセキュリティーグループ
      * 外部からhttp、https、SSHアクセスできるようにする。
      * 外部からインスタンスに対してpingが実行できるようにする。 
    * EC2-Private-Security-Group：プライベート・インスタンス用のセキュリティーグループ
      * パブリック・インスタンスからのsshアクセス、pingが実行できるようにする。 
    * MountTarget-Security-Group：EFSのマウントターゲットに紐付けるセキュリティーグループ
      * パブリック・インスタンス上で、EFSのマウントができるようにする。   
    
* Remote-VPC
  * Main-VPC側で作成したセキュリティーグループの関連付け先VPC
  * CIDRアドレス範囲は、20.0.0.0/16
  * 以下のパブリック・サブネット、プライベート・サブネットをもつ。
    * パブリック・サブネットのCIDRアドレス範囲は、20.0.1.0/24
    * プライベート・サブネットのCIDRアドレス範囲は、20.0.1.0/24
    * 各々のサブネットに対して、EC2インスタンスが1個ずつ稼働。 

* Main-EFS
  * Main-VPC側にマウントするためのEFS
* Remote-EFS
  * Remote-VPC側にマウントするためのEFS  

### 事前準備
AWSのマネジメント・コンソールからでも検証は可能ですが、環境構築が大変なので、いつものようにCloudFormationのコードを作成して、検証を実施しました。今回用意したサンプルコードは以下のものになります。

#### Remote-VPC作成用コード
<details><summary>RemoteVPC-Stack.yaml</summary>

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Script for Creating Remote-VPC Stack (VPC,Public Subnet,Private Subnet,Internet Gateway,Route Table)
Parameters:
  AzName:
    Description: Set az-name
    Type: String
    Default: ap-northeast-1a
    AllowedValues:
      - ap-northeast-1a
      - ap-northeast-1c
      - ap-northeast-1d
  VPCCidrBlock:
    Description: VPC Cidr Block
    Type: String
    Default: 20.0.0.0/16
  PublicSubnetCidrBlock:
    Description: Public Subnet Cidr Block for az-name
    Type: String
    Default: 20.0.1.0/24
  PrivateSubnetCidrBlock:
    Description: Private Subnet Cidr Block for az-name
    Type: String
    Default: 20.0.2.0/24

Resources:
  RemoteVPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: !Ref VPCCidrBlock
      EnableDnsHostnames: 'true'
      EnableDnsSupport: 'true'
      Tags: 
        - Key: Name
          Value: Remote-VPC
  RemotePublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref RemoteVPC
      CidrBlock: !Ref PublicSubnetCidrBlock
      AvailabilityZone: !Ref AzName
      Tags:
        - Key: Name
          Value: RemoteVPC-Public-Subnet
    DependsOn: RemoteVPC
  RemotePrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref RemoteVPC
      CidrBlock: !Ref PrivateSubnetCidrBlock
      AvailabilityZone: !Ref AzName
      Tags:
        - Key: Name
          Value: RemoteVPC-Private-Subnet
    DependsOn: RemoteVPC
  RemoteIGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: RemoteVPC-IGW
    DependsOn: RemoteVPC
  IGWAttachToRemoteVPC:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref RemoteVPC
      InternetGatewayId: !Ref RemoteIGW 
  RemotePublicRtbl:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref RemoteVPC
      Tags:
        - Key: Name
          Value: RemoteVPC-Public-Rtbl
    DependsOn: RemoteIGW
  RemotePublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: RemotePublicRtbl
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref RemoteIGW
  RemotePublicSubnetRouteTblAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref RemotePublicRtbl
      SubnetId: !Ref RemotePublicSubnet
    DependsOn: RemotePublicRoute
  RemotePrivateRtbl:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref RemoteVPC
      Tags:
        - Key: Name
          Value: RemoteVPC-Private-Rtbl
    DependsOn: RemotePublicRtbl
  RemotePrivateSubnetRouteTblAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref RemotePrivateRtbl
      SubnetId: !Ref RemotePrivateSubnet
    DependsOn: RemotePrivateRtbl
  
Outputs:
  RemoteVPCId:
    Description: The ID of Remote-VPC
    Value: !Ref RemoteVPC
    Export:
      Name: !Sub "${AWS::StackName}--VPCId"
  RemotePublicSubnetId:
    Description: The ID of Public Subnet for Remote-VPC
    Value: !Ref RemotePublicSubnet
    Export:
      Name: !Sub "${AWS::StackName}--PublicSubnetId"
  RemotePrivateSubnetId:
    Description: The ID of Private Subnet for Remote-VPC
    Value: !Ref RemotePrivateSubnet
    Export:
      Name: !Sub "${AWS::StackName}--PrivateSubnetId"
```
</details>

本コード内では、セキュリティーグループの作成は実施していません。後続で使用するCloudFormationコードへのインプットに必要となる下記の情報をエクスポートしています。
* Remote-VPCのVPC ID
* Remote-VPCのパブリック・サブネットID
* Remote-VPCのプライベート・サブネットID

#### Main-VPC作成用コード
<details><summary>MainVPC-Stack.yaml</summary>

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Script for Creating Main-VPC Stack (VPC,Public Subnet,Private Subnet,Internet Gateway,Route Table,Security Group)
Parameters:
  AzName:
    Description: Set az-name
    Type: String
    Default: ap-northeast-1a
    AllowedValues:
      - ap-northeast-1a
      - ap-northeast-1c
      - ap-northeast-1d
  VPCCidrBlock:
    Description: VPC Cidr Block
    Type: String
    Default: 10.0.0.0/16
  PublicSubnetCidrBlock:
    Description: Public Subnet Cidr Block for az-name
    Type: String
    Default: 10.0.1.0/24
  PrivateSubnetCidrBlock:
    Description: Private Subnet Cidr Block for az-name
    Type: String
    Default: 10.0.2.0/24
  RemoteVPCStackName:
    Description: Remote VPC Stack Name
    Type: String
    Default: RemoteVPC-Stack  
  
Resources:
  MainVPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: !Ref VPCCidrBlock
      EnableDnsHostnames: 'true'
      EnableDnsSupport: 'true'
      Tags: 
        - Key: Name
          Value: Main-VPC
  MainPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MainVPC
      CidrBlock: !Ref PublicSubnetCidrBlock
      AvailabilityZone: !Ref AzName
      Tags:
        - Key: Name
          Value: MainVPC-Public-Subnet
    DependsOn: MainVPC
  MainPrivateSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MainVPC
      CidrBlock: !Ref PrivateSubnetCidrBlock
      AvailabilityZone: !Ref AzName
      Tags:
        - Key: Name
          Value: MainVPC-Private-Subnet
    DependsOn: MainVPC
  MainIGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: MainVPC-IGW
    DependsOn: MainVPC
  IGWAttachToMainVPC:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MainVPC
      InternetGatewayId: !Ref MainIGW 
  MainPublicRtbl:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MainVPC
      Tags:
        - Key: Name
          Value: MainVPC-Public-Rtbl
    DependsOn: MainIGW
  MainPublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: MainPublicRtbl
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MainIGW
  MainPublicSubnetRouteTblAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref MainPublicRtbl
      SubnetId: !Ref MainPublicSubnet
  MainPrivateRtbl:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MainVPC
      Tags:
        - Key: Name
          Value: Main-Private-Rtbl
  MainPrivateSubnetRouteTblAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref MainPrivateRtbl
      SubnetId: !Ref MainPrivateSubnet
  EC2PublicSecGrp:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow http to client host
      VpcId: !Ref MainVPC
      SecurityGroupIngress: 
        - CidrIp: 0.0.0.0/0
          IpProtocol: tcp
          FromPort: 22
          ToPort: 22
        - CidrIp: 0.0.0.0/0
          IpProtocol: tcp
          FromPort: 80
          ToPort: 80
        - CidrIp: 0.0.0.0/0
          IpProtocol: tcp
          FromPort: 443
          ToPort: 443
        - CidrIp: 0.0.0.0/0
          IpProtocol: icmp
          FromPort: 8
          ToPort: -1
      Tags:
        - Key: Name
          Value: EC2-Public-Security-Group
  EC2PublicSecGrpAssociation:
    Type: AWS::EC2::SecurityGroupVpcAssociation
    Properties:
      GroupId: !Ref EC2PublicSecGrp
      VpcId: 
        Fn::ImportValue:
          !Sub "${RemoteVPCStackName}--VPCId"
    DependsOn: EC2PublicSecGrp
  EC2PrivateSecGrp:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow http from EC2-Public-Security-Group
      VpcId: !Ref MainVPC
      SecurityGroupIngress: 
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          SourceSecurityGroupId: !Ref EC2PublicSecGrp
        - IpProtocol: icmp
          FromPort: 8
          ToPort: -1
          SourceSecurityGroupId: !Ref EC2PublicSecGrp
      Tags:
        - Key: Name
          Value: EC2-Private-Security-Group
  EC2PrivateSecGrpAssociation:
    Type: AWS::EC2::SecurityGroupVpcAssociation
    Properties:
      GroupId: !Ref EC2PrivateSecGrp
      VpcId: 
        Fn::ImportValue:
          !Sub "${RemoteVPCStackName}--VPCId"
    DependsOn: EC2PrivateSecGrp


Outputs:
  MainVPCid:
    Description: The ID of Main-VPC
    Value: !Ref MainVPC
    Export:
      Name: !Sub "${AWS::StackName}--VPCId"
  MainPublicSubnetId:
    Description: The ID of Public Subnet for Main-VPC
    Value: !Ref MainPublicSubnet
    Export:
      Name: !Sub "${AWS::StackName}--PublicSubnetId"
  MainPrivateSubnetId:
    Description: The ID of Private Subnet for Main-VPC
    Value: !Ref MainPrivateSubnet
    Export:
      Name: !Sub "${AWS::StackName}--PrivateSubnetId"
  EC2PublicSubnetSecurityGroupId:
    Description: The ID of EC2 Public Subnet Security Group  
    Value: !Ref EC2PublicSecGrp
    Export:
      Name: !Sub "${AWS::StackName}--PublicSubnetSecGrpId"
  EC2PrivateSubnetSecurityGroupId:
    Description: The ID of EC2 Private Subnet Security Group  
    Value: !Ref EC2PrivateSecGrp
    Export:
      Name: !Sub "${AWS::StackName}--PrivateSubnetSecGrpId"
```
</details>

本コードの実行においては、先のRemote-VPC作成用コードを実行してできたスタック名を他の入力パラメータと同様に指定すれば実行可能です。
今回、当機能のリリースにより、以下のCloudFormationリソースタイプが新たに追加されています。

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-securitygroupvpcassociation.html

本コード内では以下の箇所で上記リソースタイプの定義を実施しています。
```
EC2PublicSecGrpAssociation:
  Type: AWS::EC2::SecurityGroupVpcAssociation
  Properties:
    GroupId: !Ref EC2PublicSecGrp
    VpcId: 
      Fn::ImportValue:
        !Sub "${RemoteVPCStackName}--VPCId"
  DependsOn: EC2PublicSecGrp

EC2PrivateSecGrpAssociation:
  Type: AWS::EC2::SecurityGroupVpcAssociation
  Properties:
    GroupId: !Ref EC2PrivateSecGrp
    VpcId: 
      Fn::ImportValue:
        !Sub "${RemoteVPCStackName}--VPCId"
  DependsOn: EC2PrivateSecGrp
```
このリソースタイプには、セキュリティーグループのID、及び、セキュリティーグループの関連付け先VPCのIDを指定します。これをするだけで、Remote-VPC内にあたかもセキュリティーグループが存在しているかのように処理させることが可能になります。
ちなみに、コードの実行においては、このセキュリティーグループの関連付け処理に体感で40秒程要しました。(解除処理時も同様に40秒程要しています。)

本コードでも、後続のコード実行に必要となる情報をエクスポートしています。

#### EC2インスタンス作成用コード
<details><summary>EC2Instance-Stack.yaml</summary>

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Script for Creating Public/Private EC2 Instance

Parameters:
  MainVPCStackName:
    Description: This stack name is already created MainVPC Stack for provisioning EC2 Instance
    Type: String
    Default: MainVPC-Stack
  RemoteVPCStackName:
    Description: This stack name is already created RemoteVPC Stack for provisioning EC2 Instance
    Type: String
    Default: RemoteVPC-Stack

Resources:
  MyKey:
    Type: AWS::EC2::KeyPair
    Properties:
      KeyName: MyEC2Key
      PublicKeyMaterial: '{{resolve:ssm:publickeymaterial}}'
      Tags:
        - Key: Name
          Value: My-Key
  MainPublicInstance:
    Type: AWS::EC2::Instance
    Properties:
      CreditSpecification: 
        CPUCredits: standard
      InstanceType: t2.micro
      KeyName: !Ref MyKey
      ImageId: ami-08ce76bae392de7dc
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: 
            Fn::ImportValue: 
              !Sub "${MainVPCStackName}--PublicSubnetId"
          GroupSet:
            - 
              Fn::ImportValue: 
                !Sub "${MainVPCStackName}--PublicSubnetSecGrpId"
      Tags:
        - Key: Name
          Value: MainVPC-Public-instance
  MainPrivateInstance:
    Type: AWS::EC2::Instance
    Properties:
      CreditSpecification: 
        CPUCredits: standard
      InstanceType: t2.micro
      KeyName: !Ref MyKey
      ImageId: ami-08ce76bae392de7dc
      NetworkInterfaces:
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          SubnetId: 
            Fn::ImportValue: 
              !Sub "${MainVPCStackName}--PrivateSubnetId"
          GroupSet:
            - 
              Fn::ImportValue: 
                !Sub "${MainVPCStackName}--PrivateSubnetSecGrpId"
      Tags:
        - Key: Name
          Value: MainVPC-Private-instance
  RemotePublicInstance:
    Type: AWS::EC2::Instance
    Properties:
      CreditSpecification: 
        CPUCredits: standard
      InstanceType: t2.micro
      KeyName: !Ref MyKey
      ImageId: ami-08ce76bae392de7dc
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: 
            Fn::ImportValue: 
              !Sub "${RemoteVPCStackName}--PublicSubnetId"
          GroupSet:
            - 
              Fn::ImportValue: 
                !Sub "${MainVPCStackName}--PublicSubnetSecGrpId"
      Tags:
        - Key: Name
          Value: Remote-Public-instance
  RemotePrivateInstance:
    Type: AWS::EC2::Instance
    Properties:
      CreditSpecification: 
        CPUCredits: standard
      InstanceType: t2.micro
      KeyName: !Ref MyKey
      ImageId: ami-08ce76bae392de7dc
      NetworkInterfaces:
        - AssociatePublicIpAddress: "false"
          DeviceIndex: "0"
          SubnetId: 
            Fn::ImportValue: 
              !Sub "${RemoteVPCStackName}--PrivateSubnetId"
          GroupSet:
            - 
              Fn::ImportValue: 
                !Sub "${MainVPCStackName}--PrivateSubnetSecGrpId"
      Tags:
        - Key: Name
          Value: RemoteVPC-Private-instance
  
Outputs:
  MainPublicInstanceIPaddr:
    Description: Public Instance's IP address for MainVPC
    Value: !GetAtt MainPublicInstance.PublicIp
  MainPrivateInstanceIPaddr:
    Description: Private Instance's IP address for MainVPC
    Value: !GetAtt MainPrivateInstance.PrivateIp
  RemotePublicInstanceIPaddr:
    Description: Public Instance's IP address for RemoteVPC
    Value: !GetAtt RemotePublicInstance.PublicIp
  RemotePrivateInstanceIPaddr:
    Description: Private Instance's IP address for RemoteVPC
    Value: !GetAtt RemotePrivateInstance.PrivateIp
```
</details>

本コードの実行においては、先の2つのコード実行後に作成される各々のスタック名を入力する必要があります。このコードのポイントは、Remote-VPC側で稼働するインスタンスのネットワーク・インタフェース定義箇所(RemotePublicInstance、RemotePrivateInstanceのNetworkInterfacesを参照)において、サブネットは実際インスタンスが稼働するサブネットを指定しますが、セキュリティーグループについては、Main-VPC側に存在するセキュリティーグループのIDを指定する点になります。

#### EFS作成用コード
<details><summary>EFS-Stack.yaml</summary>

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Sample Script for Creating Elastic File System

Parameters:
  MainVPCStackName:
    Description: This stack name is already created MainVPC Stack for provisioning EC2 Instance
    Type: String
    Default: MainVPC-Stack
  RemoteVPCStackName:
    Description: This stack name is already created RemoteVPC Stack for provisioning EC2 Instance
    Type: String
    Default: RemoteVPC-Stack

Resources:
  MountTargetSecGrp:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow access to EFS from EC2-Public-Security-Group
      VpcId:
        Fn::ImportValue:
          !Sub "${MainVPCStackName}--VPCId"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '2049'
          ToPort: '2049'
          SourceSecurityGroupId:
            Fn::ImportValue:
              !Sub "${MainVPCStackName}--PublicSubnetSecGrpId"
      Tags:
        - Key: Name
          Value: MountTarget-SecurityGroup
  MountTargetSecGrpAssociation:
    Type: AWS::EC2::SecurityGroupVpcAssociation
    Properties:
      GroupId: !Ref MountTargetSecGrp
      VpcId:
        Fn::ImportValue:
          !Sub "${RemoteVPCStackName}--VPCId"
    DependsOn: MountTargetSecGrp  
  MainEFS:
    Type: AWS::EFS::FileSystem
    Properties:
      Encrypted: true
      FileSystemTags:
        - Key: Name
          Value: Main-EFS
  MainEFSMountTarget:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref MainEFS
      SubnetId:
        Fn::ImportValue:
          !Sub "${MainVPCStackName}--PublicSubnetId"
      SecurityGroups:
        - !Ref MountTargetSecGrp
  RemoteEFS:
    Type: AWS::EFS::FileSystem
    Properties:
      Encrypted: true
      FileSystemTags:
        - Key: Name
          Value: Remote-EFS
  RemoteEFSMountTarget:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref RemoteEFS
      SubnetId:
        Fn::ImportValue:
          !Sub "${RemoteVPCStackName}--PublicSubnetId"
      SecurityGroups:
        - !Ref MountTargetSecGrp
    DependsOn: MountTargetSecGrpAssociation
```
</details>

本コードの実行においては、先の2つのコード実行後に作成される各々のスタック名を入力する必要があります。EC2インスタンスのセキュリティーグループ同様に、Main-VPC側で、EFSのマウントターゲットに紐付けるセキュリティーグループを作成し、Remote-VPC側に作成したセキュリティーの関連付けを行います。

:::note warn
処理のタイミングによっては、セキュリティーグループの関連付け処理が終わる前に、Remote-VPC側でマウントターゲットの作成が始まってしまう可能性があります。その場合、「セキュリティーグループが同一ネットワーク内に存在しない。」と言われてエラーとなってしまいます。その為、DependsOnを指定して、処理の順序付けを行ってあげる考慮が必要となります。
:::

上記3つのCloudFormationコードが全て正常終了することを確認します。
![q1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/29c0815a-b68a-935f-d011-b68291ee4f38.jpeg)
併せて、パブリック・インスタンスとプライベート・インスタンスのIPアドレスを確認しておきます。
![q9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/632b4ec2-ec5e-8384-836e-e26a145b56db.jpeg)


### AWSマネジメントコンソールでの確認
下記の図に見られるように、セキュリティーグループの画面において、新たに「VPC関連付け-新規」というタブが追加されています。(赤枠参照)
* デフォルトのセキュリティーグループ
ドキュメントの記載通り、「他のVPCには関連付けすることができません。」と表示されます。
![q2_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/13474a08-28a2-8bad-b7c7-db7067590f74.jpeg)

作成されたMain-VPCとRemote-VPCのIDは下記の通りです。
![q7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/aa8f0193-900e-2d52-47f8-0f66c2c963ed.jpeg)
これを踏まえて、残りのセキュリティーグループについて確認します。

* パブリック・インスタンス用のセキュリティーグループ
![q2_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/5d9d6bc3-f063-bbaa-f7f4-eaf018ba2e60.jpeg)
* プライベート・インスタンス用のセキュリティーグループ
![q2_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/7c12eca5-1c4b-e2e3-ce83-90c08d7b0cf4.jpeg)
* EFSのマウントターゲットのセキュリティーグループ
![q2_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/68748a4f-5352-c641-e41e-908f9a5a17aa.jpeg)

上記3つの画面における「VPC関連付け」において、セキュリティーグループIDはMain-VPC側で作成したもの、VPC IDはRemote-VPCのIDが表示されています。

### パブリック・インスタンスへのアクセス確認
Main-VPC、Remote-VPC各々のパブリック・インスタンスの画面において、赤枠箇所に見られるように、同一のセキュリティーグループが指定されていることがわかります。
![q8_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/cad31e16-a09b-92ba-b4c4-fbf7f24cefdc.jpeg)
![q8_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/572109ac-88a4-ba3a-c912-f57d8694221d.jpeg)
Main-VPC、Remote-VPC各々のパブリック・インスタンスに対し、まずはPingコマンドを発行します。
```
Microsoft Windows [Version 10.0.22631.4460]
(c) Microsoft Corporation. All rights reserved.

C:\Users\testuser>ping 13.231.122.24

13.231.122.24 に ping を送信しています 32 バイトのデータ:
13.231.122.24 からの応答: バイト数 =32 時間 =9ms TTL=112
13.231.122.24 からの応答: バイト数 =32 時間 =10ms TTL=112
13.231.122.24 からの応答: バイト数 =32 時間 =9ms TTL=112
13.231.122.24 からの応答: バイト数 =32 時間 =10ms TTL=112

13.231.122.24 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 9ms、最大 = 10ms、平均 = 9ms

C:\Users\testuser>ping 57.180.14.209

57.180.14.209 に ping を送信しています 32 バイトのデータ:
57.180.14.209 からの応答: バイト数 =32 時間 =9ms TTL=113
57.180.14.209 からの応答: バイト数 =32 時間 =9ms TTL=113
57.180.14.209 からの応答: バイト数 =32 時間 =10ms TTL=113
57.180.14.209 からの応答: バイト数 =32 時間 =10ms TTL=113

57.180.14.209 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 9ms、最大 = 10ms、平均 = 9ms
```
いずれのパブリック・インスタンスに対してもping応答が返ってくることが確認できました。続けて、teratermを使用してSSH接続を試みます。
![q10_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/c17267a4-7947-2984-99b1-ffbc74f476a5.jpeg)
![q10_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/be70bb18-f58c-8850-ac7b-334990df06b0.jpeg)
いずれのパブリック・インスタンスに対してもSSH接続できることが確認できました。
これにより、パブリック・インスタンスに対するセキュリティーグループの関連付け機能は問題なく機能していることが確認できました。

### プライベート・インスタンスへのアクセス確認
Main-VPC、Remote-VPC各々のパブリック・インスタンスから、プライベート・インスタンスに対してpingコマンドを発行します。
```
[ec2-user@ip-10-0-1-74 ~]$ ping 10.0.2.28
PING 10.0.2.28 (10.0.2.28) 56(84) bytes of data.
64 bytes from 10.0.2.28: icmp_seq=1 ttl=127 time=3.14 ms
64 bytes from 10.0.2.28: icmp_seq=2 ttl=127 time=0.564 ms
64 bytes from 10.0.2.28: icmp_seq=3 ttl=127 time=0.986 ms
64 bytes from 10.0.2.28: icmp_seq=4 ttl=127 time=0.614 ms
64 bytes from 10.0.2.28: icmp_seq=5 ttl=127 time=1.50 ms
^C
--- 10.0.2.28 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4149ms
rtt min/avg/max/mdev = 0.564/1.360/3.139/0.950 ms
```
```
[ec2-user@ip-20-0-1-36 ~]$ ping 20.0.2.48
PING 20.0.2.48 (20.0.2.48) 56(84) bytes of data.
64 bytes from 20.0.2.48: icmp_seq=1 ttl=127 time=2.25 ms
64 bytes from 20.0.2.48: icmp_seq=2 ttl=127 time=0.443 ms
64 bytes from 20.0.2.48: icmp_seq=3 ttl=127 time=0.284 ms
64 bytes from 20.0.2.48: icmp_seq=4 ttl=127 time=0.475 ms
^C
--- 20.0.2.48 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3082ms
rtt min/avg/max/mdev = 0.284/0.862/2.249/0.803 ms
```
双方いずれも問題なく、pingの応答が返ってきたことがわかります。

:::note info
セキュリティーグループの関連付けは行いましたが、あくまでも、Main-VPCのプライベート・インスタンスに対しては、Main-VPCのパブリック・インスタンスからのアクセスのみ、Remote-VPCのプライベート・インスタンスに対しては、Remote-VPCのパブリック・インスタンスからのアクセスのみが可能であることに変わりはありません。VPC間の通信をするためには、VPCピアリング等の環境設定が必要です。
:::

次にパブリック・インスタンスからプライベート・インスタンスへのSSH接続を確認するため、事前にPCローカルにあるSSHの秘密鍵を転送し、秘密鍵のアクセス権をchmodで600にしておきます。その上で、実際にパブリック・インスタンスからSSH接続を試みます。
```
[ec2-user@ip-10-0-1-74 ~]$ ssh -i MyEC2Key.pem ec2-user@10.0.2.28
The authenticity of host '10.0.2.28 (10.0.2.28)' can't be established.
ED25519 key fingerprint is SHA256:z7lv0vssV79ZFuAvgydErw5UQDnh+XHxneqHk0XRwKY.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.0.2.28' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
[ec2-user@ip-10-0-2-28 ~]$
```
```
[ec2-user@ip-20-0-1-36 ~]$ ssh -i MyEC2Key.pem ec2-user@20.0.2.48
The authenticity of host '20.0.2.48 (20.0.2.48)' can't be established.
ED25519 key fingerprint is SHA256:84he3jDy2w9SztWKbf+5SjUQBvWNVIfL+YHNIAHwFIM.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '20.0.2.48' (ED25519) to the list of known hosts.
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
[ec2-user@ip-20-0-2-48 ~]$
```
双方、いずれもSSH接続できることが確認できました。これにより、プライベート・インスタンスに対するセキュリティーグループの関連付け機能は問題なく機能していることが確認できました。

### EFSのマウント確認
Main-VPC、Remote-VPCのパブリック・インスタンス上で、事前に下記のコマンドでマウントヘルパー・ユーティリティーを入手しておきます。
```
$ sudo yum install -y amazon-efs-utils
```
* Main-VPC側にマウントするMain-EFSの情報
![q11_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/351ecc29-a14e-9fb9-e06e-de4a8446ca32.jpeg)
![q11_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/d01cdb3f-fa72-baf4-87fb-399881d48f2f.jpeg)
![q11_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/d111f5fd-1d78-fc59-37e3-70962f08b916.jpeg)

最後の画面に表示されているマウントヘルパーのコマンドを実行し、マウントできることを確認します。
```
[ec2-user@ip-10-0-1-74 ~]$ sudo mkdir efs
[ec2-user@ip-10-0-1-74 ~]$ sudo mount -t efs -o tls fs-0c8cbc920ef1bb550:/ efs
[ec2-user@ip-10-0-1-74 ~]$ 
```
正常にマウントすることができています。

* Remote-VPC側にマウントするRemote-EFSの情報
![q12_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/8b328a7c-b2d1-c36d-d045-954b66d1550c.jpeg)
![q12_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/867a19cf-d205-4eda-3c10-52e4d8469fe5.jpeg)
![q12_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1567099/2ceb33fc-54b3-3076-83ac-7cf37907c959.jpeg)

最後の画面に表示されているマウントヘルパーのコマンドを実行し、マウントできることを確認します。
```
[ec2-user@ip-20-0-1-36 ~]$ sudo mkdir efs
[ec2-user@ip-20-0-1-36 ~]$ sudo mount -t efs -o tls fs-07373bd95c4bc9167:/ efs
[ec2-user@ip-20-0-1-36 ~]$
```
こちらも正常にマウントすることができています。
以上のことから、EFSのマウントターゲットのセキュリティーグループにおいても、セキュリティーグループの関連付け機能が正しく機能したことが確認できました。

:::note info
EFSが1時点でマウントできるVPCは1個のみであり、セキュリティーグループの関連付けをしたからといって、別VPCからも同時に同じEFSをマウントできるわけではありません。同じEFSをマウントしたい場合には、VPCピアリング等のVPC間通信が必要になります。
:::

## さいごに
本記事では、2024年10月31日にリリースされた、AWSセキュリティーグループVPCアソシエーション機能の検証結果をご紹介しました。

どこか1つのVPCでのみセキュリティーグループの作成をすれば良くなったことで、リソースのメンテナンス性が向上する良い機能であると感じました。
以前ならば、たとえIaC化していたとしても、VPCを作成するたびに同じようなセキュリティーグループの定義を書く必要があったので、結構手間がかかってましたし、定義変更の際にどこかのVPCのみ修正漏れが発生するというリスクを気にする必要がありました。今回の機能の登場により、コードの修正反映を大きく削減することが可能になったと感じます。

今後も何か新しい機能がリリースされたら、機能検証を継続していきたいと思います。