🚀

【AWS】 CloudFormationで基本的なシングル構成を自動構築する

2022/06/06に公開


はじめに

ご覧いただきありがとうございます。阿河です。

社内で技術検証をする機会が多いですが、同じようなAWS環境を建てる場面が度々発生していて無駄な時間が発生しているなと思ってます。

今回はCloudFormationを調べて一から書いてみました。CloudFormationのドキュメントを参照しながらシングル構成(パブリックサブネットにEC2 1台)を自動構築してみます。

対象者

  • AWSを運用中
  • 手作業でAWSリソースを作成するのが面倒な人

概要

(★)がついているセクションは、今回手を動かして頂く項目です。

  1. 今回のハンズオン構成
  2. VPCの自動構築/スタック作成方法(★)
  3. ネットワークまわりのリソース自動作成(★)
  4. EC2を自動作成してApacheテストページを表示(★)

各種公式ドキュメントを参照しながら進めていきます。自力でテンプレートを作成できるように、公式ドキュメントをどのように参照していくかに重点を置いた記事になっています。

事前準備

  • AWSアカウント作成
  • AdministratorAccessを付与したIAMユーザーの作成

1.今回のハンズオン構成

【構成】

  • VPC(パブリックサブネット×1)
  • EC2インスタンス

CloudFormationを利用して、シンプルなシングル構成を作成することがゴールです。
自動作成したEC2インスタンスにアクセスしてApacheのテストページを表示させます。

2.VPCの自動構築/スタック作成方法

まずVPCを作成します。
適当なフォルダに「vpc.yml」という名前でymlファイルを作成します。

vpc.yml

AWSTemplateFormatVersion: "2010-09-09"
Description: The template is vpc

Parameters:
  SystemName:
    Type: String
    Default: CFn-Test

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: 'true'
      EnableDnsSupport: 'true'
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub ${SystemName}-vpc

それぞれドキュメントと照らし合わせながら、ざっと説明していきます。

AWSTemplateFormatVersion: "2010-09-09"
Description: The template is vpc

Parameters:
  SystemName:
  ・・・・
  
Resources:
  VPC:
  ・・・・

使用するAWSリソースを記述するテンプレートを作成すれば、CloudFormationがそれらのリソースのプロビジョニングを代行してくれます。

テンプレートの構造

テンプレートの構造については、テンプレートの分析に記載があります。

まず冒頭で形式バージョン(AWSTemplateFormatVersion)と、簡単な説明(Description)を補足します。
形式バージョンは現時点で有効な"2010-09-09"を指定します。

パラメータ(Parameters)は、実行時にテンプレートに渡すことができる値です。
今回作成したテンプレートでは、「SystemName」というパラメータを作り、デフォルト値で「CFn-Test」を設定しました。
テンプレート内で!Subを用いて、下記のように読み替えます。

(ex. !Sub ${SystemName}-vpc)
⇒CFn-Test-vpc と読み替えます。

Resources(リソース)は、VPCやEC2などスタックに含めたいAWSリソースを宣言します。
VPCに関する細かい設定については、後述します。

その他にもセクションはありますが、必須なのはResourcesセクションです。

Resourcesセクション(VPC)

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: 'true'
      EnableDnsSupport: 'true'
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub ${SystemName}-vpc

VPCの設定について、内容を見ていきます。
まずこちらの設定は、公式の記述例をもとに変更を加えたものです。

お手持ちのブラウザから「VPC CloudFormation」と検索すれば、該当のページが出てくるはずです。

こちらのページにYAMLでの記述例が載っています。

Type: AWS::EC2::VPC
Properties: 
  CidrBlock: String
  EnableDnsHostnames: Boolean
  EnableDnsSupport: Boolean
  InstanceTenancy: String
  Ipv4IpamPoolId: String
  Ipv4NetmaskLength: Integer
  Tags: 
    - Tag

なので各プロパティの意味を確認しながら、どのプロパティが必要か、値をどう設定するかを検討します。

  • CidrBlock
    CIDR表記でのVPCのIPv4ネットワーク範囲を指します。普段AWSを触っている方であれば、お使いのリージョンにVPCがあると思うので、CIDRが被らないように設定ください。

  • EnableDnsHostnames
    VPCで起動されたインスタンスがDNSホスト名を取得するかどうかを示します。有効にすると、VPC内のインスタンスはDNSホスト名を取得します。
    trueに設定します。

  • EnableDnsSupport
    DNS解決がVPCでサポートされているかどうかを示します。trueにします。

  • InstanceTenancy
    特に専用ハードウェアでインスタンスの起動を実行する必要はないので、defaultを選択します。

  • Tags
    VPCにつけるタグ。

今回は上記の5つのプロパティを設定しました。
テンプレートの記述が終わりましたら、ファイルの保存をします。

ymlファイルをS3バケットにアップロード

適当なS3バケットを作成します。

そしてS3バケットにvpc.ymlをアップロードします。
S3⇒※該当のバケットを選択⇒※vpc.ymlファイルを選択すると、アップロードしたvpc.ymlファイルの詳細が確認できます。

オブジェクトURLをコピーしておきます。

CloudFormationでスタック作成

CloudFormationのページを開きます。
スタックの作成を押します。

テンプレートの保存先として、先程コピーしたオブジェクトURLを指定します。

任意のスタック名を入力します。
後程の工程でスタック参照を行いますので、整合性をとるために「CFn-Test-vpc」と名付けます。

次へを選択。

次のページもデフォルトのまま、次へ進みます。

最後に確認画面が出ますので、入力内容に間違いがないかを確認します。
問題なければ、スタックを作成をクリックします。

スタックが作成されていきます。
「CREATE_IN_PROGRESS」が「CREATE_COMPLETE」に移行するのを待ちます。

スタックが作成されました。
リソースタブを確認すると、VPCが作成されたことが分かります。

VPCのページに飛ぶと、実際にリソースが作成されていることが分かります。

これでVPC作成はできました。
このスタックは削除してかまいません。
※S3にアップロードしたymlファイルも併せて削除します。

次のセクションで、テンプレートにサブネットなどの設定を追加します。

3. ネットワークまわりのリソース自動作成

vpc.ymlのResourcesセクションに、インターネットゲートウェイを追加します。

InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${SystemName}-igw"


  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC 

ブラウザから「Internet Gateway CloudFormation」で検索をかけます。

インターネットゲートウェイのCloudFormation記述例を確認します。

タイプで "AWS::EC2::InternetGateway"を指定。
プロパティはTagsのみです。

上記の参考ページの最後あたりに、参照情報として「AWS :: EC2 :: VPCGatewayAttachmentリソースを使用して、インターネットゲートウェイをVPCに関連付けます」とあります。

インターネットゲートウェイのアタッチメントが必要です。

インターネットゲートウェイのIDは、組み込み関数!Refを使用して、先ほど作成したインターネットゲートウェイを指定します。Refでリソースの論理名を指定すると、そのリソースを参照するために通常使用できる値(物理ID)を返します。
同様にVPCを指定します。

これでインターネットとVPC間の接続を有効にします。

PublicSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-3a"
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet-a"

  
  PublicRouteTableA: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${SystemName}-public-route-a"

サブネット作成します。
今回は大阪リージョンを使用しています。

Webブラウザで「Subnet CloudFormation」でWeb検索をかければ、該当ページがでてきます。

必要なプロパティはこのあたりでしょうか。

  • AvailabilityZone(AZ)
  • CidrBlock(CIDR)
  • VpcId(VPC)
  • Tags(タグ)
    IPv6関連のプロパティは今回必要ないので、省きます。
    参考ページの最後まで読んでも、特に追加で参照するページは載っていないようです。

次のアクションとしては、ルートテーブルを作成することです。
Webブラウザで「RouteTable CloudFormation」でWeb検索をかけると、該当のページがあります。

必要なプロパティはVpcIDとTagsです。
ページの最後にAWS::EC2::Routeを参照するように案内があります。

PublicRouteA: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PublicRouteTableA 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 
  
  PublicSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PublicSubnetA 
      RouteTableId: !Ref PublicRouteTableA

「AWS :: EC2 :: Route」は、VPC内のルートテーブルでルートを指定します。
プロパティは多いですが、最低限のみ設定します。

  • DestinationCidrBlock(宛先の一致に使用されるIPv4CIDRブロック)
  • GatewayId(インターネットゲートウェイのID)
  • RouteTableId(ルートテーブルのID)

ルートテーブルの宛先を「0.0.0.0/0の宛先はインターネットゲートウェイ」とします。
参考ページの最後あたりに特に追加で参照すべき情報は載っていません。

現時点ではルートテーブルとサブネットは関連づいていません。

Webブラウザで「routetable subnet CloudFormation」とWeb検索をかけます。

AWS::EC2::SubnetRouteTableAssociationがあります。サブネットとルートテーブルを関連づけます。

プロパティはシンプルにサブネットIDとルートテーブルIDです。
参考ページの最後には、特に追加で参照すべきページは載ってません。

では、vpc.ymlのコードをまとめます

vpc.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is vpc

Parameters:
 SystemName:
   Type: String
   Default: CFn-Test

Resources:
 VPC:
   Type: AWS::EC2::VPC
   Properties: 
     CidrBlock: 10.0.0.0/16
     EnableDnsHostnames: 'true'
     EnableDnsSupport: 'true'
     InstanceTenancy: default
     Tags: 
       - Key: Name
         Value: !Sub ${SystemName}-vpc


 
 InternetGateway: 
   Type: "AWS::EC2::InternetGateway"
   Properties: 
     Tags: 
       - Key: Name
         Value: !Sub "${SystemName}-igw"


 InternetGatewayAttachment: 
   Type: "AWS::EC2::VPCGatewayAttachment"
   Properties: 
     InternetGatewayId: !Ref InternetGateway
     VpcId: !Ref VPC 

 
 PublicSubnetA: 
   Type: "AWS::EC2::Subnet"
   Properties: 
     AvailabilityZone: "ap-northeast-3a"
     CidrBlock: 10.0.0.0/24
     VpcId: !Ref VPC 
     Tags: 
       - Key: Name
         Value: !Sub "${SystemName}-public-subnet-a"

 
 PublicRouteTableA: 
   Type: "AWS::EC2::RouteTable"
   Properties: 
     VpcId: !Ref VPC 
     Tags: 
       - Key: Name
         Value: !Sub "${SystemName}-public-route-a"


 PublicRouteA: 
   Type: "AWS::EC2::Route"
   Properties: 
     RouteTableId: !Ref PublicRouteTableA 
     DestinationCidrBlock: "0.0.0.0/0"
     GatewayId: !Ref InternetGateway 
 
 PublicSubnetARouteTableAssociation: 
   Type: "AWS::EC2::SubnetRouteTableAssociation"
   Properties: 
     SubnetId: !Ref PublicSubnetA 
     RouteTableId: !Ref PublicRouteTableA


Outputs:
 VPC:
   Value: !Ref VPC
   Export:
     Name: !Sub ${AWS::StackName}-VPC


 PublicSubnetA:
   Value: !Ref PublicSubnetA
   Export:
     Name: !Sub ${AWS::StackName}-PublicSubnetA 

コードの最後にOutputsセクションを追加しています。
この後EC2をテンプレートから作成しますが、その際にvpc.ymlの「VPC」と「PublicSubnetA」を参照する必要があります。
スタック参照ができるように出力します。

実際の構成では、マルチAZでの運用/プライベートサブネットを用意することが多いかと思いますが、必要な場合はドキュメントを参照して構築ください。今回と近い手順で構築できるはずです。

テンプレートが完成したので、S3にアップロードします。
前回と同じ手順でCloudFormationスタックを作成しましょう。

それぞれ作成されたリソースを確認します。

VPC

インターネットゲートウェイ

パブリックサブネット

ルートテーブルとの関連付け

以上ネットワークまわりの自動構築ができました。

またoutputsによって出力もなされています。

次はEC2を自動構築していきます。

4. EC2を自動作成してApacheテストページを表示

新規で「ec2.yml」ファイルを作成します。

ec2.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: The template is ec2

Parameters:
  SystemName:
    Type: String
    Default: CFn-Test

  EC2ImageId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2


Resources:

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${SystemName}-ec2-sg
      GroupDescription: !Sub ${SystemName}-ec2-sg
      VpcId:
        Fn::ImportValue: !Sub ${SystemName}-vpc-VPC
      Tags:
        - Key: Name
          Value: !Sub ${SystemName}-ec2-sg

  
  EC2SecurityGroupIngressHttp:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref EC2SecurityGroup
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      CidrIp: 0.0.0.0/0
      

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageId
      InstanceType: t2.micro
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId:
            Fn::ImportValue: !Sub ${SystemName}-vpc-PublicSubnetA
          GroupSet:
           - !Ref EC2SecurityGroup

      UserData:
        Fn::Base64: |
          #!/bin/bash
          sudo yum update -y
          sudo yum -y install httpd
          sudo systemctl start httpd.service
          sudo systemctl enable httpd.service

      Tags:     
        - Key: Name
          Value: !Sub ${SystemName}-EC2

上から1つずつ見ていきます。

AWSTemplateFormatVersion: "2010-09-09"
Description: The template is ec2

Parameters:
  SystemName:
    Type: String
    Default: CFn-Test

  EC2ImageId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-kernel-5.10-hvm-x86_64-gp2

テンプレートの冒頭部分は、基本的にはvpcテンプレートと変わりありません。
ただしEC2作成時に最新のAmazon Linux2 AMIを利用したいので、こちらのページを参考にしました。AWS Systems Manager Parameter Storeから、最新のAMIを取得します。

 Resources:

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${SystemName}-ec2-sg
      GroupDescription: !Sub ${SystemName}-ec2-sg
      VpcId:
        Fn::ImportValue: !Sub ${SystemName}-vpc-VPC
      Tags:
        - Key: Name
          Value: !Sub ${SystemName}-ec2-sg

  
  EC2SecurityGroupIngressHttp:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref EC2SecurityGroup
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      CidrIp: 0.0.0.0/0

今回EC2を作成するにあたり、必要なのはセキュリティグループです。
※必要であれば適宜ロールの作成等も行ってください。

Webブラウザで「cloudformation SecurityGroup」とWeb検索します。
参考ページを確認します。

必要なプロパティは

  • GroupName
  • GroupDescription
  • VpcId
  • Tags
    です。

VPCIDについては、vpcスタックから出力された値を参照しています。
Fn::ImportValueは。別のスタックによってエクスポートされた出力の値を返します。

インバウンドルールは別途、追加します。

  • CidrIp (CIDR)
  • IpProtocol (使用プロトコル=TCP)
  • FromPort (80番ポート)
  • ToPort (80番ポート)
  • GroupId (セキュリティグループ)

上記のプロパティを指定します。

Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageId
      InstanceType: t2.micro
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId:
            Fn::ImportValue: !Sub ${SystemName}-vpc-PublicSubnetA
          GroupSet:
           - !Ref EC2SecurityGroup

      UserData:
        Fn::Base64: |
          #!/bin/bash
          sudo yum update -y
          sudo yum -y install httpd
          sudo systemctl start httpd.service
          sudo systemctl enable httpd.service

      Tags:     
        - Key: Name
          Value: !Sub ${SystemName}-EC2

EC2インスタンスを作成します。
Webブラウザから「Instance cloudformation」とWeb検索し、こちらのページを参照します。

大量のプロパティがありますが、今回は最低限のみ設定します。

  • ImageId (AMI)
  • InstanceType(インスタンスタイプ。t2.microを指定)
  • NetworkInterfaces(パブリックIPアドレスの動的割り当て設定に必要)
  • UserData(ユーザーデータ。Apacheを立ち上げるのに必要)
  • Tags

AMIは、Parametersで設定したAmazon Linux2 AMIを使用します。

NetworkInterfacesは、インスタンスに接続するネットワークインターフェースを指定します。AssociatePublicIpAddressでパブリックIPアドレスの動的割り当てを実現します。

また今回テストとしてApacheのテストページを表示させたいので、インスタンス起動時にApacheインストール/起動設定を行うようにします。Fn::Base64は通常UserDataプロパティを介して、AmazonEC2インスタンスにエンコードされたデータを渡すために使用されます。

ユーザーデータの設定方法は、こちらを参考にしました。

以上がテンプレートの説明です。

ymlファイルの保存が完了したら、ec2.ymlファイルをS3にアップロードを行います。
またCloudFormationのスタック作成の手順はVPCのときと同じですが、スタック名は「CFn-Test-ec2」としておいてください。

スタックの作成が完了したら、先に進みます。

作成されたリソースを確認してみましょう。

セキュリティグループ

インバウンド

アウトバウンド

設定どおり、80番ポートのインバウンド通信が許可されています。

EC2

1台無事に稼働しています。
パブリックIPアドレスがふられているので、アクセスしてみましょう。

Apacheのテストページが表示されました。
CloudFormationの画面で、スタックを削除・S3バケットのymlファイルを削除して終了です。

さいごに

今回は各種ドキュメントを参照しながら、CloudFormationでネットワークまわりとEC2を作成しました。

またCloudFormationをテーマとして記事を書きたいと思います。
お疲れ様でした!!

MEGAZONE株式会社 Tech Blog

Discussion