VPC IP Address ManagerをCloudFormationで実装してみた

2023.02.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、つくぼし(tsukuboshi0755)です!

AWS 上でIPアドレスを一元管理したい場合、VPC IP Address Manager (IPAM)を用いる事で実現可能です。

今回は、VPC IP Address Manager (IPAM)をCloudFormationで実装し、VPCで利用する方法をご紹介します!

やりたい事

以下のリソースをCloudFormationで実装します。

  • IPAM
  • IPAMを利用するVPC

なおIPAMの設定は、基本以下のブログで実装するものと同様とします。

(リソースのName値のみ変更します)

IPAM

テンプレート全体

IPAMテンプレートの全体を以下に記載します。

ipam.yaml

AWSTemplateFormatVersion: "2010-09-09"

# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "IPAM Configuration"
        Parameters:
          - Name
          - TopLevelPoolCidr
          - RegionalPoolCidr
          - DevelopmentPoolCidr
          - DevelopMentPoolMinNetmask
          - DevelopMentPoolDefNetmask
          - DevelopMentPoolMaxNetmask

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
  Name:
    Description: "Fill in the System Name of IPAM."
    Type: String
    Default: cm
  TopLevelPoolCidr:
    Description: "Fill in the Cidr of Top-level Pool."
    Type: String
    Default: 192.0.0.0/8
  RegionalPoolCidr:
    Description: "Fill in the Cidr of Regional Pool."
    Type: String
    Default: 192.0.0.0/12
  DevelopmentPoolCidr:
    Description: "Fill in the Cidr of Development Pool."
    Type: String
    Default: 192.0.0.0/14
  DevelopMentPoolMinNetmask:
    Description: "Fill in the Minimum Netmask Length of Development Pool."
    Type: Number
    Default: 16
  DevelopMentPoolDefNetmask:
    Description: "Fill in the Default Netmask Length of Development Pool."
    Type: Number
    Default: 16
  DevelopMentPoolMaxNetmask:
    Description: "Fill in the Maximum Netmask Length of Development Pool."
    Type: Number
    Default: 24

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  # IP Address Manager
  IPAM:
    Type: AWS::EC2::IPAM
    Properties:
      OperatingRegions:
        - RegionName: ap-northeast-1
      Tags:
        - Key: Name
          Value: !Sub "${Name}-ipam"
  IPAMTopLevelPool:
    Type: AWS::EC2::IPAMPool
    Properties:
      AddressFamily: ipv4
      Description: "top-level pool"
      IpamScopeId: !GetAtt IPAM.PrivateDefaultScopeId
      ProvisionedCidrs:
        - Cidr: !Ref TopLevelPoolCidr
      Tags:
        - Key: Name
          Value: !Sub "${Name}-toplevel-pool"
  IPAMRegionalPool:
    Type: AWS::EC2::IPAMPool
    Properties:
      AddressFamily: ipv4
      Description: "regional pool"
      IpamScopeId: !GetAtt IPAM.PrivateDefaultScopeId
      Locale: ap-northeast-1
      SourceIpamPoolId: !Ref IPAMTopLevelPool
      ProvisionedCidrs:
        - Cidr: !Ref RegionalPoolCidr
      Tags:
        - Key: Name
          Value: !Sub "${Name}-regional-pool"
  IPAMDevelopmentPool:
    Type: AWS::EC2::IPAMPool
    Properties:
      AddressFamily: ipv4
      AllocationMinNetmaskLength: !Ref DevelopMentPoolMinNetmask
      AllocationDefaultNetmaskLength: !Ref DevelopMentPoolDefNetmask
      AllocationMaxNetmaskLength: !Ref DevelopMentPoolMaxNetmask
      Description: "development pool"
      IpamScopeId: !GetAtt IPAM.PrivateDefaultScopeId
      Locale: ap-northeast-1
      SourceIpamPoolId: !Ref IPAMRegionalPool
      ProvisionedCidrs:
        - Cidr: !Ref DevelopmentPoolCidr
      Tags:
        - Key: Name
          Value: !Sub "${Name}-development-pool"

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
  TopLevelPoolId:
    Description: "Top-level pool id"
    Value: !Ref IPAMTopLevelPool
  RegionalPoolId:
    Description: "Regional pool id"
    Value: !Ref IPAMRegionalPool
  DevelopmentPoolId:
    Description: "Development pool id"
    Value: !Ref IPAMDevelopmentPool
    Export:
      Name: DevelopmentPoolId

以下、テンプレートのポイントを説明します。

IPAM全般

  IPAM:
    Type: AWS::EC2::IPAM
    Properties:
      OperatingRegions:
        - RegionName: ap-northeast-1
      Tags:
        - Key: Name
          Value: !Sub "${Name}-ipam"

OperatingRegionsで、IPAMが管理する運用リージョンを設定します。

今回はap-northeast-1のみとしていますが、リスト形式で複数リージョンを指定する事も可能です。

トップレベルプール

  IPAMTopLevelPool:
    Type: AWS::EC2::IPAMPool
    Properties:
      AddressFamily: ipv4
      Description: "top-level pool"
      IpamScopeId: !GetAtt IPAM.PrivateDefaultScopeId
      ProvisionedCidrs:
        - Cidr: !Ref TopLevelPoolCidr
      Tags:
        - Key: Name
          Value: !Sub "${Name}-toplevel-pool"

IpamScopeIdには、プライベートスコープのIDを指定する必要があります。

今回はデフォルトのプライベートスコープを指定していますが、新規のプライベートスコープを作成し指定する事も可能です。

リージョナルプール

  IPAMRegionalPool:
    Type: AWS::EC2::IPAMPool
    Properties:
      AddressFamily: ipv4
      Description: "regional pool"
      IpamScopeId: !GetAtt IPAM.PrivateDefaultScopeId
      Locale: ap-northeast-1
      SourceIpamPoolId: !Ref IPAMTopLevelPool
      ProvisionedCidrs:
        - Cidr: !Ref RegionalPoolCidr
      Tags:
        - Key: Name
          Value: !Sub "${Name}-regional-pool"

Localeに、プールが属するリージョンを指定する必要があります。

今回はCloudFormationを実行するap-northeast-1を指定します。

開発者プール

  IPAMDevelopmentPool:
    Type: AWS::EC2::IPAMPool
    Properties:
      AddressFamily: ipv4
      AllocationMinNetmaskLength: !Ref DevelopMentPoolMinNetmask
      AllocationDefaultNetmaskLength: !Ref DevelopMentPoolDefNetmask
      AllocationMaxNetmaskLength: !Ref DevelopMentPoolMaxNetmask
      Description: "development pool"
      IpamScopeId: !GetAtt IPAM.PrivateDefaultScopeId
      Locale: ap-northeast-1
      SourceIpamPoolId: !Ref IPAMRegionalPool
      ProvisionedCidrs:
        - Cidr: !Ref DevelopmentPoolCidr
      Tags:
        - Key: Name
          Value: !Sub "${Name}-development-pool"

必要に応じて、開発者プールを設定します。(無くても問題ありません)

AllocationMinNetmaskLengthAllocationDefaultNetmaskLengthAllocationMaxNetmaskLengthで、割り当て可能なサブネットマスクの最小値/デフォルト値/最大値を指定します。

VPC

テンプレート全体

IPAMを利用するVPCテンプレートの全体を以下に記載します。

vpc.yaml

AWSTemplateFormatVersion: "2010-09-09"

# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------#
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: "VPC Configuration"
        Parameters:
          - Name
          - Env
          - VPCNetmask

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
  Name:
    Description: "Fill in the System Name of IPAM."
    Type: String
    Default: cm
  Env:
    Description: "Select the environment."
    Type: String
    Default: dev
    AllowedValues:
      - prd
      - stg
      - dev
  VPCNetmask:
    Description: "Fill in the Netmask Length of VPC."
    Type: Number
    Default: 16

# ------------------------------------------------------------#
# Resources
# ------------------------------------------------------------#
Resources:
  # VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Ipv4IpamPoolId: !ImportValue DevelopmentPoolId
      Ipv4NetmaskLength: !Ref VPCNetmask
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-vpc"

  # Internet Gateway
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-igw"
  IGWAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref IGW
  IGWRoute:
    Type: AWS::EC2::Route
    DependsOn: IGWAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref IGW

  # Subnet
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 0
        - !GetAZs
          Ref: AWS::Region
      VpcId: !Ref VPC
      CidrBlock: !Select [ 0, !Cidr [ !GetAtt VPC.CidrBlock, 4, 8 ]]
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-public-subnet-1"
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 1
        - !GetAZs
          Ref: AWS::Region
      VpcId: !Ref VPC
      CidrBlock: !Select [ 1, !Cidr [ !GetAtt VPC.CidrBlock, 4, 8 ]]
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-public-subnet-2"

  # Route Table
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-public-rt"
  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable
  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  # Network ACL
  PublicNetworkAcl:
    Type: AWS::EC2::NetworkAcl
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-public-nacl"
  PublicSubnet1NetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      NetworkAclId: !Ref PublicNetworkAcl
  PublicSubnet2NetworkAclAssociation:
    Type: AWS::EC2::SubnetNetworkAclAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      NetworkAclId: !Ref PublicNetworkAcl

以下、テンプレートのポイントを説明します。

VPC

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Ipv4IpamPoolId: !ImportValue DevelopmentPoolId
      Ipv4NetmaskLength: !Ref VPCNetmask
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-vpc"

Ipv4IpamPoolIdに、VPCが利用するIPAMプールのIDを指定する必要があります。

今回は、IPAMテンプレートで開発者プールIDを出力し、Ipv4IpamPoolIdで参照するようにしています。

またIpv4NetmaskLengthで、VPCのサブネットマスクを指定できます。

その場合、指定したIPAMプールのサブネットマスクの範囲の中(今回は16-24のいずれか)で指定する必要があります。

Subnet

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 1
        - !GetAZs
          Ref: AWS::Region
      VpcId: !Ref VPC
      CidrBlock: !Select [ 1, !Cidr [ !GetAtt VPC.CidrBlock, 4, 8 ]]
      Tags:
      - Key: Name
        Value: !Sub "${Name}-${Env}-public-subnet-2"

IPAMを利用したVPCを作成する場合、Subnetは自動で作成されないため、別途設定する必要があります。

その際にCidr関数を利用し、VPCのCIDRから自動でSubnetにCIDRを割り当てるように設定します。

なおCidr関数については、以下のブログをご参照ください。

デプロイ後の設定値

IPAMテンプレートをデフォルト値のままでデプロイすると、以下の設定でIPAMプールが構築されます。

またVPCテンプレートをデフォルト値のままでデプロイすると、以下の設定でVPC及びSubnetが構築されます。

最後に

今回は、VPC IP Address Manager (IPAM)をCloudFormationで実装し、VPCで利用する方法を紹介しました!

IPAMテンプレート自体は簡単に作れる一方で、VPCテンプレートはCidr関数を用いるというような工夫が必要になりますね。

IPAMをCloudFormationで実装し管理したいという方がいらっしゃれば、ぜひご参照ください。

以上、つくぼし(tsukuboshi0755)でした!