CloudFormation入門 #2 「SSHでログインできるEC2インスタンスの作成」


**注意！ スタックを実行するとAWSのリソースの利用料がかかります。作業の結果を削除しない場合、利用料が継続して発生します**

前回はEC2インスタンスを作成しましたが、このインスタンスには設定が足りなくログインすることができませんでした。そこで今回は、前回作成したStackに、SSHでログインできるようにするための設定を追加してインフラを更新しようと思います。


# 前回のテンプレートの実行

前回のテンプレートが残っている人は、それを使ってスタックを作成してください。残っていない人は、下記のテンプレートが前回の最終形なのでローカルに保存してテンプレートとして利用してください。

```json:cloudformation-study.template
{
  "Resources" : {
    "MyEC2Instance": {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : "ami-383c1956",
        "InstanceType" : "t2.micro"
      }
    }
  }
}
```

スタックを実行すると、EC2インスタンスが生成されます。

# テンプレートの修正

前回作成したテンプレートに、SSHでログインできるようにするための設定を追加します。具体的には次の二点を追加します。

* セキュリティーグループの追加
* EC2インスタンスの設定

## セキュリティーグループの追加

セキュリティーグループは、IPアドレスとポート番号によるリソースへのアクセス制限を行うものです。例えば、Webサーバーにはポート80へのアクセスのみ受け付けるようにしたり、管理サーバーには社内のネットワークからしか接続できないようにしたりする用途があります。

セキュリティーグループの定義はResourcesに行います。テンプレートの構成は次のドキュメントに記載されています。

http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html

セキュリティーグループでは、説明を記述する`GroupDescription`は必須となっています。また外部からの接続の制限を定義する`SecurityGroupIngress`に、任意の場所（0.0.0.0/0）からのSSH接続（TCP,ポート22）を許可する設定を記載します。`SecurityGroupIngress`には複数の設定を行うことができるため、[　]を使って配列として定義しています。EC2インスタンスから外部への接続は、`SecurityGroupEgress`を省略することで全ての通信が可能としておきます。

## EC2インスタンスの設定

すでに作成しているEC2インスタンスに対して、以下の設定を行います。

* セキュリティーグループの設定
* キーペアの設定

セキュリティーグループの設定は`SecurityGroupIds`を利用します。ここには先ほど作成したセキュリティーグループのIDを指定します。ただし、テンプレートの中で指定した名前をそのまま使うのではなく、テンプレートを元に生成されたAWS上のIDを使う必要があります。CloudFormationではそのようなIDを取得するためにRef関数が用意されています。Ref関数を使うと、テンプレートの中で指定された名前を元に生成されたリソースIDは`"Ref" : "テンプレートでの名前"`で取得できます。セキュリティーグループは一つのEC2インスタンスに複数定義できるため、[　]を使って配列として定義します。

キーペアの名前は、AWSのコンソールから"サービス - EC2"を選択し、左のメニューから"キーペア"を選択することで確認することができます。

## 最終テンプレート

今回使用する最終テンプレートは、次のようになります。

```json:cloudformation-study.template
{
  "Resources" : {
    "MyEC2Instance": {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : "ami-383c1956",
        "InstanceType" : "t2.micro",
        "KeyName" : "nakagaki_aws",
        "SecurityGroupIds" : [
          { "Ref" : "MyEC2SecurityGroup" }
        ]
      }
    },
    "MyEC2SecurityGroup": {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable SSH access via port 22",
        "SecurityGroupIngress" : [{
          "IpProtocol" : "tcp",
          "FromPort" : "22",
          "ToPort" : "22",
          "CidrIp" : "0.0.0.0/0"
        }]
      }
    }
  }
}
```

# スタックのアップデート

一番初めに、前回のテンプレートを使ってスタックを作成していますので、ここでは変更したテンプレートを使ってスタックを更新します。

まず初めにコンソールでCloudFormationの管理画面を開きます。すでに作成されているMyStackにチェックを入れ、"Actions - Update Stack"をクリックします。

![update01.png](https://qiita-image-store.s3.amazonaws.com/0/75307/183c9ad4-2f7b-7f85-916c-f44489132d30.png "update01.png")

次に今回修正したテンプレートをアップロードして"Next"をクリックします。

![update02.png](https://qiita-image-store.s3.amazonaws.com/0/75307/403971dc-c7d3-c59b-dd43-c75c9be92109.png "update02.png")

スタックの名前は変更できないので、そのまま"Next"をクリックします。

![update03.png](https://qiita-image-store.s3.amazonaws.com/0/75307/43f686e3-00b9-ff54-2a83-66badd8f5414.png "update03.png")

変更の結果を知らせる方法をどうするか確認してきます。ここではとりあえず、特に通知を行わないようにしましょう。何も選択していない状態で"Next"をクリックします。

![update04.png](https://qiita-image-store.s3.amazonaws.com/0/75307/88f30c48-5d15-0465-cb4c-488d5fe92f10.png "update04.png")


これで確認は完了です。"Update"をクリックするとインフラが更新されます。CloudFormationの管理画面に移動して、作業状態が"UPDATE_COMPLETE"になるまでしばらくお待ちください。

![update05.png](https://qiita-image-store.s3.amazonaws.com/0/75307/5bf32c6f-8810-abec-97e6-05751fe4f50d.png "update05.png")

## 動作確認

EC2の管理画面を開き、以下の内容を確認してください。

* セキュリティーグループが作成されていること
* 作成されたセキュリティーグループが適用されたEC2インスタンスが作成されていること
* 同じEC2インスタンスにキーペアが設定されていること

この状態であれば、おそらくコンソールからSSHで接続が行えます。

## アップデート時のEC2インスタンスについて

作業中にEC2インスタンスの状態を確認すると、古いEC2インスタンスと新しいEC2インスタンスが両方存在していることに気づくと思います。これを理解するには、リソーステンプレートの中のプロパティについて「更新に伴う要件（Update requires）」を確認してみてください。この値が「置換（Replacement）」になっているプロパティを変更した場合、スタックのアップデート時には新しいリソースを生成した後で古いリソースを削除するという動作になります。
今回は少なくともキーペア（KeyName）の更新に伴う要件が置換になっていたので、新しいEC2インスタンスの生成が行われています。

# 削除 #

これで今回の記事の作業は終了です。このインスタンスに必要がなければ、ActionsのDelete Stackを使って、生成したリソースを削除してください。