CloudFormation事始め


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

みなさんこんにちは。
おでんの美味しい季節がやってきましたね。

筆者は最近初めてCloudFormation(以下CFnと略)を利用してみました。
そこで本記事は備忘録として、あわよくば誰かの参考になればと思い執筆しました。

はじめに

テンプレート管理のベストプラクティスとしては、一つのテンプレートにすべてのリソースを記述して管理するのではなく、レイヤとライフサイクルを観点にリソース(スタック)を分割してテンプレートを管理するようです。

参考:[AWS Black Belt Online Seminar] AWS CloudFormation 資料及び QA 公開
https://aws.amazon.com/jp/blogs/news/webinar-bb-aws-cloudformation-2020/

そこで、今回のハンズオンはベストプラクティスに則り、最も依存度の低いネットワークレイヤのテンプレートを作成することで、今後さらに上位のレイヤのテンプレートを作成してくための土台作りを行うという事始め的位置づけとなります。

構成

本記事で構築するスタックは以下のような構成となります。

  • 可用性のためにマルチアベイラビリティゾーン構成
  • Public-SubnetはInternet gatewaysを経由してインターネットにアクセス
  • Private-SubnetはNAT gatewaysを経由してインターネットにアクセスしつつ、インターネットからのインバウンドトラフィックは遮断してセキュリティを保つ
  • NAT gatewaysは単一のAZ障害に対する可用性を確保するため、マルチアベイラビリティゾーンで配置

[構成図]

templateの作成

公式ドキュメントの参照

今回は公式ドキュメントを参照しながらテンプレートを作成していきました。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html

テンプレートリファレンス -> リソースとプロパティのリファレンス
file

今回必要だったネットワーク関連のリソースは"Amazon EC2"配下にリファレンスのページがありました。泥臭いやり方ですが、ブラウザで’ctrl+F’で必要なリソース名を検索してそのページに飛んだりしてサンプルコードと説明を読みtemplateを作成していきました。
file

テンプレート

実際にテンプレートを作成したものが以下です。
※もし、以下のテンプレートをご使用になる場合について、テンプレートはそのままお使いいただけるはずですが、お使いのアカウントの環境次第ではVPCの上限数やCidrの競合などが理由でスタック作成に失敗するかもしれません。ご注意ください。また、NAT Gatewaysは料金が発生しますので検証などでお使いになられた後、不要になりましたらスタックの削除をお忘れなきようお願いします。

AWSTemplateFormatVersion: "2010-09-09"
Description: Network resources

Resources:
# ----------------------------------------------------------------------------#
# VPC
# ----------------------------------------------------------------------------#
# create VPC
  VPC: 
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: "true"
      EnableDnsSupport: "true"
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: cfn-tom-vpc
# ----------------------------------------------------------------------------#
# subnet
# ----------------------------------------------------------------------------#
# create Public SubnetA
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref VPC
      Tags: 
        - Key: Name
          Value: cfn-tom-public-subnetA
# create Public SubnetC
  PublicSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1c
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref VPC
      Tags: 
        - Key: Name
          Value: cfn-tom-public-subnetC
# create Private SubnetA
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: 10.0.2.0/24
      VpcId: !Ref VPC
      Tags: 
        - Key: Name
          Value: cfn-tom-private-subnetA
# create Private SubnetC
  PrivateSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1c
      CidrBlock: 10.0.3.0/24
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: cfn-tom-private-subnetC
# ----------------------------------------------------------------------------#
# internet gateway
# ----------------------------------------------------------------------------#
# create Internet Gateway
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: cfn-tom-internet-gateway
# ----------------------------------------------------------------------------#
# attaches an internet gateway to a VPC
# ----------------------------------------------------------------------------#
# create Attachment
  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref IGW
      VpcId: !Ref VPC
# ----------------------------------------------------------------------------#
# ElasticIP for NatGateway
# ----------------------------------------------------------------------------#
# create ElasticIP1
  EIP1:
    Type: AWS::EC2::EIP
    DependsOn: VPCGatewayAttachment
    Properties:
      Domain: vpc
      Tags: 
        - Key: Name
          Value: cfn-tom-EIP1
# create ElasticIP2
  EIP2:
    Type: AWS::EC2::EIP
    DependsOn: VPCGatewayAttachment
    Properties:
      Domain: vpc
      Tags: 
        - Key: Name
          Value: cfn-tom-EIP2
# ----------------------------------------------------------------------------#
# NatGateway
# ----------------------------------------------------------------------------#
# create NatGatewayA
  NatGatewayA:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP1.AllocationId
      SubnetId: !Ref PublicSubnetA
      Tags: 
        - Key: Name
          Value: cfn-tom-NatGatewayA
# create NatGatewayC
  NatGatewayC:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIP2.AllocationId
      SubnetId: !Ref PublicSubnetC
      Tags: 
        - Key: Name
          Value: cfn-tom-NatGatewayC
# ----------------------------------------------------------------------------#
# route table
# ----------------------------------------------------------------------------#
# create Public RouteTable
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags: 
        - Key: Name
          Value: cfn-tom-public-route-table
      VpcId: !Ref VPC
# create Private RouteTableA
  PrivateRouteTableA:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags: 
        - Key: Name
          Value: cfn-tom-private-route-tableA
      VpcId: !Ref VPC
# create Private RouteTableC
  PrivateRouteTableC:
    Type: AWS::EC2::RouteTable
    Properties:
      Tags: 
        - Key: Name
          Value: cfn-tom-private-route-tableC
      VpcId: !Ref VPC
# ----------------------------------------------------------------------------#
# add route
# ----------------------------------------------------------------------------#
# PublicRoute
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW
      RouteTableId: !Ref PublicRouteTable
# PrivateRouteA
  PrivateRouteA:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayA
      RouteTableId: !Ref PrivateRouteTableA
# PrivateRouteC
  PrivateRouteC:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayC
      RouteTableId: !Ref PrivateRouteTableC
# ----------------------------------------------------------------------------#
# Association of Route Tables with subnets
# ----------------------------------------------------------------------------#
# Public SubnetA
  AssociationWithPublicSubnetA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetA
# Public SubnetC
  AssociationWithPublicSubnetC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnetC
# Private SubnetA
  AssociationWithPrivateSubnetA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableA
      SubnetId: !Ref PrivateSubnetA
# Private SubnetC
  AssociationWithPrivateSubnetC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableC
      SubnetId: !Ref PrivateSubnetC

スタックの作成

CloudFormationを検索して開きます。
file

スタック -> スタックの作成 -> 新しいリソースを使用(標準)
file

テンプレートの準備完了 -> テンプレートファイルのアップロード -> (ファイルをアップロードしたら)次へ
file

今回、テンプレートでパラメータは定義していないためパラメータなしと表示されます。
そのまま任意のスタック名を入力 -> 次へ
file

スタックオプションの設定はデフォルトのまま次へ(画像省略)
レビューも問題なければそのまま送信を押してください(画像省略)

以下のような画面に遷移するかと思います。
イベントタブで各リソースのステータスが時系列(降順)で表示されます。
file

以下の画像のように、左側のスタックのステータスが"CREATE_COMPLETE"と表示になったら作成成功です。
file

不要になった場合は、右上の"削除"からリソースを丸ごと削除できます。
テンプレートで作成したリソースは基本的にテンプレートで管理するため手動で削除や変更などしないようにします。
※手動でリソースを変更してしまった場合にドリフト検出という機能が使えるそうで、詳しい機能についてはまた検証します。

課題

実は今回のテンプレートでは、Outputsセクションを記述できていません。Outputsセクションによって出力した値は別のテンプレートから参照することができるため、次回はこのOutputsセクションを今回のテンプレートに追加するところから行っていきたいと考えています。
また、そのほかにもMetadataセクションやParameterLabelsセクションを記述していません。これらは、ユーザからの入力を受け付けることでテンプレートにより柔軟さを持たせることができるらしく、次は利用してみようと思います。

おわりに

また次回お会いしましょう!!

Last modified: 2023-11-15

Author