[JAWS-UG CLI] Route53:#7 Hosted Zone更新 (CloudFormation)

http://jawsug-cli.doorkeeper.jp/events/14614 でのハンズオン資料です。

CloudFormationを利用して、Route53上に独自ドメインを運用します。

今回は、http://qiita.com/tcsh/items/dcd72bfd807626fad8f0 で作成した、リソースレコードセットのないピュアなHosted Zoneに対して以下のリソースレコードセットを追加します。

* http://qiita.com/tcsh/items/3e72c7bd32d4872b972a で作成したWebサイトホスティングのS3バケットへのAliasレコード
* 上記のエイリアスレコードへのCNAMEレコード

ドメイン運用中の作業として行なうことを想定しています。


注意
======

Route53には無料使用枠がないので、下記の料金がかかります。

* http://aws.amazon.com/jp/route53/pricing/
* 作成から 12 時間以内に削除されたホストゾーンについては無料。


前提条件
========

* Route53へのフルアクセス権限
* 独自ドメインを保有。運用している。


0. 事前作業
=============

0.1. ドメインの決定
---------------------

Route53に登録されているドメインを選択します。

```bash:コマンド:
$ R53_ZONE_NAME=example.com
```

0.2. スタックの特定
--------------------

対象となるドメインのCloudFormationスタックが稼動しているリージョンを選択します。

```コマンド(東京リージョンの場合):
$ export AWS_DEFAULT_REGION='ap-northeast-1'
```

対象となるドメインが稼動しているCloudFormationスタックを特定します。

```bash:コマンド:
$ aws cloudformation list-stacks --query 'StackSummaries[].StackName'
```

0.3. 変数の定義
-----------------

表示されたスタックの一覧から、対象となるスタックを選択して、CF_STACK_NAME変数に代入します。

```bashコマンド(例):
$ CF_STACK_NAME="route53-`echo ${R53_ZONE_NAME}|sed 's/\./-/g'`-hostedzone"; echo ${CF_STACK_NAME}
```

```bash:コマンド:
$ FILE_CF_TEMPLATE="${CF_STACK_NAME}.template"
```

0.4. リポジトリへの移動
-----------------------

リポジトリが存在する場合、作業場所としてリポジトリディレクトリに移動します。

```bash:コマンド(例):
$ cd ~/_work/aws/project-01
```

1. テンプレート更新
====================

CloudFormationテンプレートを編集します。

ここではHosted Zone(リソースレコードセットなし)に、以下の設定を追加します。

* WebサイトホスティングのS3バケットへのAliasレコード
* 上記のエイリアスレコードへのCNAMEレコード

```json:変更前:
{
  "AWSTemplateFormatVersion": "2010-09-09",

  "Description": "hosted zone example.com",

  "Resources": {
    "DnsZone": {
      "Type": "AWS::Route53::HostedZone",
      "Properties": {
        "Name": "example.com"
      }
    }
  }
}
```

```text:修正部分:
11a12,32
>     },
>     "DnsRecord": {
>       "Type": "AWS::Route53::RecordSetGroup",
>       "Properties": {
>         "HostedZoneName": "example.com.",
>         "RecordSets": [
>           {
>             "Name": "www.example.com.",
>             "Type": "A",
>             "AliasTarget": {
>               "HostedZoneId": "Z2M4EHUR26P7ZW",
>               "DNSName": "s3-website-ap-northeast-1.amazonaws.com"
>             }
>           },
>           {
>             "Name": "test.example.com.",
>             "Type": "CNAME",
>             "ResourceRecords": ["www.example.com."]
>           }
>         ]
>       }
```

```json:変更後:
{
  "AWSTemplateFormatVersion": "2010-09-09",

  "Description": "hosted zone example.com",

  "Resources": {
    "DnsZone": {
      "Type": "AWS::Route53::HostedZone",
      "Properties": {
        "Name": "example.com"
      }
    },
    "DnsRecord": {
      "Type": "AWS::Route53::RecordSetGroup",
      "Properties": {
        "HostedZoneName": "example.com.",
        "RecordSets": [
          {
            "Name": "www.example.com.",
            "Type": "A",
            "AliasTarget": {
              "HostedZoneId": "Z2M4EHUR26P7ZW",
              "DNSName": "s3-website-ap-northeast-1.amazonaws.com"
            }
          },
          {
            "Name": "test.example.com.",
            "Type": "CNAME",
            "ResourceRecords": ["www.example.com."]
          }
        ]
      }
    }
  }
}
```

JSONファイルを作成したら、フォーマットが壊れてないか必ず確認します。

```bash:コマンド:
$ cat ${FILE_CF_TEMPLATE} | json_verify
```

```text:結果:
JSON is valid
```

invalidとエラーが表示された場合は、jsonlintでどこが壊れているか調べます。

```bash:コマンド:
$ cat ${FILE_CF_TEMPLATE} | jsonlint
```

validate-templateサブコマンドで、簡単な検証ができます。

```bash:コマンド:
$ aws cloudformation validate-template --template-body file://${FILE_CF_TEMPLATE}
```


2. スタック(Hosted Zone)更新
============================

2.1. スタック(Hosted Zone)更新
-------------------------------

```bash:コマンド:
$ aws cloudformation update-stack --stack-name ${CF_STACK_NAME} --template-body file://${FILE_CF_TEMPLATE}
```


2.2. スタックの内容確認
-------------------------

スタックの更新状況を確認します。

```bash:コマンド:
$  aws cloudformation list-stacks \
  | jq -r --arg stackname ${CF_STACK_NAME} '.StackSummaries[] | select(.StackName == $stackname) | .StackStatus'
```

```text:結果:
UPDATE_COMPLETE
```

StackStatusが’UPDATE_COMPLETE’になっていれば作成は完了です。 (30秒から1分程度かかるようです。)

それ以外が表示されている場合は、下記コマンドでFailedの文字が出ている前後を見て原因を調べます。

```bash:コマンド:
$ aws cloudformation describe-stack-events --stack-name ${CF_STACK_NAME}
```


2.3. スタックの内容確認
------------------------

更新されたスタックの内容を確認します。

```bash:コマンド:
$ aws cloudformation get-template --stack-name ${CF_STACK_NAME}
```

result:

3. リソースレコードセットの確認
==============================

3.1. Hosted Zone IDの取得

```bash:コマンド:
$ R53_ZONE_ID=`aws route53 list-hosted-zones | jq --arg zone_name ${R53_ZONE_NAME} -r '.HostedZones[]|select(.Name |contains($zone_name))|.Id' | sed 's/\/hostedzone\///'` && echo ${R53_ZONE_ID}
```

```text:結果:
ZYXXXXXXXXXXX
```

3.2. リソースレコードセットの確認
--------------------------------

```bash:コマンド:
$ aws route53 list-resource-record-sets --hosted-zone-id ${R53_ZONE_ID}
```

```json:結果:
{
  "ResourceRecordSets": [
      {
          "ResourceRecords": [
              {
                  "Value": "ns-XXXX.awsdns-XX.org."
              },
              {
                  "Value": "ns-XXX.awsdns-XX.com."
              },
              {
                  "Value": "ns-XXX.awsdns-XX.net."
              },
              {
                  "Value": "ns-XXXX.awsdns-XX.co.uk."
              }
          ],
          "Type": "NS",
          "Name": "example.com.",
          "TTL": 172800
      },
      {
          "ResourceRecords": [
              {
                  "Value": "ns-XXXX.awsdns-XX.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
              }
          ],
          "Type": "SOA",
          "Name": "example.com.",
          "TTL": 900
      },
      {
          "ResourceRecords": [
              {
                  "Value": "www.example.com."
              }
          ],
          "Type": "CNAME",
          "Name": "test.example.com.",
          "TTL": 900
      },
      {
          "AliasTarget": {
              "HostedZoneId": "Z2M4EHUR26P7ZW",
              "EvaluateTargetHealth": false,
              "DNSName": "s3-website-ap-northeast-1.amazonaws.com."
          },
          "Type": "A",
          "Name": "www.example.com."
      }
  ]
}
```


4. リソースレコードの確認
==========================

自分のドメインの各種レコードが、Route53の登録情報と同じであることを確認します。

```bash:コマンド:
$ dig www.${R53_ZONE_NAME} |grep www.${R53_ZONE_NAME}
```

```text:結果:
; <<>> DiG 9.8.3-P1 <<>> www.example.com
;www.example.com.             IN      A
www.example.com.      42      IN      A       54.231.226.91
```

```bash:コマンド:
$ dig cname test.${R53_ZONE_NAME} |grep test.${R53_ZONE_NAME}
```

```text:結果:
; <<>> DiG 9.8.3-P1 <<>> cname test.example.com ;test.example.com. IN CNAME test.example.com. 891 IN CNAME www.example.com.
```