何かと便利なCloudFormationを触ってみた

# 目次
[1. はじめに](#1-はじめに)
[2. 用語](#2-用語)
[3. やりたいこと](#3-やりたいこと)
[4. 作成するリソース](#4-作成するリソース)
[5. やってみた](#5-やってみた)
[6. まとめ](#6-まとめ)
[7. 参考](#7-参考)
# 1. はじめに
こんにちは。
AWSの魅力の沼にハマり出したインフラエンジニアです！
最近、AWSの環境を複製する機会が何回かありました。
AWS環境が増える度に、環境構築をするのは正直めんどくさい。。。
そうだ！CloudFormationを利用しよう！
ということで、CloudFormationを触ってみました。

# 2. 用語
:::note info
CloudFormationとは
:::
CloudFormationはプログラミング言語やテキストファイルを使用してAWSリソースを自動で構築するサービスです。
所望のAWS環境をテンプレート化しておくことで、同じ環境を作成する時間を削減することができます。

:::note info
スタックとは
:::
スタックは、単一のユニットとして管理できる AWS リソースのコレクションです。 
スタックを作成、更新、削除することで、リソースのコレクションを作成、更新、削除できます。 
スタック内のすべてのリソースは、スタックの AWS CloudFormation テンプレートで定義されます。

# 3. やりたいこと
今回はCloudFormationを利用し、簡単な環境を作りEC2インスタンスを立ててみたいと思います。
AWSの構成図は、以下になります。（すごく簡単な構成で恐縮です。）
![qiita用-環境構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/ee037c68-4564-1625-93ff-a7f7d12b0ee0.png)


# 4. 作成するリソース
* **VPC** 
* **サブネット（パブリックサブネット / プライベートサブネット）** 
* **EC2** 
* **インターネットゲートウェイ**
* **セキュリティーグループ**

# 5. やってみた
それでは、CloudFormationを利用してEC2インスタンスを作成していきたいと思います。
## 5-1. テンプレートを準備
テンプレート内では、作成するリソースの詳細設定を記述します。
ex) EC2インスタンスのインスタンス名、インスタンスタイプ、インスタンスのサイズ等
```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  AWS-Environment-Template
  
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups: #各リソースのパラメータを管理
      - Label: #Labelでどのリソースのパラメータなのか区別
          default: "VPC Definition"
        Parameters:
          - VPCCIDR
          - DNSSupport
          - DNSHostNames
          - VPCName

      - Label:
          default: "InternetGateway Definition"
        Parameters:
          - IGWName

      - Label:
          default: "Subnet Definition"
        Parameters:
          - PublicSubnet1aName
          - PublicSubnet1aCIDRBlock
          - PrivateSubnet1aName
          - PrivateSubnet1aCIDRBlock
          - PublicSubnet1cName
          - PublicSubnet1cCIDRBlock
          - PrivateSubnet1cName
          - PrivateSubnet1cCIDRBlock

      - Label:
         default: "RouteTable Definition"
        Parameters:
          - PublicSubnetRouteTableName
          - PrivateSubnetRouteTableName

      - Label:
         default: "Define Security Group For EC2 Instance"
        Parameters:
          - EC2SecurityGroupName

      - Label:
         default: "EC2 Instance Definition"
        Parameters:
          - EC2InstanceName
          - EC2ImageId
          - EC2InstanceType
          - EC2KeyName

    ParameterLabels: #ParameterLabelsではParametersで定義したパラメータの説明を記載
      VPCCIDR:
        default: "VPC CIDR"
      DNSSupport: 
        default: "Whether DNS resolution is supported - true or false"
      DNSHostNames:
        default: "Whether to get the DNS host name - true or false"
      VPCName:
        default: "VPC Name"
      IGWName:
        default: "IGW Name"
      PublicSubnet1aName:
        default: "Public Subnet 1a Name"
      PublicSubnet1aCIDRBlock:
        default: "CIDR Block"
      PrivateSubnet1aName:
        default: "Private Subnet 1a Name"
      PrivateSubnet1aCIDRBlock:
        default: "CIDR Block"
      PublicSubnet1cName:
        default: "Public Subnet 1c Name"
      PublicSubnet1cCIDRBlock:
        default: "CIDR Block"
      PrivateSubnet1cName:
        default: "Private Subnet 1c Name"
      PrivateSubnet1cCIDRBlock:
        default: "CIDR Block"
      PublicSubnetRouteTableName:
        default: "Public Subnet  RouteTable Name"
      PrivateSubnetRouteTableName:
        default: "Private Subnet RouteTable Name"
      EC2SecurityGroupName:
        default: "Security Group Name For Instance"
      EC2InstaNcename:
        default: "Instance Name"
      EC2ImageId:
        default: "ami-09038cccc3722883e"
      EC2InstanceType:
        default: "Instance Type"
      EC2KeyName:
        default: "Key Name"
      
Parameters: #各パラメータの値を定義
  VPCCIDR:
    Type: String
    Default: "10.0.1.0/24"
    
  DNSSupport:
    Type: String
    Default: "true"
    
  DNSHostNames:
    Type: String
    Default: "true"
  
  VPCName:
    Type: String
    Default: "environment-name-vpc"

  IGWName:
    Type: String
    Default: "environment-name-igw"
    
  PublicSubnet1aName:
    Type: String
    Default: "environment-name-public-subnet-1a"

  PublicSubnet1aCIDRBlock:
    Type: String
    Default: "10.0.1.0/26"

  PrivateSubnet1aName:
    Type: String
    Default: "environment-name-private-subnet-1a"

  PrivateSubnet1aCIDRBlock:
    Type: String
    Default: "10.0.1.64/26"

  PublicSubnet1cName:
    Type: String
    Default: "environment-name-public-subnet-1c"
  
  PublicSubnet1cCIDRBlock:
    Type: String
    Default: "10.0.1.128/26"

  PrivateSubnet1cName:
    Type: String
    Default: "environment-name-private-subnet-1c"

  PrivateSubnet1cCIDRBlock:
    Type: String
    Default: "10.0.1.192/26"
  
  PublicSubnetRouteTableName:
    Type: String
    Default: "environment-name-public-rtb"

  PrivateSubnetRouteTableName:
    Type: String
    Default: "environment-name-private-rtb"

  EC2SecurityGroupName:
    Type: String
    Default: "environment-name-sg"

  EC2InstanceName:
    Type: String
    Default: "environment-name-az"
  
  EC2ImageId:
    Type: String
    Default: "ami-072bfb8ae2c884cc4"
  
  EC2InstanceType:
    Type: String
    Default: "t2.micro"

  EC2KeyName:
    Type: String
    Default: "test-key"

Resources: #Resourcesでは各リソースの詳細設定を定義

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: !Ref DNSSupport
      EnableDnsHostnames: !Ref DNSHostNames
      Tags:
        - Key: Name
          Value: !Ref VPCName
 
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref IGWName
  
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref IGW
      VpcId: !Ref VPC
 
  PublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnet1aCIDRBlock
      Tags:
        - Key: Name
          Value: !Ref PublicSubnet1aName

  PrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnet1aCIDRBlock
      Tags:
        - Key: Name
          Value: !Ref PrivateSubnet1aName

  PublicSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PublicSubnet1cCIDRBlock
      Tags:
        - Key: Name
          Value: !Ref PublicSubnet1cName

  PrivateSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnet1cCIDRBlock
      Tags:
        - Key: Name
          Value: !Ref PrivateSubnet1cName

  PublicSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref PublicSubnetRouteTableName

  PrivateSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Ref PrivateSubnetRouteTableName

  PublicSubnetRouteSetting:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW

  AttachPublicSubnetRouteTable1a:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicSubnetRouteTable
      SubnetId: !Ref PublicSubnet1a

  AttachPublicSubnetRouteTable1c:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicSubnetRouteTable
      SubnetId: !Ref PublicSubnet1c

  AttachPrivateSubnetRouteTable1a:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateSubnetRouteTable
      SubnetId: !Ref PrivateSubnet1a

  AttachPrivateSubnetRouteTable1c:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateSubnetRouteTable
      SubnetId: !Ref PrivateSubnet1c
  
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPC
      GroupDescription: "My-NetWork"
      SecurityGroupIngress:
        - IpProtocol: -1
          CidrIp: 10.100.0.0/16
      Tags:
        - Key: Name
          Value: !Ref EC2SecurityGroupName

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageId
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref EC2KeyName
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          GroupSet: 
            - Ref: "InstanceSecurityGroup"
          SubnetId: !Ref PublicSubnet1a
      Tags:
        - Key: Name
          Value: !Ref EC2InstanceName
```

:::note info
ParameterGroupとは
:::
パラメータをまとめて定義することができます。
ParameterGroup内でさらに分類したい場合は、Labelを使用することで分類できます。

:::note info
Parametersセクションとは
:::
パラメータを使用すると、スタックを作成または更新する度にテンプレートにカスタム値を入力できます。

## 5-2. スタックを作成
CloudFormation Management Consoleから「スタック」➡「スタックの作成」をクリック。
テンプレートの準備では、「テンプレートの準備完了」を選択し、「テンプレートファイルのアップロード」を選択します。
上記を選択することで、テンプレートファイルをアップロードすることが可能になります。
（テンプレートは上記を使用しました。）
![qiita用-スタックの作成1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/4447b9e7-b7e3-cc69-2ea3-1c4ece3bd2b2.png)

次に、スタックの詳細設定を行います。
今回スタックの名前は、「test-stack」としました。
パラメータは、状況にあった値に変更してください。
今回は、デフォルトの値で作成しました。
![qiita用-スタック作成2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/b51f9a69-5ced-6360-0c57-47f56877a505.png)
上記のパラメータのセクションで、テンプレートで定義したパラメータが表示されていることがわかります。
また、VPC / インターネットゲートウェイのように、Labelで区切ったリソースごとにパラメータが表示されていることもわかります。
![qiita用-スタック作成3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/631c4713-eb3f-e720-f476-a1f730d170d3.png)
パラメータのセクションで入力されている値は、テンプレートのParametersのDefaultに設定した値が表示されます。
![qiita用-スタック作成4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/68d4a741-90e5-9c00-c54e-a63917fd65e8.png)
スタックオプションでは、任意でタグを追加してください。
アクセス許可では、デフォルトのままで設定します。
スタックの失敗オプションでは、「すべてのスタックリソースをロールバックする」を選択します。
![qiita用-スタック作成5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/5eb5e4e3-fad4-b431-bb4d-ff7bc3be5d51.png)
以下は、すべてデフォルトのままでOK。
![qiita用-スタック作成6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/34317c51-50b3-c34d-c0cc-b63850d5bbd7.png)
最後にレビューとなります。
上記で設定したパラメータが確認できます。
設定に問題がなければ、「送信」をクリックします。
![qiita用-スタック7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/896b5136-b801-afa1-f793-d07eaf9644a6.png)
![qiita用-スタック8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/6163eb06-444e-b6f4-c468-74a4a62ed796.png)
送信されると、CoudFormationのスタックが実行され、テンプレートで設定したリソースが作成されます。

## 5-2. 作成したリソースを確認
スタックの実行が正常に完了した場合、「CREATE_COMPLETE」が表示されます。
![スタック状況.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/c54ca943-f75f-c649-2c9a-9e2b8e34e1d5.png)
また、イベントでは作成したリソースの状況を細かく確認することができます。![スタックイベント1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/35d7cd6f-4c58-ccc9-9904-9539a7d42451.png)
実際に作成されたリソースも確認してみます。
![インスタンスの状態.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730351/87d43ca6-3903-fa96-dd69-e6de83a98442.png)
テンプレートで指定したVPC / サブネット / EC2が作成されていることがわかります。


# 6. まとめ
CloudFormationでリソースの一元管理をすることで、管理者の作業負担と時間短縮に貢献できる。
CloudFormationで作成したリソースすべてを削除したい場合は、対応するスタックを削除することで可能。
（残しておきたいリソースを選択することで、削除しないことも可能。）
今回は簡単な構築をCloudFormationで作成しましたが、次回はより複雑な構築を行いたいと思います。
皆さんもとても便利なCloudFormationを活用して、作業負担と時間短縮をしていきましょう！！！


# 7. 参考
* [AWS CloudFormationとは](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html)
* [Amazon Elastic Compute Cloud リソースタイプのリファレンス](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_EC2.html)