【AWS】CloudFormationテンプレートの作成効率化【CloudFormation】

## はじめに
CloudFormationテンプレートでデプロイ作業を行っていると、テンプレートの記載ミスによりうまく実行できない場合があります。そのエラーを修正して再度実行してみてもまた別のエラーが発生して再度修正と実行をするなどしてリリース作業に思わぬ時間がかかってしまいます。
今回の記事ではCloudFormationテンプレートをデプロイする前に正しく定義を作成するためにはどのようにすればよいかを記載しました。

【この記事で説明する内容】
①CloudFormationテンプレートの書き方(変数や関数の使用)
②CloudFormationテンプレートの静的解析
③CloudFormationテンプレートの分け方

## CloudFormationテンプレートの書き方(変数や関数の使用)
CloufFormationのテンプレートには作成したいリソースの定義を記載しますが、その定義の中には反映する環境に依存するアカウントIDやプレフィックスの文字列、その他テンプレート内に繰り返し登場する固定の文字列があります。
これを毎回手動で更新していては更新漏れやミスによりエラーにつながることがあります。これを回避するために変数や関数を使用します。
### ①パラメータの使用(Parameters)
・CloudFormationテンプレート実行(スタック作成)時に選択する変数を設定します。
・リソースの名前につけるプレフィックス(接頭語)を定義してスタック作成時に選択することで環境ごとにプレフィックスの異なるリソースを作成することができます
・これがないと環境ごとにCloudFormationテンプレートファイルを環境ごとに作るなどの手間が増えてしまいます。
・複数の環境や
(定義例)
・環境毎に異なるプレフィックスを定義する場合に使われる環境プレフィックスを定義しています
```yml
Parameters:
  EnvironmentType:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - stg
      - prd
```
(呼び出し例)
・呼び出しには Fn::Sub 関数を使用します
```yml
Resources:
  MyS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: 
        Fn::Sub: "${EnvironmentType}-MyS3Bucket"
```
(定義項目)
|項目 |説明  |
|---|---|
|Type  |パラメータのデータ型を指定します。一般的なタイプにはString、Number、Listなどがあります  |
|Default  |パラメータのデフォルト値を指定します  |
|AllowedValues  |許可される値のリストを指定します。これにより、指定された値以外のものが入力されるのを防ぎます  |

### ①Mappings変数
(定義例①)
・Mappingsは階層立てて定義することが可能です
・この定義例ではスタックが作成されたリージョンごとのインスタンスのAMIを定義しています
```yml
Mappings:
  RegionMap:
    us-east-1:
      AMI: ami-12345678
    us-west-2:
      AMI: ami-98765432
```
(呼び出し例)
・呼び出しには Fn::FindInMap 関数を使用します
・定義したRegionMapの階層は3階層まであるため、3階層目のAMIまでを指定しています。
　※第2階層のリージョン名についてはAWS CloudFormationによって事前定義された疑似パラメータが設定されており、自動的にスタックが作成されるリージョンの値が入ります(変数を使用せず固定値で指定しても良いです)
```yml
Resources:
  MyEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMI]
```


## CloudFormationテンプレートの静的解析
・CloudFormationテンプレートは上記の変数設定などで編集していきますが、その定義の整合性のチェックを目視で行うのは慣れるまでは大変です。そこで静的解析ツールを使用して自動的にチェックしてスタック作成を実行する前に誤りに気が付けるようにしておきたいです。
・CloudFormationテンプレートの静的解析にはcfn-lintというツールを使用して行います。
### cfn-lintの使い方
・ここではVisualStudioCode(VSCode)でテンプレートの編集を行うことを前提にしています
#### ①cfn-lintのインストール
VSCodeのターミナルから以下のインストールコマンドを実行して下さい
```
pip3 install cfn-lint
```
インストール完了後にインストール先のパスが表示されますのでこのパスを控えておいてください。
![Cfn-lintインストール.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/620071bf-8baf-baa4-ec60-0032a3acd977.png)


#### ②VSCode拡張機能インストール
・VSCodeの拡張機能の検索で「cloudformation」と入力するとcfn-lintが出てくるのでそれをインストールします
![Cfn-lint.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/201fff71-61f9-ef67-be5b-fda5922f2072.png)
拡張機能の設定からCfn-lintのインストール先のパスを設定します※pipインストールした結果で出てきたパス
![パス設定1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/0e14e604-ae34-6022-2b8c-82f6aec868e4.png)

![パス設定2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/5f9cd709-234c-a3cf-a2be-da5ef600f873.png)



#### ③リンターエラー検出結果の確認と修正
・エラーがあるところはアンダーラインとメッセージでどのようなエラーがあるかを教えてくれます
・これを修正すればスタック作成時のエラー発見と再実行が避けられるはずです
![リンターエラー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/4ea24a59-f761-85ef-19c1-ba6f77f03c29.png)


### 補足：その他補助ツール
リンター以外の補助ツールについてはVSCodeの拡張機能であるCloudFormationがオススメです
![その他補助ツール.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/dbb7e211-2013-0de5-0fa5-3e3bd2d37141.png)
こちらは入力し始めた文字から入力候補を予測して提案してくれるのでテンプレート作成をスムーズに行えます
![入力補助.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192949/960d1dee-c758-40ee-cc5b-5793a72518fd.png)

## CloudFormationテンプレートの分け方
・CloudFormationテンプレートにはLambdaやGlueなど複数のリソースの定義を含められるため1つの環境に含まれるリソースを全て1つのテンプレートに含めることもできますが、ある程度の単位で分割することで保守性を上げることもできます。
・どの単位で分割するかの考え方はチームやPJの考え方次第ですが、一般的な分け方とそのメリットデメリットに関しては下記のURLのAWSブラックベルトの資料を参照してください

https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AWS-CloudFormation-Dev-Test-Deploy_1231_v1.pdf