はじめてのCloudFormation

#はじめに#


**CloudFormation**を学習し始めて、エラーラッシュに見舞われながら覚えたことを書いています。
対象読者は同じにようにテンプレートエラーに悩んでいる方AWS初学者です。
qita初投稿ですので、何か不備等があればご指摘いただけると助かります。

##参考##
この記事はAWS初学者を導く体系的な動画学習サービス
**AWS CloudTech**の課題カリキュラムで作成しました。
https://aws-cloud-tech.com



##CloudFormationについて##
構成したいAWSリソースやその細かい設定を**JSON、YAML**といった記述方式に則ってファイル(テンプレート）に記述し、それを読み込ませることで複数のAWSリソースを同時に起動できるしくみ。ひとつのテンプレートで生成されるAWSリソース群を**スタック**と呼ぶ。

テンプレートの型は複数のセクションからなるが(以下template.yml)、記述が必須なのは、**AWSTemplateFormatVersion**と**Resource**の部分のみ。前者はテンプレートのバージョンを表しているだけなので、ユーザーは後者の**Resource**セクションに構築したいAWSサービスを書式に則って定義していく。

```yml:template.yml
AWSTemplateFormatVersion: 2010-09-09　＃記載必須
Description: ---
Metadata:

Parameters:

Mappings:

Conditions:

Resources:　#記載必須

Outputs:

```

###_Resource_セクションの書式について###
``` yml:application.yml
Resources:　　#以下のセクションではAWSリソースを定義する宣言
  Logical ID:　　#テンプレート内で使う固有の名前。他のリソースを定義するときに参照したい場合などに使う。
    Type: Resource type　　　#AWSリソース名をここに定義する。　
    Properties:　　　　　　　　#AWSリソースの細かい設定をPropertiesとして個々に指定。
      Set of properties　　　＃どんなPropertyがあるかは公式ドキュメントTemplate Referenceを参照する。

```
#####例) VPCを定義する場合#####
AWS公式ドキュメントの[_Template Reference_](https://docs.aws.amazon.com/en_us/AWSCloudFormation/latest/UserGuide/template-reference.html)上でVPCのテンプレートを探す。
VPCのテンプレートは[EC2 resource type reference](https://docs.aws.amazon.com/en_us/AWSCloudFormation/latest/UserGuide/AWS_EC2.html)タブ内にある[AWS::EC2::VPC](https://docs.aws.amazon.com/en_us/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html)ページ内にある。これを元に自分の設定したい値を定義していく。本例のようにリソース名「VPC」として探すとで他のリソースと関連して定義されている点に注意。

```yml:network.yml
Type: AWS::EC2::VPC　　
Properties: 
  CidrBlock: String　　#セミコロンの後は半角スペース開けてValueを記述
  EnableDnsHostnames: Boolean
  EnableDnsSupport: Boolean
  InstanceTenancy: String
  Tags: 
    - Tag
```

##_Properties_セクションについて##
各_Property_は全て_Key: Value_形式で記述。
_Property_の設定の仕方は_Template reference_の_Properties_のセクションで確認できる。
_Value_の前は半角スペース開けないとエラーになるので注意

_Properties_を記述する際は、_Template reference_で定義されている**_Rquired_**、**_Type_**、**_Update_requires_**の３つに気を付ける。

**_Rquired_**
_Property_には設定が必須のものと、そうでないものがあり、それを以下の３パターンとして定義している。

- **_Required: Yes_**　設定しないとエラーになる_Property_。
- **_Required: No_**　設定しないとなんらかのデフォルト値が勝手に設定される_Property_。
- **_Required: Conditional_**　他の設定との兼ね合いで必須だったり不要だったりする_Property_。ただ、具体的にどの
設定と関連しているかは明示されていないこともあるので注意。

**_Type_**
_Property_の_Value_をどのデータ型で記述するか。基本は**_Type、String、Boolean、List of Tag、integer_**などだが、特定のIDや入れ子で何か指定する場合がある。
**_List of Tag_**の場合は以下のようにリスト形式で記述する必要がある。

```
  Tags: 
    - ListItem1
    - ListItem2
```

**_Update requires_**
スタックを更新したとき、この_Property_を変更した場合にどうなるか

- **_Update requires: No interruption_** リソース自体はそのままで_Property_部分のみ変更
- **_Update requires: Replacement_** リソースごと交換

データベースなどにおいてリソースごと交換が必要な_Property_を変更した場合は、保存したデータごと_Resource_が一旦削除されて作り直しになるので注意。

予め設定できる_Value_が限定されているものは以下のように**_Allowed values_**として明記されている。
例）EC2インスタンスのテナンシーを　専用｜デフォルト(共有)｜専有　から選択する例
_InstanceTenancy:　
Allowed values: dedicated | default | host_

##定義が必要な_Resource_について##
マネジメントコンソール上ではウィザード内の一連の流れの中で一緒に作成しているリソースでも個別に定義する必要がある。
例：ロードバランサーの起動には、最低限、以下の_Resource_を個別で定義する必要がある。


- Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
- Type: "AWS::ElasticLoadBalancingV2::Listener"- 
- Type: "AWS::ElasticLoadBalancingV2::TargetGroup"

##組み込み関数について##
_Properties_の値を記述する際に、直接、値を入力するのではなく、外部から参照した情報を値としたい場合に使用する。

####Ref関数####
テンプレート内の他のリソースを参照に使う関数
**書式**
　!Ref 論理ID　
   例）同テンプレート内で定義した別の_Resource MyVpc_のIdを取得する例

```　yml:network.yml
  PublicSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref MyVpc 
　　　　　　　~　~　略　~ ~
```
####GetAtt関数####
他のリソースの属性を取得する場合に使う関数。
**書式**
　!GetAtt 論理ID.属性名

####その他の関数####
参考：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html




##OutputとImportValue###
通常は一つのテンプレートに構築したいリソースを全てまとめて記述することはせず、ライフサイクルやアプリケーション、ネットワーク、セキュリティなどのレイヤーごとにテンプレートを分ける。よって、別ファイルで定義した値を参照したい場合は、_Output_と_ImportValue_を使う。

#####Ouputsセクションの書式　(出力元)#####

``` yml:network.yml
Outputs:　　＃以下に出力したいリソースを定義
  PublicSubnet1:　#任意の論理ID。Resourceセクションで定義したものと同じでもOK　
    Description: PublicSubnet-AZ1a　#説明書き
    Value: !Ref PublicSubnet1　＃出力したいリソースを記載。
    Export:
      Name: MyPublicSubnet1　＃出力した時の名前(任意)
```


#####ImportValueの書式(出力した値を使う時)#####

``` yml:application.yml
  WebServer1:
    Type: AWS::EC2::Instance
    Properties:
      ImageId:
        "ami-0ca38c7440de1749a"
       #  ~~~~~ 中略 ~~~~~~ #
      SubnetId: !ImportValue PublicSubnet1 ＃Outputsで出力した名前
```

_Parameter_の活用
_Property_の値を固定値ではなく変数として定義するようなもの。_Parameters_セクションで定義した値を_Property_の_Value_としてRef関数で代入する。テンプレートをアップロードする際に、任意の値に都度変更できる。


```yml:application.yml
Parameters:
  InstanceTypeParameter:　#パラメータの名前(任意)
    Type: String　　　　　　＃パラメータのデータ型
    Default: t2.micro　　 ＃パラメータのデフォルト値
    AllowedValues:　　　　　#代入可能な値をリストで表示
      - t2.micro
      - m1.small
      - m1.large
    Description: Enter t2.micro, m1.small, or m1.large. Default is t2.micro.
```
参考：https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html


##エラーについて##
まずはVSCodeの拡張機能を使って、エディター上でエラーを自動検出できる体制を作るのが大事。ただ、VSCode上でエラーが出てなくても以下のようなエラーに出くわすこともあるので注意。何れの場合も、エラーメッセージをちゃんと読んでテンプレートの該当箇所を_Template reference_の規則に従って修正することが大事。

- マネジメントコンソールでテンプレートファイルをアップロードした際にエラーメッセージがでる。
- マネジメントコンソールでアップロード後、「デザイナーで表示」を実施する。記述に不備がでるとエラーが出る。
- 「デザイナー」で表示した際にエラーがなくても「スタックを作成」するとエラーになることがある。
- 無事テンプレートに従って_Resource_の作成が始まるも途中でエラー。イベントタブにエラー内容が表示される。