CloudFormationからはじめるSaaS生活

![cloudformation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/1ac2ae7d-b36b-66f4-a580-9e790892ee9d.png)

## CloudFormationについて

CloudFormationとは、AWSのリソースを簡単に構築することができる機能のことです。

YAML/JSONファイルにリソースの設定を書き込んで、AWSコンソールのCloudFormationでファイルをアップロードすると、
設定したリソースが作成されていきます。
もちろんCLIでもアップロードができます。

[AWS CloudFormation](https://aws.amazon.com/jp/cloudformation/)

これら一つのYAML/JSONファイルに設定された一連のリソース群のことをスタックと呼びます。
アップロードが完了すると、AWSコンソール上のCloudFormationではスタックの作成が始まります。

![product-page-diagram_CloudFormation.ad3a4c93b4fdd3366da3da0de4fb084d89a5d761.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/ddd3ffd3-932f-bd44-8dde-eb11460d44ee.png)

CloudFormationのテンプレートは単純にリソースを構築するだけではなく、
プロジェクトで利用されているAWSリソースの構成図としても役立ちます。

AWSコンソール上では把握しづらいリソース間の関係性も、
テンプレートファイル上で概要をつかむことができます（読める知識があれば）。


簡単にお試しできそうな記事がありました:point_down_tone2:
[【CloudFormation入門】5分と6行で始めるAWS CloudFormationテンプレートによるインフラ構築](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html)

## CloudFormationの基本的なパラメーター

CloudFormationを始めるにあたって理解しておいた方が良いパラメーターは以下の3つです。

- Parameters
- Outputs
- Resources

まず、テンプレートの冒頭はこんな感じになると思います。

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Your project name # 別に必須ではない
```

上に挙げているパラメーターはこの`Description`下に付け足して書いていきます。

ちなみにこのエントリーではYAMLでテンプレートを書いています。

### Resources

ResourcesにはAWSリソースを定義していきます。
それぞれのリソースは機能役割が異なるので、各々の設定方法が存在します。

リソースの定義は想定している構成でどのリソースを使うべきかを判断し上で、
以下のリストから探して試していくと良いです。

[AWS リソースおよびプロパティタイプのリファレンス](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)

#### 設定方法

全てのリソースについて説明することは不可能なので、
ここにはVPCとInternetGatewayの組み合わせをサンプルで記載します。

```yaml
Resources:
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      Tags:
        -
          Key: "Name"
          Value: "MyVPC"
  MyInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        -
          Key: "Name"
          Value: "MyInternetGateway"
  AttachGatewayToVPC:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref MyIGW
```

IPv4 CIDR ブロックを指定してVPCを作成し、インターネットゲートウェイ(IGW)を関連づけています。
VPCとIGWの参照には`Ref`組み込み関数（YAMLの短縮記法）を使います。

VPCとIGWを関連づけるためには`AWS::EC2::VPCGatewayAttachment`を利用します。
周知の通り、VPC内からインターネットへアクセスするにはIGWが必須です。
上記のサンプルでは基本的なVPCを設定しているだけです。

ここからサブネットやルートテーブル、EC2インスタンスをスタックに追加してあげると、
インスタンス内で開発を行えるところまでをCloudFormationで構築することができるようになります。

:point_down_tone2:のリソースを設定すればできます。

[AWS::EC2::Subnet サブネットの作成](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html)
[AWS::EC2::Instance EC2インスタンスの作成](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html)
[AWS::EC2::RouteTable ルートテーブルの作成](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route-table.html)
[AWS::EC2::SubnetRouteTableAssociation サブネットとルートテーブルの関連付け](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html)
[AWS::EC2::Route ルートテーブルとIGWの関連付け](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route.html)

### Parameters

Parametersでは同じテンプレート内で利用する値を設定することができます。

#### 設定方法

と言ってもはじめての人にはこの言葉では理解しづらいと思うので具体的に設定を見てみましょう。

```yaml
Parameters:
  S3BucketNameParameter:
    Type: String # パラメーターの型
    Default: 'Megalovania' # 初期値
    AllowedPattern: ^[a-zA-Z0-9]*$ # 正規表現に合致するものを許容します。
    AllowedValues: # enumのようにこの中の値を許容します。
      - 'Megalovania'
      - 'TheManWithTheMachineGun'
      - 'BlueEyesWhiteDragon'
```

`Default`ではCloudFormationでのスタック作成時に設定しなかった場合の初期値を設定します（AWSコンソール上からCFnスタック作成をするときにGUIで設定できる、けどしなかった場合の初期値）。
`AllowedPattern`と`AllowedValues`ではその初期値やあとで設定し直した時の値のルールを設定できます。
正規表現や想定されている値が数個しかない場合はこれらを使ってみると良いと思います。

#### 参照方法

参照はResourcesおよびこのあと登場するOutputsで行えます。
組み込み関数の`Ref`を使って参照します。

```yaml
# in case of Resources
Resources:
  MyBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref S3BucketNameParameter # 'Megalovania'

# in case of Outputs
Outputs:
  ExportedBucket:
    Description: "MyBucket"
    Value: !Ref MyBucket
    Export:
      Name: "ExportedBucket"
```

### Outputs

Outputsを使うと、同一リージョン内に作成された他のスタックで参照可能な値を設定したり、CloudFormationコンソール上にリリース担当者に参照してほしいリソースを表示したりすることができます。

#### 設定方法

```yaml
# スタックA

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  SampleVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      Tags:
        -
          Key: "Name"
          Value: "SampleVPC"
    
Outputs:
  ExportedVPC:
    Description: "SampleVPC" # 説明
    Value: !Ref SampleVPC # エクスポートする値
    Export:
      Name: "ExportedSampleVPC" # インポートするときに参照する名前
```

ここではVPCのVPC IDをエクスポートしています。

#### 参照方法

次にエクスポートした値を参照しながら別のスタックを作成します。
上記ではVPCをエクスポートしたので、そのVPCに対してIGWをアタッチしてみます。

参照時には`Fn::ImportValue`組み込みを利用します。
YAMLでは組み込み関数の短縮記法が使えるので、ここでは`!ImportValue`としています。

```yaml
# スタックB

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        -
          Key: "Name"
          Value: "MyInternetGateway"
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !ImportValue ExportedSampleVPC # ExportのNameで指定した名前から値を参照します。
      InternetGatewayId: !Ref MyInternetGateway
```

こうするとスタックAで作成されたVPCにスタックBで作成されたIGWが関連付きます。
スタック間を超えて参照したいリソースがあれば便利に使うことができます。

また、作成したスタックのOutputsはAWSコンソール上でも参照することができます。

<img width="876" alt="Screenshot 0032-02-12 at 11.14.13 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/2ffb26e8-4798-6eb5-dc19-9dc7c5548bcf.png">

例えば、

「ReactクライアントのビルドファイルをアップロードS3バケットはどれだったっけ？」

というようなときに、このCloudFormationコンソールのOutputsからバケット名が参照できればわざわざドキュメントにまとめる必要も無くなります。
せいぜい、「コンソールの出力を参照してください」と指示するくらいでしょう。

特に複雑に肥大したスタックなどには主要なものはOutputsにまとめておくと良いと思います。

## テンプレートのアップロードとスタック作成

テンプレートが書けてもスタックの作り方がわからなければ意味がありません。
ただテンプレートを流し込むだけなので全く難しいことなどはありませんが。。。

一応ざっくりと手順に触れておきます。

#### 1. CloudFormationコンソールへ入る

AWSコンソールでCloudFormationを開くとスタック一覧画面の上の方に「スタック作成」（Create stack）ボタンを見つけられると思います。

<img width="1139" alt="Screenshot 0032-02-12 at 11.54.04 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/20565c57-29fa-a271-20db-9b2e5ffa6894.png">

ドロップダウンになっているので「新しいスタック」（With new resources）を選択します。

#### 2. テンプレートファイルを選択

<img width="1028" alt="Screenshot 0032-02-12 at 11.54.31 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/923e7b94-af68-345e-3ace-45316b6a183e.png">

スタック作成画面では「既存のテンプレート」（Template is ready）から「テンプレートをアップロード」（Upload a template file）を選択し、テンプレートファイルYAML/JSONを選択して「次へ」（Next）を押します。

#### 3. スタック名を決める

<img width="1034" alt="Screenshot 0032-02-12 at 11.57.12 PM.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245704/4c6187eb-4ccc-6bd0-d1c1-438cd81c346a.png">

スタック名を記入して「次へ」（Next）を押します。


#### 4. 詳細設定（やりたい人だけ）

次のページの詳細設定等は省いて「次へ」（Next）を押します。
設定したいことがあればします。


#### 5. 確認とスタックの作成実行

確認画面が表示されるので、確認ができたら下部の「スタック作成」（Create stack）を押します。

するとスタックの作成が始まってステータスが`CREATE_IN_PROGRESS`となるのがわかると思います。
設定したリソースの量にもよりますが少しすればスタックが`CREATE_COMPLETE`になります。

問題があればエラーも出ますが、基本的には以上がスタック作成の基本的な流れです。
コンソール上でリソースが作成されているのを確認できると思います。

## おわり

以上の基本的なパラメーターについてとりあえず触れましたが、
CloudFormationを理解するには組み込み関数についても知る必要があります。

これは上でも触れた`!Ref`とか`!ImportValue`みたいなやつのことです。

組み込み関数には条件関数というものもあって、より柔軟にスタックテンプレートを作成することができるようになります。

今の職場では複数の環境（お客様が別々で要望も別々なので環境を分けている）がそれぞれの環境差異を持っているので各スタックテンプレートで組み立てているのですが、
そうは言っても類似した部分の共通簡略化は行いたくなるのでテンプレートの部分分岐をしたりします。
こういったときに条件関数が使えるので知っておいて損はないと思います。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

また、スタックはネスト化して表現することもできるので、「ネストされたスタック」についても知っておいた方が良いかもしれないです。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html