柔軟なCFnを書くために使いこなしたいパラメータや関数

実務でCFnを触る機会があったのでこの辺りを抑えていけばある程度柔軟なCFnがかけるよって言うところをまとめてみました。

### 目次

- よく使う組み込み関数
- Conditionsとは
- Exportとimportvalueの使い方

### 用語

- Template・・・実行前のCFnファイルそのもの
- Stack・・・CFn実行によって作成されたリソース群のこと

### よく使う組み込み関数

とりあえずこの3つだけ理解しておけば大体の環境差分に対応するCFnが書けるのではないかと思います。

- **`Fn::FindInMap`**
    - Mappingsで指定した値を呼び出すのに使う。あらかじめ固定値はMappingにまとめるようにしておくと見やすくなっていい。
        
        ```yaml
        Mappings:
        	MapName:
        		TopLevelKey:
        			SecondLevelKey: ~
        			SecondLevelKey: ~
        Resources:
        	なんらかのリソース:
        		Properties: 
        			なんらかのプロパティ: !FindInMap
        				- MapName
        				- TopLevelKey
        				- SecondLevelKey
        ```
        
    - **Chips**
        
        Mappingの固定値を環境ごとに変えたい場合などに便利。実行するCFnは同じだが、環境毎に少し値が変わる場合にこのような対処の仕方はよくやるっぽい。
        
        ```yaml
        Mappings:
        	MapName:
        		TopLevelKey:
        			Dev: ~
        			Prod: ~
        Resources:
        	なんらかのリソース:
        		Properties: 
        			なんらかのプロパティ: !FindInMap
        				- MapName
        				- TopLevelKey
        				- !Ref ${Env} # パラメータ（入力値）で指定した方のSecondLevelKeyの値が設定される
        ```
        
- **Fn::If**
    - 特定のプロパティの値が可変な場合に使う。書き方は以下。
        
        ```yaml
        なんらかのリソースパラメータ: 
        - !If [条件, trueの場合, falseの場合]
        
        なんらかのリソースパラメータ: !If
        	- 条件
          - trueの場合
          - falseの場合
        ```
        
    - **Chips1**
        
        falseの場合のところに、`AWS::NoValue`とすることでtrueでない場合は、このパラメータをそもそも設定していないことにするというもの。
        
        ```yaml
        なんらかのリソースパラメータ: !If
        	- 条件
          - trueの場合
          - AWS::NoValue # falseの場合このパラメータを無効化
        ```
        
- **Fn::Equals**
    - 特定の値と一致するかの条件式として使う。`!If`と組み合わせて使ったりする。
        
        ```yaml
        なんらかのリソースパラメータ: !If
        	- !**Equals [!Ref Env, prod]**
          - trueの場合
          - falseの場合
        ```
        

### Conditionsとは

これを使うと条件式だけを外出ししておいて使いまわせるようになるので、条件式がシンプルになり読みやすいTemplateが書けるようになります。

例えば公式では以下のようにして環境変数によって値の変わる条件式を指定している。

```yaml
Conditions:
  CreateProdResources: !Equals 
    - !Ref EnvType
    - prod
```

ここで指定した`CreateProdResources`は他の組み込み関数で条件として使用される。

```yaml
なんらかのリソースパラメータ: !If # Fn::Ifと同じ
	- **CreateProdResources**
  - trueの場合
  - falseの場合
```

### Exportとimportvalueの使い方

これを使うと、スタックを別テンプレートで利用できるようになります。

CFnは外部の作成済みスタックを読み込む必要のあるリソースがいくつかあります。そんな時に読み込まれる側を外部参照できるようにするための`**Export**`とそれを読み込むための**`Fn::ImportValue`**が以下になります。

**Export**

Outputsセクションの中でExportすることで使用可能。

使用例）セキュリティグループをExportして置いて、別でALBスタックを作成する際にImportするなど。

```yaml
Outputs:
  Logical ID:
    Export:
      Name: '${AWS::StackName}-セキュリティグループ名'
```

[スタックの出力値のエクスポート - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html)

**Fn::ImportValue**

`${ALBSevurityGroupStackName}`はパラメータとして取得して、**Export**したセキュリティグループを読み込んでいるパターン。上記Exportに記述したTemplateのスタック名がそのままImportに入ることで参照できる。

```yaml
Resources:
	ALB:
		 ALBのセキュリティグループ設定パラメータ: 
			- Fn::ImportValue 
			  'Fn::Sub': '${ALBSevurityGroupStackName}-セキュリティグループ名'
```

[Fn::ImportValue - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html)

注意点

**Export**と**Fn::ImportValue**では`!Ref`が使えないので、手動でImportする値を合わせる必要があります。