AWS CLIで Web サイトを構築、管理、運用する(6日目)

5日目では、コンテンツ配信ネットワークのサービスである、CloudFront を導入しました。
6日目は、ついに、Web サイトに独自ドメインを割り当てます!

# 6日目の要約
独自ドメインを使うことで、アクセスしやすくするよ！

# AWS CLI の準備
このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。
なんなら、 AWS CloudShell で実行するのも楽でよいと思います。
この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。

バージョン１
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html

バージョン２
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html

# 概要
Route 53 に独自ドメインを設定して Web サイトにアクセスできるようにするよ！

# さあ、やってみよう！
## 独自ドメインの取得
　何は無くともまずは、独自ドメインを取得しましょう。無料ドメインで試行するのであれば、[freenom](https://www.freenom.com/ja/index.html?lang=ja)でどうぞ。
.tk、.ml、.ga、.cf　、.gq ドメインが無償で取得できます。

.com や .net などを取りたい方はお名前.comなどで。

## Route53 のホストゾーンを作成する
独自ドメインを取得しても利用可能な状態いしないといけません。
というわけで、Route53 で取得したドメインを利用可能な状態にします。

まずは、ホストゾーンを作成します。route53 create-hosted-zone コマンドを使用します。

```sh
# caller-reference オプションのためにユニークキー(uuid)を作成しておく
uuid=`uuidgen`

aws route53 create-hosted-zone ¥
--name <取得したドメイン名> ¥
--caller-reference ${uuid}
```

コマンド実行が成功すると以下のように出力されます。
このあと、DelegationSet 内の NameServers を使うので確認しておきます。

```json
{
    "Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/Z0*******************",
    "HostedZone": {
        "Id": "/hostedzone/Z0*******************",
        "Name": "<domain-name>",
        "CallerReference": "<uuid>",
        "Config": {
            "PrivateZone": false
        },
        "ResourceRecordSetCount": 2
    },
    "ChangeInfo": {
        "Id": "/change/C00*******************",
        "Status": "PENDING",
        "SubmittedAt": "YYYY-MM-DDThh:mm:ss.000000+00:00"
    },
    "DelegationSet": {
        "NameServers": [
            "ns-***.awsdns-**.com",
            "ns-****.awsdns-**.co.uk",
            "ns-***.awsdns-**.net",
            "ns-****.awsdns-**.org"
        ]
    }
}
```

## ドメイン取得元で ネームサーバの設定を行う
freenom や お名前.comなどで設定を行います。
freenom であれば、取得したドメイン名を画面上部の Services > My Domains から表示し、Manage Domainボタンをクリックします。
そして、Management Tools > Nameservers を選択し、Use custom nameservers (enter below)から、先ほど確認したネームサーバを入力して保存します。

## Certificate Manager(ACM) を使って、証明書作成のリクエストを行う
5日目のまとめでお伝えした通り、CloudFront を使用すると https で通信が可能です。
すごくざっくりといえば、独自ドメインでの暗号化のための証明書が必要です。
そこで、ACM を使って証明書をリクエスト、作成します。

この際、気をつけないとならないのは、「バージニア北部(us-east-1)」リージョンでないと、CloudFrontの独自ドメイン用の証明書として利用できないことです。
コマンドは acm request-certificate を使用します。
また、証明書の認証方法は DNS を用います。併せて、バージニア北部リージョンを指定します。

```sh
aws acm request-certificate --domain-name <ドメイン名> \
--validation-method DNS --region us-east-1
```

正常に終了すると、以下のように出力されます。

```json
{
    "CertificateArn": "arn:aws:acm:us-east-1:************:certificate/********-****-****-****-************"
}
```

## ドメイン認証をするために、Route53 にレコードを追加する
証明書のリクエストを行うと、ドメイン認証が済むまで保留状態になります。
acm describe-certification コマンドを使って、ドメイン認証に必要な情報を確認します。

```sh
aws acm describe-certificate --certificate-arn <証明書リクエストのARN> --region us-east-1
```

コマンドが正常に実行できると以下の json が返されます。
DomainValidationOptions の ResourceRecord に入っている、NAME と Value が必要な情報です。

```json
{
    "Certificate": {
        "CertificateArn": "arn:aws:acm:us-east-1:************:certificate/********-****-****-****-************",
        "DomainName": "<ドメイン名>",
        "SubjectAlternativeNames": [
            "ドメイン名"
        ],
        "DomainValidationOptions": [
            {
                "DomainName": "ドメイン名",
                "ValidationDomain": "ドメイン名",
                "ValidationStatus": "PENDING_VALIDATION",
                "ResourceRecord": {
                    "Name": "この後必要になる情報１",
                    "Type": "CNAME",
                    "Value": "この後必要になる情報2"
                },
                "ValidationMethod": "DNS"
            }
        ],
        "Subject": "CN=ドメイン名",
        "Issuer": "Amazon",
        "CreatedAt": "YYYY-MM-DDThh:mm:ss.000000+00:00",
        "Status": "PENDING_VALIDATION",
        "KeyAlgorithm": "RSA-2048",
        "SignatureAlgorithm": "SHA256WITHRSA",
        "InUseBy": [],
        "Type": "AMAZON_ISSUED",
        "KeyUsages": [],
        "ExtendedKeyUsages": [],
        "RenewalEligibility": "INELIGIBLE",
        "Options": {
            "CertificateTransparencyLoggingPreference": "ENABLED"
        }
    }
}
```

acm describe-certificate　コマンドの実行結果から、DomainValidationOptions の ResourceRecord に入っている、NAME と Value を使って、 Route53 に CNAME レコードを作成し、認証できる状態にします。
まずは、レコード作成用の json を以下のように作成します。

```json:CNAME.json
{
  "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "前述の「この後必要になる情報１」",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [{
            "Value": "前述の「この後必要になる情報2」"
        }]
      }
  }]
}

```

作成した json ファイルをインプットにして、 route53 change-resource-record コマンドを実行します。
ホストゾーンを作成した際の ホストゾーンIDの確認をお忘れなく！

```sh
aws route53 change-resource-record-sets --hosted-zone-id <ホストゾーンのID>  \
--change-batch file://CNAME.json
```

正常に実行できると以下のような json が返却されます。

```json
{
    "ChangeInfo": {
        "Id": "/change/C0******************",
        "Status": "PENDING",
        "SubmittedAt": "YYYY-MM-DDThh:mm:ss.000000+00:00"
    }
}
```

## ドメイン認証のステータスを確認する
概ね、数分以内にはドメイン認証が完了しますが、念のため、 acm describe-certificate コマンドで状況を確認します。

```sh
aws acm describe-certificate --certificate-arn <証明書の ARN> --region us-east-1
```

正常に成功したら、json が返るので DomainValidationOptions 内の ValidationStatus の値を確認します。 SUCCESS になっていればドメイン認証が成功しています。

```json
(省略)
        "DomainValidationOptions": [
            {
                "DomainName": <ドメイン名>",
                "ValidationDomain": "<ドメイン名>",
                "ValidationStatus": "SUCCESS",
（省略）
```

## CloudFront の設定を変更する準備
証明書が作成できたら、次に、 CloudFront の設定を変更します。
現在の設定情報をもとに、変更を加えるので、 cloudfront get-distribution-config コマンドを実行します。

```sh
aws cloudfront get-distribution-config --id <CloudFront の Distribution ID> > distributionconfig_tmp.json
```

ファイルに書き込みが行えたら、次に、必要な部分だけ抽出して更新用の元ファイルにします。jq コマンドを使うので、必要に応じてインストールしてください。AWS CloudShell であれば標準でインストール済みです。

```sh
cat distributionconfig_tmp.json | jq .DistributionConfig > distributionconfig.json
```

また、get-distribution-config の出力内容のうち、 Etag の値が必要なので確認しておきます。

```sh
cat distributionconfig_tmp.json | grep ETag
```

以下のように出力されます。

```json
    "ETag": "**************",
```

さて、distributionconfig.json　を編集し、Distribution の更新が行えるように準備します。
以下の属性を編集します。

* Aliases
* ViewerCertificate

### Aliases の見本
独自ドメインを指定します。
未設定の場合は、以下のようになっています。

```json
        "Aliases": {
            "Quantity": 0
        },
```

以下のように設定します。

```json
        "Aliases": {
          "Quantity": 1,
          "Items": [
            "<独自ドメイン名>"
          ]
        },
```

### ViewerCertificate の見本

証明書の情報を指定します。
未設定の場合は、以下のようになっています。

```json
        "ViewerCertificate": {
            "CloudFrontDefaultCertificate": true,
            "MinimumProtocolVersion": "TLSv1",
            "CertificateSource": "cloudfront"
        },
```

以下のように設定します。

```json
        "ViewerCertificate": {
          "ACMCertificateArn": "<証明書のARN>",
          "SSLSupportMethod": "sni-only",
          "MinimumProtocolVersion": "TLSv1.2_2021",
          "Certificate": "<証明書のARN>",
          "CertificateSource": "acm"
        },
```

さらに、本ファイルに対して、以下のように頭と末尾に情報を入力します。

```json:distributionconfig.json_頭
{
"DistributionConfig": {
  "CallerReference": 

（省略）
```
1行目に波括弧({)があるので、その2行目に対して、 ```“DistributionConfig”: {``` を追加します。
次に末尾です。

```json:distributionconfig.json_末尾
(省略)
  "WebACLId": "",
  "HttpVersion": "http2",
  "IsIPV6Enabled": true
 }
}
```

末尾に閉じ波括弧(}）を追加します。


## CloudFront の設定を変更する
準備ができたら、 cloudfront update-distribution コマンドを実行して、設定変更を行います。

```sh
aws cloudfront update-distribution --id <CloudFront Distribution ID> --cli-input-json file://distributionconfig.json --if-match 確認したETag の値
```
コマンド実行に成功すると、Distribution の内容が記された json が返されます。

これで、CloudFront に独自ドメイン名と証明書を割り当てることができました。


## Route53 のホストゾーンにエイリアスレコードを作成する
ホストゾーンを作成し、ネームサーバの設定を行い、そして、CloudFrontの設定を行ったので、Route53 に名前解決用のレコードを登録します。
コマンドは route53 change-resource-record を使用します。

コマンド実行前に、レコードの情報が入った json ファイルを作成しておきます。
HostedZoneId は AWSが管理/利用している Route53 のHostedIdです。こちらの[マニュアル](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-route53-aliastarget.html)から確認できます。

```json:record.json
{
  "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "<ドメイン名>",
        "Type": "A",
            "AliasTarget": {
               "HostedZoneId": "Z2FDTNDATAQYW2",
               "DNSName": "<CloudFrontのディストリビューションドメイン名>",
               "EvaluateTargetHealth": false
            }
      }
  }]
}
```

ファイルが完成したら、 route53 change-resource-record-sets コマンドを実行します。

```sh
aws route53 change-resource-record-sets --hosted-zone-id <ホストゾーンのID>  \
--change-batch file://record.json
```

コマンドが正常に実行できたら、以下のように出力されます。

```json
{
    "ChangeInfo": {
        "Id": "/change/C0*******************",
        "Status": "PENDING",
        "SubmittedAt": "YYYY-MM-DDThh:mm:ss.000000+00:00"
    }
}
```

これで独自ドメインを Route53 に登録できました。
独自ドメインを使ってアクセスしてみましょう。

## 動作確認
いつも通り、 curl コマンドで確認してみます。


```sh
curl https://<独自ドメイン名>/
```

名前解決ができ、各種設定に問題がなければ3日目に作成した HTML が表示されるはずです。

```HTML
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>Advent calendar 2021</title>
</head>
<body>
  <h1>Hello world!!</h1>
  <h2>Advent calendar 2021 DAY 3</h2>
</body>
</html>
```

# まとめ
6日目にしてついに、独自ドメインかつHTTPS化にたどり着きました！
今回は、 3種類のサービスについて AWS CLI で操作しました。結構色々なコマンドを実行したのではないでしょうか？

7日目以降は、CloudFront + S3 となっている状況なので、設定を見直していきます。

* 今回使ったコマンド
 * route53 create-hosted-zone
 * acm request-certificate
 * acm describe-certificate
 * route53 change-resource-record-sets
 * acm describe-certificate
 * cloudfront get-distribution-config
 * cloudfront update-distribution