【備忘録・まとめ】AWS CloudFormation ユーザーガイド

## 出典

[AWS CloudFormation ユーザーガイド](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html)

## まとめ

* AWS謹製のInfrastructure as Codeのフレームワーク
* JSONやYAMLで宣言的にインフラを構築、制御できる
* ワリと簡単に使える
* ただし、全ての制御がここからできるわけではなく、あくまで各種サービスのプロビジョニングまで・・・といった程度
* なので、本格的に使い込もうとすると、設定に一部手作業が発生したりする
* スタックの分割はできるのでそれなりに複雑なものも作れるが、Terraformなんかと比べると記述力には劣る
* 薄め、簡素なサービスの定義だったらこれで良さそう

## 要点

### 概念

* テンプレート：JSONまたはYAMLで記述されたスタックの設計情報。これをCFに食わせると、テンプレートに書かれた構成でスタックが立ち上がる。
* スタック：実際にAWSクラウド内に構築されるサービスのインスタンス群で、一まとまりのもの。テンプレートからCFが作ってくれる。ユーザーはスタックを作成、変更、削除することで、サービスインスタンス群を操作する。
* 変更セット：スタックに加える変更の差分。スタックに変更セットを加える形で、CFは実際のサービスインスタンスを操作する。

### アクセス制御について

* CFは、CF自体にアタッチされたロールのポリシーの他に、CFユーザー自身のポリシーも引き継いで作業を実行できる。
* スタックを操作するのに必要な一連の許可が、CFのプロジェクトないしユーザーにアタッチされていることを確認する。
* もちろん、CFユーザーにはCFの各種アクションが許可されていなければならない。

### [テンプレートの基本](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html)

テンプレートはJSONないしYAMLで記述され、コードとしてGit等でバージョン管理ができる。テンプレートには[フォーマット](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-formats.html)があり、その形式に準じた形でなければCFで実行できない。

#### 論理名による参照

* テンプレートに記載された各リソースは、テンプレート内の論理名で他の箇所から参照できる。
* [Ref関数](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html)を用いる。

#### パラメータ定義と参照

* テンプレートのトップレベルフィールド`Parameters`以下にパラメータを定義することで、テンプレート内にテンプレート外からパラメータを導入して参照することができる。
* `Parameters`フィールドで定義した変数名を、`Ref`関数で参照する。
* パラメータの定義にはデータ型ないしリソースタイプを設定できるので、これによりパラメータ設定時に値のバリデーションが行われる。
* パラメータの定義には説明を残すこともできる。

```yaml

Parameters:
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: 'AWS::EC2::KeyPair::KeyName'
```

#### リソースやパラメータの属性取得

* [Fn::GetAtt](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html)関数を使う。
* 下記の例は、`myBucket`リソースの`DomainName`属性を参照する使い方。他には、ARNの参照なんかによく使う。

```yaml

- DomainName: !GetAtt myBucket.DomainName
```

#### [Mappingsを使用した条件値の指定](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html#gettingstarted.templatebasics.mappings)

* よくあるプログラミング言語の`switch ~ case`構文の様な書き方ができる。
* `Mappings`オブジェクトと[FindInMap関数](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html)を使う。
* 下記の例は、`AWS::Region`の属性値によって`AMI`フィールドの値を切り替える使い方。

```yaml
Mappings:
  RegionMap:
    us-east-1:
      AMI: ami-76f0061f
    us-west-1:
      AMI: ami-655a0a20
    eu-west-1:
      AMI: ami-7fd4e10b
Resources:
  Ec2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: !FindInMap 
        - RegionMap
        - !Ref 'AWS::Region'
        - AMI
```

#### [値の連結](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/gettingstarted.templatebasics.html#gettingstarted.templatebasics.outputs)

* [Fn::Join関数](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html)で、各種値を結合できる
* 下記の例では、空文字で第2引数のリストの要素3つ`HTTP:`、`!Ref WebServerPort`、`/`を連結している
* 結果は`HTTP:8888/`のようになる

```yaml
 Target: !Join 
          - ''
          - - 'HTTP:'
            - !Ref WebServerPort
            - /
```

#### その他テンプレートで目ぼしい機能

* [DependsOn属性](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html)：他のリソースへの依存関係を記述できる。リソースの作成順等をコントロールできる。
* [DeletionPolicy属性](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html)
    * このポリシーに準じて、スタックが削除された際にリソースを保持または (場合によっては) バックアップできる。
    * [この記事](https://dev.classmethod.jp/cloud/aws/how-to-keep-datas-when-deleting-stacks/)がとっても参考になる
* [AWS::CloudFormation::Stack](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html)
    * CFスタックをリソースとしてネストできる
    * スタックの中にスタックをつくれる
    * こいつぁすげぇ

### [テンプレートセクション](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-anatomy.html#template-anatomy-sections)

テンプレート構造の基本。一度は読んでおく。あとはリファレンスとして使う。

### [CLIでの操作](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-using-cli.html)

 一度手で動かしてみる。

### [スタックの更新](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks.html)

* スタックの更新方法には、「直接更新」と「[変更セットの作成と実行](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html)」がある
* なるべく更新セットを使う方法でいきたい
* リソースタイプによって、以下3つの更新時動作がある
    * 中断を伴わない更新
    * 中断を伴う更新
    * 置換
* リソースタイプと変更するプロパティによってどの動作を取るのか異なるので、要確認のこと

### ハマりどころとか、イケてないてところ

* `ImportValue`関数は、既存のスタックで公開された値しか参照できないので、スタックを作る順序はこちらで制御する必要がある。つまり、スタック間の依存関係を作成順にバラし、`DependsOn`属性でサブスタック作成の順番を制御してやらねばならない。
* サブスタック参照されるスタックの定義ファイルは、S3に置かねばならない。しかも、ファイルの指定はURLの形式で・・・。