LoginSignup
5
1

More than 3 years have passed since last update.

CloudFormationで汎用的な構成を作る

Last updated at Posted at 2021-03-04

この記事では、CloudFormationで様々な環境構築に転用できそうな、ベースとなる「基本のだし」のような構成を作成します。

仕事柄、様々な製品のデモを行うことが多いのですが、AWS学習の狙いとしてはCloudFormationテンプレートのストックを溜めておき、デモのシナリオや製品の動作要件に応じて、ささっと環境を作れるようになるのが当面の目標であり、願望です(^^
※構成やテンプレートの書き方等、至らない点も多々あると思いますがご容赦ください・・・・

構成図

image.png

通信要件

  • AWS東京リージョン(ap-northeast-1)固定
  • Public SubnetとPrivate Subnetを1つずつ作成
  • それぞれのSubnetにEC2インスタンスを1つずつ作成
  • AMIはAmazon Linux 2、インスタンスタイプはt2.micro固定
  • Private SubnetのEC2インスタンスは、Public Subnetに設置したNAT Gatewayを介してインターネット接続
  • Public Subnetに設置されたEC2インスタンスには、Elastic IPを設定する
  • Security Groupは、SSHインバウンド通信のみ許可する

CloudFormationテンプレート

今回は以下の4つのStackで環境構築を行いました。
備忘録のため、テンプレートにやや多めにコメントを入れています。
- stack_Network.yml
- stack_NATgateway.yml
- stack_RouteTable.yml
- stack_Instance.yml

テンプレートを分けた理由

ルーティングは、今後汎用的に作るであろう構成によって柔軟に変更できるようにしたいのと、NAT GatewayとElastic IPおよびEC2インスタンスは、Runしたままだと課金が絡んでくるため、不要時はStackごと削除できるようにテンプレートを分離しました。

stack_Network.yml
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  # Create a VPC
  myVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/21
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: myVPC

  # Create an Internet Gateway
  internetGW:
      Type: AWS::EC2::InternetGateway
      Properties:
        Tags:
          - Key: Name
            Value: internetGW

  # Attach the internet gateway to the VPC
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref myVPC
      InternetGatewayId: !Ref internetGW  

  # Create a Public Subnet
  publicsubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref myVPC
      CidrBlock: 10.0.0.0/24
      Tags:
        - Key: Name
          Value: PublicSubnetFromCF

  # Create a Private Subnet
  privatesubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref myVPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: PrivateSubnetFromCF

  # Create a Security Group allowing Inbound SSH only
  SecGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: mySGdesc
      GroupName: mySGName
      VpcId: !Ref myVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: mySG

# List Outputs for cross reference
Outputs:
    VPC:
      Value: !Ref myVPC
      Export:
        Name: VPCId
    inetGW:
      Value: !Ref internetGW
      Export:
        Name: internetGWName
    Subnet1:
      Value: !Ref publicsubnet
      Export: 
        Name: Subnet1Name
    Subnet2:
      Value: !Ref privatesubnet
      Export: 
        Name: Subnet2Name        
    SG1:
      Value: !GetAtt SecGroup.GroupId
      Export:
        Name: SG1Name

ここまでで
- VPC
- Internet Gateway
- Subnet
- Security Group
の作成完了です。

次に、NAT Gatewayの設定を行います。

stack_NATgateway.yml
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  # Create a NAT Gateway in Public Subnet
  NATGW:
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId: !GetAtt EIpNATGW.AllocationId # Associate EIP with NATGW
      SubnetId: !ImportValue Subnet1Name # Associate NATGW with the Public Subnet
      Tags:
        - Key: Name
          Value: NATGW
  EIpNATGW:
    Type: AWS::EC2::EIP # Allocate an Elastic IP with NAT Gateway
    Properties:
      Domain: vpc

Outputs:
    NatGW:
      Value: !Ref NATGW
      Export:
        Name: NATGWId

ここまでで
- NAT Gatewayの作成
が完了しました。

次は、
- Public SubnetがInternet Gatewayを経由してインターネット接続を行うためのルートテーブル作成およびルートの設定
- Private SubnetがNAT Gatewayを経由してインターネット接続を行うためのルートテーブル作成およびルートの設定
を行います。

stack_RouteTable.yml
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  # Create a Route Table for Public Subnet
  RouteTablePublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !ImportValue VPCId
      Tags:
        - Key: Name
          Value: RouteTablePublic

 # Attach the Route Table to Public Subnet
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref RouteTablePublic
      SubnetId: !ImportValue  Subnet1Name

# Create a Route from Public Subnet to Internet
  routeINET:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTablePublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !ImportValue internetGWName

  # Create a Route Table for Private Subnet
  RouteTablePrivate:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !ImportValue VPCId
      Tags:
        - Key: Name
          Value: RouteTablePrivate

 # Attach the Route Table to Private Subnet
  PrivateSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref RouteTablePrivate
      SubnetId: !ImportValue  Subnet2Name

# Create a Route from Private Subnet to NATGateway
  routeNATGW:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTablePrivate # Associate NATGW with RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !ImportValue NATGWId # Property Name must be NatGatewayID when NAT Gateway is the next hop.

Outputs:
    RouteTableName:
      Value: !Ref RouteTablePrivate
      Export:
        Name: RouteTablePrivate # NATGW needs to refer this RouteTable Name

ここまでで、
- Route Tableの作成およびルートの設定
- が完了しました。

最後に、Public SubnetとPrivate SubnetのそれぞれでEC2インスタンスを起動します。

stack_Instance.yml
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  # Create a Public EC2 instance 
  PublicEC2:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: mykey01 # Set keypair
      ImageId: ami-09d28faae2e9e7138 # Set Amazon Linux 2 free AMI
      NetworkInterfaces: 
        - AssociatePublicIpAddress: true # Attach IPv4 Public Address
          DeviceIndex: 0
          GroupSet: [!ImportValue SG1Name] # Refer external SG, need to set as List
          SubnetId: !ImportValue Subnet1Name # Refer external Subnet
      InstanceType: t2.micro
      Monitoring: false
      Tags:
        - Key: Name
          Value: PublicEC2fromCF
  EIPPublic:
    Type: AWS::EC2::EIP #Allocate an EIP with Public EC2
    Properties:
      Domain: vpc
      InstanceId: !Ref PublicEC2

  # Create a Private EC2 instance 
  PrivateEC2:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: mykey01 # Set keypair
      ImageId: ami-09d28faae2e9e7138 # Set Amazon Linux 2 free AMI
      NetworkInterfaces: 
        - AssociatePublicIpAddress: true
          DeviceIndex: 0
          GroupSet: [!ImportValue SG1Name] # Refer external SG, need to set as List
          SubnetId: !ImportValue Subnet2Name # Refer external Subnet
      InstanceType: t2.micro
      Monitoring: false
      # SecurityGroupIds:
      Tags:
        - Key: Name
          Value: PrivateEC2fromCF

ハマった点

AWS Management Consoleでの操作はわかっても、CloudFormationでの記法がわからない点が多々あり、多くの時間を費やしました。
- EC2インスタンスにPublicIPv4を割り当てる設定の記法がわからず、特にGroupSetにSecurity Groupを設定する際はリストの形で渡さなくてはならない点
- Elastic IPをEC2インスタンスやNAT Gatewayに割り当てる際、Type::EC2::EIPをどのインデントに書けばいいのかがわからなかった点
- Public SubnetからInternetへのルーティングを記載するRoute Tableは、CloudFormationでは、VPC DefaultのRoute Tableを参照することはできない(別のRoute Tableを作らないといけない) < これが最もハマった(参考URL)
- NAT GatewayをNext Hopにするルートを記載する際、Property NameはGatewayIdではなく、NatGatewayIdを使用しなくてはならない点

感想

AWSもCloudFormationもほぼ初見で、あれこれ調べながらやりましたが、非常に勉強になりました。今後のAWS学習もぜひCloudFormationとセットで進めていきたいです。Qiita投稿も初めてだったので読みづらかったかもしれません。今後少しずつ改善したいと思います。

その他感想としては、
- LogicalIDやOutputなどの名前付けが難しい
- 当初長大なテンプレートを作ってしまい、Stack実行時のエラーのデバッグが大変になり、考えた結果テンプレートを4つに分けることにしたこと。
- テンプレートを修正し、エラーを確認し、ドキュメントを調べて修正し、また試す。という繰り返し作業は(大変ですが)面白かったこと

最後に

この記事はAWS初学者を導く体系的な動画学習サービス
「AWS CloudTech」の課題カリキュラムで作成しました。
https://aws-cloud-tech.com

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1