入門 AWS CloudFormation #1（EC2関係とYAMLの復習編）

インフラやクラウドの専門というわけではないのですがそろそろIaC周りもちゃんと勉強しておきたい・・・という感じなのでAWSのCloudFormationについて入門しつつ復習として記事にまとめておきます。

なお、会社としてはほぼAWSで統一されている点と社内のクラウドなどに詳しい方々もCloudFormationを使われていたので相談のしやすさなどを加味しTerraformではなくCloudFormationを選択しています。その辺りはユースケースに合っていれば特にこだわりはありません。

※とりあえず最初ということでEC2を絡めてごく基本的なところの記事を書いていきます。将来もしかしたら2記事目以降を執筆して追加のEC2関係や他の様々なサービスや機能などにも触れていくかもしれません。

# 注意事項

本記事の処理を動かすとEC2関係などで色々と追加になったり起動したりします。その辺はリソースの停止や削除などをしないとお金がかかったりしてくる可能性があるためご注意ください。

# 触れること

- CloudFormationの概要
- YAMLの復習
- CloudFormationでのEC2操作（ごく基本的なところまで）

# CloudFormationとは

AWSのInfrastructure as Code（IaC）のためのサービスです。YAMLなどで記述することでインフラ構成の管理などを行うことができます。

近いものとしてTerraformなども存在します。そちらは他のクラウドも扱える一方でCloudFormationはAWSのサービスとなるため対象は基本的にAWSとなります。それらの比較に関しては以下の記事などをご参照ください。

https://qiita.com/answer_d/items/74c3d317058d48394d21

# YAMLについて軽くおさらい

CloudFormationではYAMLを使って構成などを書いていきます。そのため少しYAMLの知識が必要ですので少し復習がてら軽く触れておきます（既に慣れている方とかはこれらの節はスキップしてください）。

YAMLに初めて触る方でもJSONやマークダウンなどに慣れている方はすんなり馴染むと思います。

## YAMLのハッシュ設定

YAMLではハッシュの値としてセミコロンで左側にキー名、右側に値を記述する形で定義します（ブロックスタイルと呼ばれる形で、JSONみたいな書き方のフロースタイルという書き方も出来ますがフロースタイルは本記事では取り扱いません）。

例えば以下のような感じです。

```yaml
name: "John"
```

上記の記述はJSONで言うと以下の記述と同じような値になります。

```json
{"name": "John"}
```

## YAMLの文字列

YAMLの文字列にはシングルクォーテーションでもダブルクォーテーションでもどちらでも使うことができます。また、スペースやコロン、括弧、pipe記号などが文字列中に入っていなければクォーテーションを省略することもできます。

例えば以下の`John`という部分がYAMLでの文字列に該当しますが、クォーテーションを書かない / 書くパターンで値は一緒になります。

```yaml
name: John
```

```yaml
name: "John"
```

上記の記述はJSONで言うと以下の記述と同じような値になります。

```json
{"name": "John"}
```

文字列中にスペースや意味を持つ記号などが来る場合にはクォーテーションを追加しないといけません。

```yaml
name: "Taro Tanaka"
```

pipe記号（`|`）をセミコロンの後に配置して使うことで改行を維持した状態の文字列を定義することができます。行の先頭のインデント分などは無視されますが改行は保持されます。

```yaml
address: |
  北海道
  札幌市中央区
```

上記はJSONで言うと以下と同じような値になります。

```json
{"address": "北海道\n札幌市中央区"}
```

## YAMLのインデント

YAMLでのインデントは基本的に半角スペース2つで表現するケースが多いようです。他の4文字などでも認識してくれるそうですが本記事ではスペース2つで扱っていきます。

## YAMLの真偽値

trueもしくはfalseの値でサポートされています。

## YAMLの欠損値

欠損値はnullで扱うことができます。

## YAMLでのリスト

リストは以下のような形で改行とインデントを加えて、マークダウンのように`-`の記号を使うことで表現できます。

```yaml
Names:
  - タマ
  - ミケ
  - ポチ
```

もしくは`[]`の括弧を使ってコンマ区切りでインラインで書くこともできます。件数が少ない場合にはこちらも便利だと思います。

```yaml
Names: [タマ, ミケ, ポチ]
```

## YAMLでの入れ子

特定のキーの部分に入れ子にする形で文字列や数字、ハッシュ値やリストなどに階層を持たせることができます。

```yaml
Animal:
  Species: 猫
  Name: タマ
  Age: 5
  Family:
    - Name: ミケ
      Age: 5
    - Name: きなこ
      Age: 7
```

上記はJSONだと以下のような値になります。

```json
{
    "Animal": {
        "Species": "猫",
        "Name": "タマ",
        "Age": 5,
        "Family": [
            {
                "Name": "ミケ",
                "Age": 5
            },
            {
                "Name": "きなこ",
                "Age": 7
            }
        ]
    }
}
```

## YAMLの行コメント

Pythonなどのように`#`の記号でコメントを書くことができます。

# 事前準備

CloudFormationでEC2を実際に操作していこうと思いますが、事前に準備が少し必要なのでその辺を以降の節で作業していきます。

なお、AWSの各UIは今回は日本語表示設定にしてあります。また、リージョンは東京リージョンを選択して進めています。

## EC2用のキーペアの作成

EC2にSSHなどでログインするためのキーペア設定を事前に追加しておきます。

まずはAWSのwebコンソールにログインしてEC2関係のページに移動します。現時点では起動しているインスタンスなどは無い状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d743f529-f8e3-8a60-38a3-bab229e3f45e.png)

左のメニューの下の方をスクロールしてみるとネットワーク＆セキュリティというセクションにキーペアというメニューがあるので選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4b15482e-be15-3f5b-968a-9f8cfd02df1a.png)

キーペアのページにアクセスすると現在キーペアが存在しない状態になっています。右上のキーペアを作成ボタンをクリックして追加していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e1983100-1202-671a-3dd0-d740a90c058d.png)

今回はキーペアの各種設定には以下のようにしておきます。

- 名前 : cloudformation-test-1
- キーペアのタイプ : RSA
- プライベートキーファイル形式 : .ppk
- タグ : 今回は未設定のまま進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2beda912-bcf1-5b36-1740-bf11ca9e5337.png)

上記の設定をしたら右下のキーペアを作成ボタンをクリックして作成します。

秘密鍵の保存用のダイアログが表示されるので保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/809c2cd2-628d-3615-53fc-8b39d86930b3.png)

一覧画面でキーペアが追加となったことが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b10d9d04-454b-c2da-33bb-ba9f5b32cc38.png)

また、同様に名前の部分だけ`cloudformation-test-1`から`cloudformation-test-2`に変更したキーペアも同様に追加しておきます（名前以外の設定は同じです）。
CloudFormationでの更新設定で使用します。

最終的に以下のように2つのキーペアが追加となっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2a76eed4-fda2-a8bc-4b6d-071699fab5b8.png)

## EC2インスタンスのAMI IDを調べる

CloudFormationでEC2インスタンスを指定する際に必要になるためEC2のAMI IDを調べます。この設定で使われるOSとかが変わります。

まずはEC2関係のページで左のメニューのインスタンスのセクションにあるインスタンスメニューをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e35af127-a183-3488-f4bb-d72dc191c026.png)

インスタンス一覧画面が表示されるので右上のインスタンスを起動ボタンをクリックします（この時点ではまだ起動とかは実行されません）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2be4996b-7fe3-d5fd-d234-c434087050e5.png)

クリック後インスタンスを起動というページに遷移します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d632ef49-e767-a907-e965-945f0ec3dc4f.png)

アプリケーションおよび OS イメージ (Amazon マシンイメージ)のセクションのクイックスタートの部分で対象の必要なOSを選択していきます。今回はUbuntu 22.04を選択しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/87f7dec7-4527-7cfc-7216-84177f89bda2.png)

AMI IDというところの値を使うためメモしておきます。今回は ami-0590f3a1742b17914 となっています（対象やバージョン・リージョンなどで変わってくると思います）。

## VS CodeのCloudFormation関係の拡張機能を入れてみる

生のYAMLでもVS CodeやらTabnineやらを使っているのである程度楽ができますが、追加でより一層楽をするためにcfn-lint関係のVS Codeの拡張機能を入れてみます。

色々と世の中にはその辺があるようですが、以下の記事でcfn-lintが優秀らしいと書かれているのと拡張機能の評価も本記事執筆時点で4.5と高めだったのでcfn-lint関係で進めていこうと思います。

> …と、正直標準機能のvalidateはsyntaxレベルまでしか見ていないようなので使い物にならない気がします。
>
> その代わり(？)、cfn-lintは優秀っぽいのでそちらを使いましょう。

https://qiita.com/answer_d/items/74c3d317058d48394d21#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3

cfn-lintに関してはvscode-cfn-lintという拡張機能が該当するようなのでそちらを検索してインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e387d941-215b-566a-0502-ec3336ca9630.png)

インストール後にどうやらpipで追加のPythonライブラリをインストールする必要がある？ようなのでこちらもインストールしておきます。

```
$ pip install cfn-lint
```

```
Successfully installed aws-sam-translator-1.55.0 boto3-1.26.32 botocore-1.29.32 cfn-lint-0.72.5 jmespath-1.0.1 jschema-to-python-1.2.3 jsonpatch-1.32 jsonpickle-3.0.1 jsonpointer-2.3 junit-xml-1.9 networkx-2.8.8 pbr-5.11.0 s3transfer-0.6.0 sarif-om-1.0.4
```

インストールが終わったらVS Codeを再起動しておきます。

動作確認のためにローカルでYAMLファイルを追加して以下のような記述をコピペしてみます。

```yaml
AWSTemplateFormatVersion: 2010-09-09
Resources:
  DevEC2Instance:
    Type: 'AWS::EC2::Instance'
```

VS Code上で特にエラーなどが発生していないようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/302145fd-8880-bc2d-b67e-5c2c7c61c3e0.png)

試しに記述をミスした想定で`Resources`の部分を`Resource`と書き換えてみます。
保存してみると以下のようにエラーとなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8aae26c9-3c5d-25c5-226f-50446cfd87cb.png)

エラーの箇所にマウスオーバーしてみると色々と情報を表示してくれるようです。AWSドキュメントへのリンクも表示してくれるようでこれは助かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b86fdc5f-3e44-d1e9-7bea-4a515eb8f13f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ffd2db02-61fb-0782-cf4d-27710b213d3e.png)

また、VS Codeの元々の機能なのか拡張機能なのかは把握していませんが（恐らく入れた拡張機能？）入力補完でスパナのものを選択すると色々追加で補完してくれるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4500b58e-442c-292e-8f36-7fc8cd8e3c7e.png)

AWと入力してAWSTemplateFormatVersionの補完候補を試しに選択してみるとバージョンの日付部分なども含めて設定してくれるようです。こちらも便利そうですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e9c3130a-c7b6-86bb-47c6-9dd21b522f6b.png)

# CloudFormationで新しいスタックを作ってみる

突然スタックという単語が出てきてCloudFormationにおけるスタックってなんだ・・・？という感じですが、CloudFormationのドキュメントには以下のように書かれています。

> スタックは、単一のユニットとして管理できる AWS リソースのコレクションです。つまり、スタックを作成、更新、削除することで、リソースのコレクションを作成、更新、削除できます。スタック内のすべてのリソースは、スタックの AWS CloudFormation テンプレートで定義されます。たとえば、ウェブサーバー、データベース、ネットワークルールなど、ウェブアプリケーションの実行に必要なすべてのリソースをスタックに格納できます。ウェブアプリケーションが不要になった場合は、スタックを削除するだけで関連リソースをすべて削除できます。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacks.html

EC2とかDB、ネットワーク、webアプリ実行用のライブラリなどプロジェクトに必要な諸々の設定のセットといったところでしょうか。1つのスタック内で様々なサーバーなどのリソースの定義を行うことができます。

単一のYAMLで書くケースであったり、もしくは親子関係を持たせて複数のYAMLファイルに記述することもできます。

早速スタックの作成を進めてみます。AWSのwebコンソールのCloudFormationのページに遷移し左のメニューのスタックを選択し、スタック一覧ページに遷移して右上にあるスタック作成ボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/87b33d30-4aa9-283e-d039-dd36981df885.png)

スタックの作成ページでは以下のような画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/8bdde317-23ff-b334-c25b-ff39a567a69c.png)

「前提条件 - テンプレートの準備」の部分ですが、これはスタックで使用するYAMLファイルをどう準備するか・・・という感じになります。若干日本語のUIだと分かりづらい（この辺は英語表記の方が分かりやすい）気がしないでもないですが、それぞれ以下のような形になります。

- テンプレートの準備完了 -> 既にローカルやS3上などにYAMLファイル（テンプレート）などが存在していて準備が完了している（アップロードすれば対応ができる）場合に選択肢です。選択するとS3上のパスの指定もしくはローカルファイルのアップロードのUIが表示されます。
- サンプルテンプレートを使用 -> AWS側で用意されているサンプルのテンプレート設定を使います。
- デザイナーでテンプレートを作成 -> デザイナーとはCloudFormationにそういった名称の機能があるためそちらのことを指しています。UnrealEngineのブループリントやBlenderにあるような感じでYAMLなどを直接記述する代わりにビジュアルスクリプティング的にGUIでテンプレートを記述できる機能です。

この節ではとりあえずローカルでYAMLを記述してアップロードする形で進めます（デザイナーとかは後の節で触れます）。

ローカルにYAMLファイルを用意して記述を進めていきます。とりあえず必要最低限で試したいためごくシンプルなEC2の記述で進めていきます。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.micro
```

`AWSTemplateFormatVersion: "2010-09-09"`部分はテンプレートのフォーマットのバージョン指定となります。本記事執筆時点では`"2010-09-09"`のバージョンのみとなっているので固定でこの値で良いと思います。

省略も可能？な気配がありますが、将来バージョンが追加になって（新しいバージョンが使われるように切り替わってしまうなどすると）悪影響が出る・・・可能性も無きにしもなので一応本記事では指定する形で進めます。

`Resources:`セクション部分には設定するAWSのリソース（サーバーなど）を指定していきます。今回はEC2のインスタンスのみを設定するため1件のみ指定しています。

`MyFirstEC2Instance:`部分は任意の名称です。今回は試しに適当なEC2リソース識別用の名前を付けました。

`Type: AWS::EC2::Instance`部分はEC2の中の何の指定なのかの種別です。公式ドキュメントに色々載っています。今回はEC2のインスタンスを扱いたいため`AWS::EC2::Instance`と指定しています。

タイプの一覧に関しては以下の公式ドキュメントのページで対象のリソースを選んだ後に各タイプ一覧が表示されるようになっています。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

例えばリソース一覧でAmazon EC2を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c7d79b64-099e-5902-feaf-9c07bde34dc1.png)

その後にEC2関係のタイプ一覧が表示されます。その中には先ほど指定した`EC2::Instance`のタイプも存在します。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_EC2.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4e40da0f-1cff-a4d7-9a00-fe0706280249.png)

このように一覧からCloudFormationで操作したいタイプを調べて選択する形となります。

`Properties:`部分は対象のリソースに指定する属性値です。今回はEC2インスタンスなのでEC2インスタンス絡みの属性設定となります。

今回は以下の2つを指定しています。

- イメージのID（`ImageId: ami-0590f3a1742b17914`）: 使用するイメージのID（AMI ID）となります。OSとかの決定に使われます。今回は事前準備で調べておいたUbuntuインスタンスの`ami-0590f3a1742b17914`を指定しました。
- インスタンスタイプ（`InstanceType: t2.micro`）: 使用するインスタンスのスペックなどを決めるためのインスタンスタイプです。今回はお試しという面が強いので無料枠などもある程度あったりコストも低い`t2.micro`のインスタンスタイプを指定しています。

YAMLの準備が出来たのでYAMLテンプレートのアップロードをしていきます。

スタックの作成ページに戻って「テンプレートの準備完了」 → 「テンプレートファイルのアップロード」を選択します。そうするとファイルの選択のボタンが表示されるので保存したYAMLを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1ad12238-b804-69fe-7e0c-b4b78cffcbe5.png)

アップロードが完了するとそのファイルはS3へと配置されます。ボタンの付近にアップロード先のS3のパスが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/10237de9-6b8a-5724-9089-8549c5a272f8.png)

実際にS3を見てみても対象のバケットが作成されてYAMLファイルがアップロードされていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ef806c71-2d46-2e0d-c9af-ebaf4f7a7ab0.png)

CloudFormationのページで「次へ」ボタンを押して次のページに遷移します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/39147bbf-d049-3d7b-82de-bd1d008f0484.png)

スタックの名前を設定します。任意の値が設定できるので今回はmy-first-ec2-instanceという名前にしました。

パラメータのセクションに関してはまだYAML内でパラメータの指定を使用しておらず特に設定などはできないのでそのままにしておきます。「次へ」ボタンを押して次の画面に遷移します。

タグやロールなどの設定画面になります。必要なタグの設定などができますが、今回は諸々スキップしていきます。スタックの失敗オプションや詳細オプションのセクションなどもデフォルトのままでスキップしていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6e322aac-8278-3b44-a6c6-98b06dc56ee9.png)

「次へ」ボタンを押すと最後に諸々の設定の最終確認のためのレビュー画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cbb254e0-029e-a939-75bd-d3b1b0479336.png)

今回はレビュー画面で特に触れることがないためそのまま「送信」ボタンを押します。

そうするとスタックが生成されてテンプレートの内容に応じたリソースの作成処理などが走り始めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6fe7d01f-20af-6163-f035-2815b354ad1b.png)

遷移した直後ではイベントのタブが表示されていると思いますが、それぞれ以下のような内容になっています。

- タイムスタンプ -> 対象のイベント発生時の日時。
- 論理ID -> 識別用のID。名前などで設定した値が表示されます。
- ステータス -> 処理のステータス。最初はリソース作成処理などが走り始めたイベントの`CREATE_IN_PROGRESS`などが設定されます。完了時には`CREATE_COMPLETE`などが設定されます。なんらかのエラーなどが発生した場合等には別のものが設定されます。
- 状況の理由 -> そのイベントが発生した理由が表示されます。基本的にエラー時などに調査として使えます。

ページをリロードするか、もしくはページ内のリロードボタンを押すと内容が更新されます（ボタンの方はたまにうまく反映されないことがあるのでその場合はページのリロードをすると無難です）。

しばらく待ってからリロードすると、（何か問題が発生していなければ）以下のように完了状態のイベントが色々表示されると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/448ab8da-a79d-3691-f699-126aefcdc3f5.png)

ここまで来るとYAML内で指定したEC2インスタンスのリソース作成処理が完了しています。EC2のページに移って確認してみます。

そうするとEC2インスタンスが追加になっていることが確認できます。また、対象のEC2インスタンスは起動していることも確認できます。インスタンスタイプもYAML内で指定したt2.microになっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/37ab46bb-3182-acd9-363c-8258543e9d76.png)

インスタンスの詳細を確認してみると、AMI IDがYAMLで指定した値（`ami-0590f3a1742b17914`）になっており、且つOSも指定したAMI IDに準じたUbuntuになっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6508c681-c2a6-e432-3b30-15b353ba89c2.png)

これでひとまずはCloudFormationの最初の一歩が完了です！

# スタック操作後の注意点

前節のようにEC2インスタンスは起動した状態でリソースが作成されます。
そのため設定や無料枠次第ではそのまま放置するとコストがかかってしまいます。ご利用の際にはご注意ください。

# スタックを更新してみる

例えばサーバーリソースの設定で変更や追加が必要になったパターンを考えてみます。
そういった場合にはスタックを更新することで対応することができます。

試しに先ほど作成したスタックを更新してみます。

CloudFormationのスタックのページに遷移すると作成したスタック一覧が表示されます。対象のスタックを選択すると「更新」ボタンが押せるようになるためそちらのボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/239a65cc-4944-a679-9fd7-f40be0531cb9.png)

そうするとスタックの更新ページに遷移します。テンプレートの準備の箇所ではいくつかの選択肢が表示されていますがそれぞれ以下のようになっています。

- 現在のテンプレートの使用 -> アップロード済みのYAMLはそのまま変えずに、パラメータやその他オプション設定などを変更できます。
- 既存テンプレートを置き換える -> YAMLテンプレートの内容も更新する形で設定を変更できます。
- デザイナーでテンプレートを編集する -> CloudFormationのデザイナーの機能を使って編集を行います。

今回はYAMLテンプレートの内容も更新するため「既存テンプレートを置き換える」を選択して進めます。

テンプレートソースでは「テンプレートファイルのアップロード」を選択してファイルアップロードのUIを表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a3754cc9-00ea-9e26-394f-39c07f0fee41.png)

アップロード用のYAMLファイルを更新しておきます。今回は事前準備で追加したキーペアをEC2に設定する形を想定します。

YAMLのPropertiesセクションに`KeyName: cloudformation-test-1`という設定を追加することでキーペア設定を行うことができます。

`cloudformation-test-1`の名前は事前準備でキーペアを追加するときに指定した名前です。EC2のキーペアのページから追加済みのキーペアの名前は確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/968a27a4-040e-a6f2-d368-de5cb13e132f.png)

YAML全体は以下のようになっています。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.micro
      KeyName: cloudformation-test-1
```

CloudFormationのページに戻ってYAMLファイルをアップロードし次へボタンをクリックします。

スタックの詳細を指定のページとなりますが、前節で触れた通りまだYAMLファイルでパラメータを追加していないのでここでは設定できる点はありません。そのまま次へをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b4126d76-ac0b-53b3-9cca-3118efd3b136.png)

スタックオプションの設定の画面になりますがこちらも今回は特に変えなくてOKなのでそのまま次へをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ebde7da6-2548-13f2-b8c9-1e4f3b4658fe.png)

最後にレビュー画面に遷移します。この辺の流れは新しいスタックを作成する場合と同じになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e5e6ecdf-57ae-1892-a720-f20ee470e13e.png)

ただしレビュー画面の下の方にいくと「変更セットのプレビュー」という部分で変更点が表示されるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6930cf49-37a6-cca8-e47c-5de5cc715bba.png)

今回はEC2のリソース内容に変更を加えたのでEC2関係が表示されています。

注意すべき点として置換の部分がTrueになっていたら既存のインスタンスは終了（停止と削除）されて新しくインスタンスの作成が実行されます。既に動いている（使われている）ものに対して更新をかける際には注意が必要です。

今回は特に既存のインスタンスを既に使っているという感じでもないためそのまま送信をクリックします。

※もし前節などで作成したEC2インスタンスが削除済みもしくは停止している場合などにはもしかしたらエラーになるかもしれません。

そうすると対象のスタックのイベント表示の画面になります。以前の作成時のCREATE_COMPLETEのイベントの後にUPDATE_IN_PROGRESSの今回のイベントが表示されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7bf79157-60bc-830e-d8a3-58d29c802598.png)

しばらく待ってページをリロードするとDELETE_COMPLETEとUPDATE_COMPLETEなどのイベントが走っていることが確認できます。UPDATE_COMPLETEと出ていたら更新完了です。

また、前述の通り置換にTrueが設定されていた場合削除なども実行されるためDELETE関係の各イベントが走っていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a5105806-76c9-1d5a-e8e1-410e67b90092.png)

EC2のページに遷移してみると、別のインスタンスIDのインスタンスが1台起動していることが確認できます。一見変わっていないように見えますが置換が実行されたため別のインスタンスIDとなっており、インスタンスの作り直しが実行されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/45363326-c878-2e47-cdb3-49fcd8b09f44.png)

また、インスタンスを選択して詳細を確認するとキーペアが指定したものに変わっていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/691690a1-0e76-32af-97ad-33fbbdab3a73.png)

これでテンプレートの更新によってインスタンスの設定が更新されることを確認できました。

# 変更セットによる更新を試してみる

CloudFormationには前節までに触れた「更新」ボタンをクリックして更新するパターンと既存のスタックに対して「変更セット」と呼ばれるものを作成して更新する方法があります。

何が違うのか？という感じですが、変更セットの方は事前に変更内容の作成と結果の表示を行うものの、その変更セットを反映せずに削除するのかそのまま反映（実行）するのかを選択できるようになっています。

> 一言で言うと、安心してスタックを更新できる機能です。
> 3行で言うと
> スタックの更新前に
> リソースの更新や削除を
> 把握することができる
> という機能です。

https://zenn.dev/mn87/articles/e5cbf848a97bb9

更新ボタン経由での更新時よりも事前に得られる情報が多くなる・・・といったところでしょうか。なんとなく安全な変更セット側だけあれば良いのでは・・・？と思ってしまいましたが、もしかしたら更新ボタン側は把握できていないメリットがあるのかもしれません。

また、変更セット側ではGitのコミット時のように変更セットの内容に任意のメッセージを付けたりなども行うことができます。

今度は変更セットを使った更新を行ってみましょう。

CloudFormationのスタックのページに遷移して、変更セットを作りたいスタックを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/90ffbe98-f04d-cebe-1fca-3f74638f9755.png)

その後に右上のスタックアクションのメニューを選択して、中に含まれている「既存スタックの変更セットを作成」というメニューを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c16c5684-bb9d-870c-7ed2-8c6e66e3d477.png)

更新ボタン経由の時と同じように前提条件選択の画面が表示されます。前節までと同様に今回は「既存テンプレートを置き換える」を選択します。

また、アップロードするYAMLを少し変更しておきます。`KeyName: cloudformation-test-1`という箇所を`KeyName: cloudformation-test-2`に変更します。YAMLの内容は以下のようになります。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.micro
      KeyName: cloudformation-test-2
```

CloudFormationの画面に戻って「テンプレートファイルのアップロード」を選択して更新したYAMLファイルをアップロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/34a5f6a7-8f68-cd33-77f1-f4ed7525eedd.png)

後は今までと同様に次へボタンを押して画面を進めます。レビュー画面になると少しUIが変わっています。最下部のボタンのラベルが「変更セットの作成」になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/db168951-9c88-53c6-981e-71eecc029f20.png)

更新ボタン経由の時と異なり、このボタンを押した時点ではまだインスタンスなどのリソースへの更新は反映されない形になっています。変更セットの作成ボタンをクリックして進めます。

そうすると「変更セットの作成」というダイアログが表示されます。これは変更セット名と変更セットの説明を設定することができます。Gitのコミットメッセージみたいなものだと考えて良いかもしれません。

今回は変更セット名を`keypair-change-2`、説明を「cloudformation-test-2へのキーペアの変更。」としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1f39a7ec-b00c-60a8-0719-ffcaffca52d9.png)

変更セットの作成ボタンを押して作成処理を開始します。

そうすると以下のような画面になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4f0b767d-2d22-9258-487e-9117c31265b2.png)

ステータスがCREATE_PENDINGとなっており、且つ変更内容も「変更内容はありません」と表示されています。事前に変更内容が確認できるのでは？という感じでしたが、この画面の内容は反映されるまで少し時間がかかります。少し待ってから右上のリロードアイコンなどを押すと内容が反映されます。

少し待ってリロードアイコンをクリックすると以下のような表示になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a7fad710-bc2d-15bb-ce55-e97a5b478587.png)

ステータスがCREATE_COMPLETEとなっていることが確認できます。また、実行ステータスもAVAILABLEとなっておりこの変更が利用できることが確認できます。

変更に関しても対象のEC2インスタンスのリソースに対する変更をかけているのでEC2関係の表示がされています。

更新ボタン経由での変更時とどうように、置換にTrueと設定されていたらリソースの削除などが一旦実行される内容の更新となっているので要注意です。

内容を確認してこの変更セットを反映する場合は右上の「変更セットを実行」、もし変更内容に違和感やミスなどがあれば「変更セットの削除」を選択することで変更セットの作成結果を取り消すことができます。今回はこのまま変更セットを実行をクリックして進めます。

実行前に「変更セットを実行」ダイアログが表示されます。これは変更セットを反映した結果何らかエラーなどが発生した際にどうするのかを指定することができます。こちらもGitのように何らか問題が起きた時に古いバージョンの状態にロールバック出来る・・・といった感じのようです（ロールバック側も失敗しなければ）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/71e1ba8a-e73f-0729-f4d4-915ea6339b3d.png)

今回は「すべてのスタックリソースをロールバックする」を選択して変更セットを実行ボタンをクリックして反映します。

そうするとお馴染みのイベント画面となりました。後の挙動はスタック作成時や更新ボタンを押して更新したときと同じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/999dd97f-ed90-eeba-6420-b12521e71aa4.png)

余談ですが、今回変更セットの説明に日本語を初めて使ってみましたがUI上の表示が?????的になっていたので日本語の使用は良くなかったかもしれません・・・。

# インスタンスタイプを変更してみて、置換が実行されないことを確認する

今度はインスタンスタイプを変更してみます。`t2.micro`から`t2.nano`へと変更してみます。YAMLテンプレートは以下のようになります。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-2
```

前節までと同様にスタックのページから対象のスタックを選択し、スタックアクション → 既存スタックの変更セットを作成 → 既存テンプレートを置き換える → テンプレートファイルのアップロードの流れでYAMLファイルをアップロードします。パラメーターやタグなどの設定はデフォルトのままで変更セットの作成を行う画面まで遷移します。

変更セット名は`change-instance-type-to-t2-nano`として、変更セットの説明は今回はスキップしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f62eae41-33fa-8907-ff7f-2142831d419b.png)

変更セット作成後の画面では以下のようになっています（例のごとく反映されるまで少し時間がかかるため、少し待ってからリロードアイコンのクリックなどをする必要があります）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/716d9567-42bc-9cc2-01a1-5441b81937df.png)

注目すべき点として下の方の変更の箇所の置換がConditionalとなっている点が挙げられます（今までの節ではTrueとなっていました）。

この場合インスタンスの削除などの処理が走るとは限りません（条件によっては走るケースもある・・・感じでしょうか？）。実際にインスタンスタイプの変更は一時的にインスタンスの停止はされるものの、インスタンスの破棄はしなくともインスタンスタイプは変更できるので破棄はされずにインスタンスIDなどを維持したまま変更セット分が反映されます。

変更セットの実行ボタンを押して反映してみます。

イベントタブの画面に遷移するので反映されるまでしばらく待ちます。その後リロードアイコンを押したりしてステータスが`UPDATE_COMPLETE`になることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7da40bef-2f62-c932-83b8-d1e3ab28aa29.png)

想定通りイベントのステータスが`UPDATE_IN_PROGRESS`と`UPDATE_COMPLETE`のみになっており、DELETE関係は走っていないことが確認できます。

EC2のページに遷移してインスタンスを確認してみても、インスタンスIDは変わっていないことが確認できます。また、インスタンスタイプも`t2.nano`に無事更新されていることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f6945b7d-f3fc-f50f-e436-2f5189dbcd49.png)

# スタックの作成が失敗する場合の挙動を確認してみる

新しいスタックを作成する場合で、且つYAMLテンプレートの内容が不正で作成に失敗するケースを試してみます。

今回は存在しないキーペアの名前を指定してわざとエラーにしてみます。`KeyName`部分を`cloudformation-test-2`から`cloudformation-test-3`に変更します。YAMLテンプレートは以下のようになっています。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.nano
      KeyName: cloudformation-test-3
```

スタックのページの右上のスタックの作成から新しいリソースを使用（標準）を選択して新しいスタックを作成していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d214d4f4-ffd0-06ef-605f-58ca6ef542d5.png)

前提条件のところは今までの節と同じようにテンプレートの準備完了 → テンプレートファイルのアップロードを選択してYAMLファイルをアップロードしていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/04f9ebb1-a746-e9df-8273-5e60e8b9620c.png)

スタックの名前は`my-second-ec2-instance`としました。パラメータやタグ設定はデフォルトのままスキップして次へボタンを押していきます。作成がスタートしてイベントタブが表示される画面まで進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/21d6a44f-056b-4f35-39ca-0a85269b0dcc.png)

イベントタブの画面でしばらく待ってリロードアイコンを押すと、色々と失敗関係の表示がされることを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/dca4da5e-fdb7-b474-71d3-18e73edb70b1.png)

状況の説明を見てみると以下のように表示されており、キーペアが存在しないためエラーになっているという点を確認することができます。

> The key pair 'cloudformation-test-3' does not exist (Service: AmazonEC2; Status Code: 400; Error Code: InvalidKeyPair.NotFound; Request ID: 3bd561c1-b478-4dde-a777-e26f99ed2ae3; Proxy: null)

イベントに削除関係のもの（`DELETE_COMPLETE`）が表示されていることから分かる通り、この状態でEC2のページに遷移してみても削除済みで今回のインスタンスは作成されていないことが確認できます（前節までに作成した1つのインスタンスのみ起動していることが確認できます）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b4a4120f-6317-e079-c1ef-5d73906bc1c1.png)

スタックのページに遷移すると対象の失敗したスタックは表示はされるものの、選択しても更新や変更セットの作成メニューなどは選択できないようになっています。つまり作成時に失敗すると更新などは効かなくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1828f2f7-63a7-f287-3844-47e08c79ae17.png)

このような場合には残しておいてもしょうがないので基本的に対象のスタックは削除する方向になります。

削除は対象のスタックを選択して、更新ボタンの隣にある削除ボタンをクリックすることで行うことができます。実際に削除してみましょう。

ボタンクリック後、削除確認のダイアログが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5c459838-b6b6-fa3c-f39c-3b6823607136.png)

そのままスタックの削除をクリックします。

削除が開始された旨が表示されます。
余談ですがこの辺も日本語表示がおかしいので仕事と同様英語表示にした方が良いだろうか・・・という気がしてきますね・・・w

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4b3c23e1-5fef-9d7d-7508-d76b23882986.png)

しばらくまってリロードアイコンなどをクリックすると対象が削除されたことを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/feb78f12-8e70-74d9-9e84-d6ad11b69e6b.png)

# 変更セットの反映失敗時の挙動の確認

前節ではスタック作成の失敗時を確認しましたが、今度は変更セットの反映が失敗した際にどうなるのかを見ていきます。

まずはYAMLファイルを用意します。

前回までと同様にわざと失敗するように`cloudformation-test-3`という存在しないキーペア名を指定しています。また、インスタンスタイプを`t2.micro`に調整しています（作成済みのインスタンスは`t2.nano`になっています）。最終的にYAMLファイルは以下のようになっています。

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  MyFirstEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0590f3a1742b17914
      InstanceType: t2.micro
      KeyName: cloudformation-test-3
```

これを既存の作成済みのスタック（`my-first-ec2-instance`）に対して変更セットで反映していきます。

スタック一覧のページに遷移して対象のスタックを選択し、スタックアクション → 既存スタックの変更セットを作成を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/328f4086-2e66-0489-46ef-cfe1f7f90070.png)

前提条件で既存テンプレートを置き換える → テンプレートファイルのアップロード → ファイルの選択で先ほどのYAMLをアップロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/560dbd68-24ce-8658-bbab-7ad313d48462.png)

パラメーターやタグ設定などはデフォルトのままで進めます。変更セットの名前は`update-keypair-name-and-instance-type`としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e8658fd4-f269-9d3d-697e-15cce7779464.png)

作成された変更セット画面で少し待ってリロードすると以下のようになります。キーペアを変更したので置換がTrueとなっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/152fa94f-0d05-b9f2-d18d-558d2cb4abce.png)

右上の変更セットを実行ボタンを押して進めます。失敗時の動作はすべてのスタックリソースをロールバックするように選択しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6fa71718-7eee-7f28-71e3-6e7c1d2c3fba.png)

実行後のイベントタブ画面でしばらく待ってリロードすると`UPDATE_FAILED`や`UPDATE_ROLLBACK_IN_PROGRESS`などのステータスが表示され失敗していることが確認できます。エラーメッセージの表示などもスタック作成時と同じような感じで表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e586dce8-a3e7-9e60-a484-10c06efaf0e2.png)

また、置換がTrueになっていたので削除と追加が実行されるテンプレートだったわけですが結果のイベントには`DELETE_COMPLETE`や`UPDATE_ROLLBACK_COMPLETE`とあることから分かるように追加されたインスタンスは削除され、以前のものがロールバックされている・・・ということが分かります。

実際にEC2のページを確認してみると、以前のインスタンスだけが残っていることが確認できます。インスタンスタイプも変化していません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7cef85b1-c235-7125-15ca-674dbb4eeada.png)

スタックのページに遷移するとステータスは`UPDATE_ROLLBACK_COMPLETE`となっているものの、スタック作成の失敗時と異なり更新や新たな変更セットの作成などは行えるようになっています。つまり変更セットの反映が失敗した場合にはテンプレートを調整して再度反映を試す形で対応が効きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6f197fd6-1c44-9eb0-5519-fcfe24613c79.png)

記事が長くなったので一旦本記事はここまでで区切って残りは別の記事にしようと思います。
最後に、EC2インスタンスなどは生成済みとなっているため必要な場合はそれらの削除などを忘れないようにご注意ください（私は次回記事でも使用するためこのまま残す形にはしていきます）。

# 参考サイト・参考文献・講座など

https://aws.amazon.com/jp/cloudformation/

https://qiita.com/answer_d/items/74c3d317058d48394d21

https://www.udemy.com/course/aws-cloudformation-simplified-hands-on-learning/

https://ja.wikipedia.org/wiki/YAML

https://www.task-notes.com/entry/20150922/1442890800

https://qiita.com/jerrywdlee/items/d5d31c10617ec7342d56

https://magazine.rubyist.net/articles/0009/0009-YAML.html

https://dev.classmethod.jp/articles/getting-start-yaml-for-ansible/

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/stacks.html

https://github.com/aws-cloudformation/cfn-lint#install

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/format-version-structure.html

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

https://zenn.dev/mn87/articles/e5cbf848a97bb9