CloudFormationを使うならルールを決めておきましょう

# はじめに
すでに既出している内容ではありますが、今回を機に自分でも考え社内のルールに反映できたらと思います
内容的には、これから使う方やルールが決まっていない現場の方々向けです（新しい知識を得れるわけではありません）

さて、タイトルにも記載した通り AWS CloudFormation（以後、CFnとします） の テンプレート について話していきます
簡単におさらいですが、CFnはAWSのサービスリソースをテンプレートを用いてプロビジョニングや管理できるサービスです
YAML または JSON で記載されたテンプレートを用いてリソースをサクッと作成できて便利です
当たり前のことを声を大にして言いますが、~~人生において~~**ルールは大事** です


## どんなルールが必要？
個人的には リソース名の **命名規則** だけ決めておけばいいと思っています
命名規則を決めることは当たり前のことですが、、、決まっていないと何が起きるかを想定しておくことが大事です

### 環境名
まずどこの企業でも使っているであろう 環境名(Environment)です

各企業で決まっているとは思いますが、環境名に関しては以下のような感じで扱ってると思います

- stg, prod
- staging, production
- Staging, Production

どれも意味は通じますが、決まっていないと**個人の考えで決めてしまいがち**なため、企業で働くうえではルールを決めて置く方がよいでしょう

仮に環境名のルールが無い場合は、どんな問題が起きるかを考えてみます

#### 環境名のルールがない場合
「Aスタックで作成したALB用のセキュリティグループを他スタックでも使用したい場合」を例にあげてみます
テンプレートの内容は以下の通りです

```yaml:Aスタック
Parameters:
  Environment:
    Type: String
    AllowedValues:
      - Production
      - Staging

Resources:
ALBSecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    省略

Outputs:
  ALBSecurityGroup:
    Value: !Ref ALBSecurityGroup
    Export:
      Name: !Sub "${AWS::StackName}-ALBSecurityGroup-${Environment}"
```
パラメータで指定する`Environment` を Export名で使用し、環境ごとに分けて作成します
このリソースを他のスタックでも使う場合は、組み込み関数の `Fn::ImportValue` を使用して取得します

```yaml:他スタック
Resources:
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      ~他省略~
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          Description: "Allow from ALB of A stack"
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: 
            Fn::ImportValue:
              !Sub "${AStackName}-ALBSecurityGroup-${Environment}"
```
他のスタックで上記のリソースを使用したい場合は、必ず `Environment` を揃えなければなりません

これから作成するスタックは問題ないと思いますが、既存スタックの場合はどうでしょうか？
ルールが決まっていない場合、他の値が使用されていて取得できない可能性があります
（別のパラメータを用意すれば対応できますが、そんなことしたくないですよね）

他スタックで作成したセキュリティグループを既存スタックで取得するかどうかはさておきｗ
今から全て揃えるかについては、問題が起きていないのであればそのままにしておくのがこの業界？ですし、、、

**我々にはそんな時間がないのでございます(　･`ω･´)ｷﾘｯ**
（CFnの論理ID/リソース名を変更する場合、リソースを再作成するので調査とか対応手順とか考えるの面倒よね）

というわけで、環境名は統一しておきましょう

### ◯◯ケース
次に命名規則でよく使われている？◯◯ケースについてです

代表的なものは以下でしょうか？（他のについては知りませんし、調べたくありません）
- キャメルケース （camelCase）
- スネークケース（snake_case）
- ケバブケース（kebab-case）

どれを使うかはケースバイケースかなと思いますが、個人的には以下のようにしています

- 論理名(ID)
    - キャメルケース（アッパーキャメルケース）
    ドキュメントを見ると、アッパーキャメルケース で記載しているのが多い？（一部除く）
- リソース名
    - ケバブケース
    ハイフンで繋げた方がキャメルケースより見やすい
    リソース名にアンダースコアを含めれないサービスもあるため(s3バケット名とか)

ということで、セキュリティグループを例にあげると私は以下のように書きます

```yaml:sample
Resources:
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${Service}-${Environment}-ALB
      ~他省略~
```

論理IDに関しては、組み込み関数の `Ref` を使用して取得することがほとんどなので、環境名のようなイレギュラーは発生しないため名前の付け方は自由度が高いと思います
リソース名に関しても、この値を使って他スタックから参照することもないので自由度が高いと思います

そのため論理IDとリソース名を決める際は以下を意識して決めるといいと思います
これを行うことで**対象リソースを見つけやすくなります**
- 論理ID
CFnが見る値なので、**テンプレートを見た時に何用なのか** が分かるようにする
- リソース名
Webコンソールなどで確認した際に、**どのサービスの何用なのか** が分かるようにする

自由度が高いからこそルールが大事です

### Export名
さて最後にExport名についてです
Export名はユニークにする必要があるため疑似パラメータの `${AWS::StackName}` を使うのが一般的です

```yaml:sample
Outputs:
  ALBSecurityGroup:
    Value: !Ref ALBSecurityGroup
    Export:
      Name: !Sub "${AWS::StackName}-ALBSecurityGroup-${Environment}"
```
※環境名で記載したものと同じです

Outputsセクションでの論理IDは、対象リソースの論理IDと同じでも動作上は問題ありません
そのため、同じにしておくことでどのリソースを出力するかがわかりやすくなります

sample に記載したように、疑似パラメータの `${AWS::StackName}` のあとにリソース名を指定しておけば、どのスタックの何用なのかが分かりやすくなりますし、環境名を追加することでより細かく分かります
知っている人にとっては当たり前ですが、こちらも認識を揃えておきましょう


## さいごに
短い内容でしたが、個人的に重要な部分を記載しました
知っている方にとっては当たり前の話にはなってしまいましたが、**CloudFormationにおいてルールが大事** なことが少しでも伝わったのなら幸いです（ルールはCFnだけの話ではないけどねっ）