インフラ構築を簡単にするAWS CloudFormation

皆さん、こんにちは！　おすすめの牛は短角牛、ITエンジニアのSkyfox（なかやま）です！
この記事を読んで、AWS CloudFormationを使ってくださると嬉しいです！

# **この記事について**

### **対象者**
- 手作業でAWS環境構築するのに疲れた方
- CloudFormationに興味がある方
- AWSリソースをマネージドコンソールで作成したことがある方

### **本記事のゴール**
- CloudFormationの概要がわかること
- CloudFormationを使用してみようかという気持ちになること

# 背景
AWS環境をマネージドコンソールで作成したことがある皆さん、こんなことを思ったことはありませんか？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/e2b26497-ff9b-9d50-c183-74a6c909db49.png)

今回の記事では、このようなお悩みを解決します。

# CloudFormationの概要

### AWS CloudFormationとは

AWS CloudFormationは、AWSのインフラ構成をコードで作成・管理してくれるサービスです。
いわば、あなたの作業チームの
「 **インフラ構築・管理をしてくれる、<INS> もう1人のメンバー**</INS> 」
です！


### メリット・デメリット
<font color="IndianRed">**メリット**</font>
- <font color="IndianRed">インフラ構成をコード化できる</font>
CloudFormationを使用すると、AWSリソースの設定をコードとして管理できます。
これにより、リソースの構成が管理しやすくなり、誰でも同じ環境を構築することができるようになります。

&#13;&#10;

- <font color="IndianRed">作業効率がUPする</font>
インフラ構成をコード化してしまえば、複数のAWSリソースを一度に自動で作成、更新、削除することができます。
手動の設定作業が不要になるため、作業効率が大幅に上がります。

&#13;&#10;

- <font color="IndianRed">環境・用途ごとに柔軟に対応できる</font>
AWSリソースを作成するとき、開発環境や本番環境などに合わせて、リソースの設定をすることができます。

&#13;&#10;

- <font color="IndianRed">変更履歴を管理できる</font>
リソースの更新や変更履歴がCloudFormationで管理できるため、誰が何を変更したかがわかるようになっています。
そのため、問題が起きたときに原因を特定しやすくなります。

<br>

<font color="LightSkyBlue">**デメリット**</font>
- <font color="LightSkyBlue">エラーメッセージがわかりにくい</font>
コード側でエラーが発生したとき、エラーメッセージがわかりにくく、原因を特定することが難しいことがあります。

&#13;&#10;

- <font color="LightSkyBlue">コード化には慣れが必要</font>
コードを書くルールがあるため、CloudFormationを使い始めた方だと、はじめはコード作成に慣れが必要です。
（最近は身近なAIもあるので、簡単な構成であれば、コードを書かずにできるやも）

&#13;&#10;

- <font color="LightSkyBlue">複雑なコードにすると読みづらい</font>
コード化するときに内容が複雑になると、読みづらく、保守が難しくなることがあります。

&#13;&#10;

- <font color="LightSkyBlue">デプロイに時間がかかることがある</font>
多くのリソースを管理している場合、コードの変更やリソースの削除に時間がかかることがあります。


# CloudFormationを使用するための前知識

前章までで、CloudFormationの概要や良さは、わかったかと思います。
それでは、実際にみなさんが使えるようになるため、ここからは必要な知識をインプットしていきましょう！

### 主なキーワード

- **テンプレート**
    - リソースの設定や構成など、インフラ構成に必要な情報を定義するファイル
    - YAML形式 or JSON形式で書きます
    - <font color="Gray">※前章まで「コード化」と言っていたのは、これのことです</font>

&#13;&#10;

- **スタック**
    - テンプレートをもと作成されたAWSリソースのコレクション（集合体）
    - スタック単位でリソースの作成・更新・削除をおこない、一括管理ができます
    - <font color="Gray">※テンプレートをもとに作成されたリソースたちを、まとめて管理する単位としても呼ばれています</font>

&#13;&#10;

- **変更セット**
    - スタックを更新する前に、その内容をプレビューする機能
    - どのリソースが追加・更新・削除されるかを一目で確認できます


# リソース作成までの流れ

流れはたったの3ステップです。

1. テンプレートの作成
2. デプロイ（スタックの作成）
    ※AWS マネージドコンソール or CLI
3. スタックをもとに設定されたAWSリソースが自動的に生成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/8093a93e-43f3-f5b4-1b9e-d754d43d1f31.png)

<br>

それでは、詳しくみていきましょう！

### テンプレートの書き方

#### 覚えておきたい5セクション

テンプレートを作るときは、作りたいシステム構成を思い描いてから作りましょう！
今回はyaml形式で書いていきます。

テンプレートは、以下の7つのセクションで構成されています。
重要なのは、赤枠がついた5つです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/1fb93733-5c8b-a0b1-905b-7b5d280bc8ca.png)

- Parameters
    - ユーザーがスタック作成時に入力する値を設定するセクション
    - スタックを作成・更新する度に設定値を変えたい場合に便利です

&#13;&#10;

- Mappings
    - 3階層「Key, Key, Value」で、マップ定数を定義するセクション
    - 開発環境・本番環境などの環境によって、異なる値を設定したい場合に便利です
    - 「Parameters」や「Conditions」セクションと組み合わせて使われます

&#13;&#10;

- Conditions
    - リソースや設定値の作成条件を定義するセクション
    - IF分岐する場合などに使います
        - ▶︎ ある環境ではリソースを作成し、他の環境では作成しないといった柔軟な設定ができます

&#13;&#10;

- Resources
    - 実際に作成・管理するAWSリソースを記入するセクション
    - メインとなるセクションで、テンプレートには必須で書きます

&#13;&#10;

- Outputs
    - スタック外（別のテンプレート）にリソース情報をアウトプットするセクション
    - 例えば、Aスタックで作成したS3バケットを、Bスタックで使いたいときに便利です

#### テンプレート例
必要なセクションについて分かったところで、実際のテンプレートを見てみましょう。
今回のテンプレートは、1回のデプロイでLowCost環境、もしくはHighPerformance環境を立ち上げる構成になっています。
dev環境・prod環境の違いは以下の通りです！

- 共通基盤:
    - 1つのVPC（CIDR: 10.0.0.0/16）に構築
    - パブリックサブネットを使用し、インターネットゲートウェイを通じて外部接続を確立
    - SSH（ポート22）とHTTP（ポート80）のアクセスを全世界から許可

- 環境ごとの違い:
    - 低コスト環境（LowCost）: 
        - 低コストを重視
        - インスタンスタイプ: t2.micro、動的パブリックIP使用
    - 高性能環境（HighPerformance）: 
        - 高性能を重視
        - インスタンスタイプ: t2.large、固定のElastic IP使用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/4a20ee78-d7e0-36d3-51c9-468a99fa0d1a.png)

``` yaml:PracticeStack.yaml
# [SPEC] テンプレートのフォーマットバージョン
AWSTemplateFormatVersion: '2010-09-09'
# [SPEC] テンプレートの説明
Description: 'CloudFormation Template for Network and EC2 with Environment Parameter, Mappings, and Conditions'

# [SPEC] パラメータの定義
Parameters:
  # [SPEC] 環境タイプ
  EnvironmentType:
    Type: String
    Default: 'LowCost'
    AllowedValues:
      - LowCost
      - HighPerformance
    Description: 'Environment type (LowCost or HighPerformance)'

# [SPEC] マッピングの定義
Mappings:
  # [SPEC] 環境タイプに応じた設定値のマッピング
  EnvConfigMap:
    LowCost:
      InstanceType: 't2.micro'   # [SPEC] 低コスト環境用のインスタンスタイプ
      CidrBlock: '10.0.2.0/24'   # [SPEC] 低コスト環境用のCIDRブロック
    HighPerformance:
      InstanceType: 't2.medium'   # [SPEC] 高性能環境用のインスタンスタイプ
      CidrBlock: '10.0.1.0/24'   # [SPEC] 高性能環境用のCIDRブロック

# [SPEC] 条件の定義
Conditions:
  # [SPEC] 環境が高性能環境かどうかの条件
  IsHighPerformanceEnvironment: !Equals [!Ref EnvironmentType, 'HighPerformance']

# [SPEC] リソースの定義
Resources:
  # [SPEC] VPCの作成
  PracticeVpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: '10.0.0.0/16'                 # [SPEC] 固定のCIDRブロック
      Tags:
        - Key: Purpose
          Value: Practice

  # [SPEC] インターネットゲートウェイの作成
  PracticeIgw:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
        - Key: Purpose
          Value: Practice

  # [SPEC] インターネットゲートウェイをVPCにアタッチ
  AttachIgwToVpc:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref PracticeVpc                    # [SPEC] アタッチするVPCの参照
      InternetGatewayId: !Ref PracticeIgw        # [SPEC] インターネットゲートウェイの参照

  # [SPEC] パブリックサブネットの作成
  PracticePublicSubnet:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref PracticeVpc                    # [SPEC] サブネットを作成するVPCの参照
      CidrBlock: !FindInMap [EnvConfigMap, !Ref EnvironmentType, CidrBlock]  # [SPEC] 環境に基づいたCIDRブロック
      AvailabilityZone: !Select [0, !GetAZs '']  # [SPEC] AZの取得
      MapPublicIpOnLaunch: true                  # [SPEC] パブリックIPの自動割り当て
      Tags:
        - Key: Purpose
          Value: Practice

  # [SPEC] ルートテーブルの作成
  PracticeRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref PracticeVpc                    # [SPEC] ルートテーブルを作成するVPCの参照
      Tags:
        - Key: Purpose
          Value: Practice

  # [SPEC] ルートテーブルとIGWの関連付け
  RouteTableToIgw:
    Type: 'AWS::EC2::Route'
    Properties:
      RouteTableId: !Ref PracticeRouteTable      # [SPEC] ルートを設定するルートテーブルの参照
      DestinationCidrBlock: '0.0.0.0/0'         # [SPEC] 宛先CIDRブロック
      GatewayId: !Ref PracticeIgw               # [SPEC] InternetGatewayの参照

  # [SPEC] サブネットとルートテーブルの関連付け
  SubnetRouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PracticePublicSubnet         # [SPEC] サブネットの参照
      RouteTableId: !Ref PracticeRouteTable       # [SPEC] ルートテーブルの参照

  # [SPEC] EC2のセキュリティグループの作成
  PracticeEc2SecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: 'SecurityGroup for EC2'   # [SPEC] セキュリティグループの説明
      VpcId: !Ref PracticeVpc                     # [SPEC] セキュリティグループを作成するVPCの参照
      SecurityGroupIngress:                       # [SPEC] インバウンドルール
        - IpProtocol: 'tcp'                          # [SPEC] プロトコル
          FromPort: 22                               # [SPEC] 開始ポート
          ToPort: 22                                 # [SPEC] 終了ポート
          CidrIp: '0.0.0.0/0'                        # [SPEC] CIDR
        - IpProtocol: 'tcp'
          FromPort: 80
          ToPort: 80
          CidrIp: '0.0.0.0/0'
      Tags:
        - Key: Purpose
          Value: Practice

  # [SPEC] Elastic IPの作成（高性能環境のみ）
  PracticeEip:
    Type: 'AWS::EC2::EIP'
    Condition: IsHighPerformanceEnvironment                 # [SPEC] 高性能環境の場合にのみ作成
    Properties:
      Domain: vpc                                # [SPEC] VPCに紐付け

  # [SPEC] EC2インスタンスの作成
  PracticeEc2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: !FindInMap [EnvConfigMap, !Ref EnvironmentType, InstanceType]  # [SPEC] 環境に基づいたインスタンスタイプ
      SubnetId: !Ref PracticePublicSubnet        # [SPEC] サブネットの参照
      ImageId: 'ami-00c79d83cf718a893'           # [SPEC] 固定のAMI ID
      Tags:
        - Key: Name                                # [SPEC] EC2インスタンスの名前
          Value: 'practice-ec2'
        - Key: Purpose
          Value: Practice

  # [SPEC] EIPをEC2インスタンスにアタッチ（高性能環境のみ）
  AttachEipToEc2:
    Type: 'AWS::EC2::EIPAssociation'
    Condition: IsHighPerformanceEnvironment                 # [SPEC] 高性能環境の場合にのみアタッチ
    Properties:
      InstanceId: !Ref PracticeEc2Instance       # [SPEC] EC2インスタンスの参照
      EIP: !Ref PracticeEip                      # [SPEC] EIPの参照

# [SPEC] 出力
Outputs:
  InstanceId:                                      # [SPEC] 出力名
    Description: 'ID of the EC2 instance'             # [SPEC] 出力の説明
    Value: !Ref PracticeEc2Instance                   # [SPEC] 出力する値
  PublicIP:
    Description: 'Public IP of the EC2 instance'
    Value: !GetAtt PracticeEc2Instance.PublicIp
```


# スタック（AWSリソース）のデプロイ方法

今回は、一例として前章のテンプレートを使用し、AWSマネージドコンソールでデプロイする方法を簡単に書きます。
前章のテンプレートを使用する場合は、事前にコードを貼り付けた、「PracticeStack.yaml」ファイルを作成してください！

**＜今回の構成＞**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/4a20ee78-d7e0-36d3-51c9-468a99fa0d1a.png)

#### ステップ1
CloudFormationのコンソールを開き、「スタックの作成」の「新しいリソースを使用（標準）」（画像の赤枠）をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/f676e448-4ee6-de7c-a6db-15bad6bcea94.png)

#### ステップ2
「スタックの作成」ステップでは、今回すでに作成してあるテンプレートを使いたいので、画像のように設定していきます。
テンプレートの設定は、「テンプレートの指定」項目の「ファイルの選択」ボタン（画像の赤枠）をクリックし、指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/2e06bb8e-cf64-bffd-a77e-9696bcf55b31.png)

#### ステップ3
「スタックの詳細を指定」ステップでは、以下の設定をします。

- 設定
    - スタック名を提供
        - スタック名：　※適したスタック名をつけてください
    - パラメータ
        - ※ テンプレートの内容により変化します
            - テンプレートの「Parameters」セクションで設定した項目が表示されます
            - ここで今回作成したいスタックの設定値を選択していきます

:::note warn
注意
前章のテンプレートを使用している方は、LowCost環境とHighPerformance環境で、コストが異なるので注意です！
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/f0e20872-8afa-694a-1193-98b27b852bed.png)

#### ステップ4
「スタックオプションの設定」ステップでは、適宜設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/1c24a9ea-f288-a194-089b-4d6d779cc94e.png)

#### ステップ5
「確認して作成」ステップで、設定した項目を確認し、大丈夫でしたら、「送信」ボタンをクリックします。

#### ステップ6
画像のように、CloudFormationと作成したリソースが以下のように確認できましたら、完成です。

- CloudFormationのスタック確認
    - 作成したスタックの「ステータス」項目が「CREATE_COMPLETE」になっている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/331acc4e-2559-8f28-8a91-096b112f9c91.png)

&#13;&#10;

- 作成したリソース（今回はEC2など）の確認
    - 作成したかったリソースができているか確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/dcb86984-b9e4-37bb-605d-8720d27817c9.png)



# Appendix
### 変更セットについて
今回は詳細には書きませんが、スタック更新時に以下の赤枠のように、変更があるリソースを「変更セットのプレビュー」項目で表示してくれます。
これで変更した内容が合っているか確認してから、スタックを更新することができます。
（「間違って必要なリソースを消してしまった」ということも防げます。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752776/bfee717f-2a7a-da52-f50c-23eafebfd703.png)

# さいごに
AWS CloudFormationのイメージは、つきましたでしょうか？

この記事を導入としてもらい、
AWSリソースを一括作成・管理する
「 **インフラ構築・管理をしてくれる、<INS> もう1人のメンバー**</INS> 」
として、AWS CloudFormationで快適なインフラ構築を実施してもらえればと思います。


#### 使用イラスト
- イラスト「Loose Drawing」
    - https://loosedrawing.com/