AWSにてドメインを取得した後、Terraformを使用してAWS Certificate Managerの証明書を発行した

# 前書き
表題の構築を行ったので、備忘録も兼ねて記載します。

# AWS Certificate Managerの概要
- ウェブサイトやアプリケーションのセキュリティを強化する為に、**SSL/TLS証明書**の発行、管理、展開を自動化するサービス
- ウェブ通信の暗号化が簡単に実現、データの機密性を確保、なりすましを防止が可能になる
- ACMで発行された証明書は自動で更新される為、証明書の有効期限を気にする必要がない

## 主な機能
### 証明書の発行と管理
- 無料での証明書発行: `ELB`、`CloudFront`、`API Gateway`等の`AWS`リソースで利用可能
- 自動更新: 証明書の有効期限切れの心配が不要

### ドメイン検証
- DNS検証: 自動化が可能で、手動操作を減らし、証明書の再発行や更新時の再検証が不要
- Email検証: 手動で確認する必要があり、`DNS`検証よりも手間がかかる

### AWSサービスとの統合
- `Elastic Load Balancing (ELB)`: HTTPS通信の実現
- `Amazon CloudFront`: セキュアなコンテンツ配信
- `Amazon API Gateway`: APIエンドポイントの暗号化

### セキュリティの向上
- 通信の暗号化によるデータ保護
- デジタル証明書で通信相手の正当性を保証

### 料金
- 無料: ACMで発行する証明書は無料
- 有料証明書のインポート: 他の認証機関から購入した証明書の管理も可能（ただし自動更新は不可）

# ネームサーバーの概要
ドメイン名とIPアドレスを関連付ける役割を担うサーバー。これにより、人間が覚えやすいドメイン名を入力する事で、インターネット上の特定のサーバーやウェブサイトへアクセスを出来る様にする

## 主な機能
### ドメイン名とIPアドレスの変換
- ドメイン名をIPアドレスに変換し、ウェブブラウザや他のアプリケーションが適切なサーバーに対して接続を出来る様にする
### DNSゾーン情報の管理
- DNSゾーン情報を保持し、レコード（Aレコード、CNAMEレコードなど）を管理する
### 名前解決の委任
- 上位のネームサーバーから委任されたゾーンの名前解決を行い、問い合わせに応じて情報を提供する
### 高可用性と負荷分散
- 複数のネームサーバーを配置して、サービスが常に利用できる状態を保つ
- 1つのサーバーが故障しても他のサーバーが代わりに動作し、サービスを継続出来る
- 複数のサーバーに対して、アクセスの負荷を分散させる事で、処理速度を維持し、過負荷によるパフォーマンス低下を防ぐ
### キャッシング
- 一時的に名前解決の結果をキャッシュし、再度の問い合わせ時に応答速度を向上させる

## レコードの種類と説明
### Aレコード (Address Record)
- ドメイン名を<font color="Red"> **IPv4アドレスに紐づける** </font>レコード
- `example.com`を`192.0.2.1`のようなIPアドレスへ変換する為に使用する
- ドメイン名を入力する事で指定のサーバーへアクセスが出来る様になる

### CNAMEレコード (Canonical Name Record)
- あるドメイン名を<font color="Red"> **別のドメイン名にエイリアスとして関連付ける** </font>レコード
- `www.example.com`を`example.com`にマッピングする事で、`example.com`のIPアドレスが変わった際に`www.example.com`も自動的にその新しいアドレスを参照する様になる
- CNAMEレコードを設定する事で、管理が容易になる

# 前提条件
- `Terraform`の実行環境がある
- `AWS`の操作権限がある
- `Terraform`のバージョンは、`v1.5.0`以上
- 著者は、[AWS-Vault](https://github.com/99designs/aws-vault)を使用して、`AWS`の認証関連の構築を行っている

# 構築方法
##  ドメインの発行
- 下記の公式の手順に従い、新規ドメインを作成する
- 構築が完了すると`Route53`のホストゾーンに作成したドメインが表示される

https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/domain-register.html

## 基本構成
```main.tf
module "route53" {
  source = "../../modules/route53"
}

module "acm" {
  source = "../../modules/ssl"
}
```

## ディレクトリ構成
```sh
.
├── .terraform/
│   └── # terraformに必要なファイル、ディレクトリが自動生成される
├── .terraform.lock.hcl # 自動生成
├── .gitignore
├── providers.tf
├── variables.tf
├── terraform.tfvars
├── main.tf
└── modules/
    ├── ssl/
    │   ├── acm.tf
    │   ├── variables.tf
    │   └── output.tf
    └── route53/
        ├── route53.tf
        ├── variables.tf
        └── output.tf
```

## Route53
- `ACM`証明書の作成を完了させる為には、`Route53`のzone_idが必要になる為、最初に構築を行う
- `Terrform`の`import`機能を使い、ホストゾーンの情報とTerraformを紐付ける

```sh
# 構文
terraform import aws_route53_zone.example ホストゾーン ID

# 入力例
terraform import aws_route53_zone.example Z0**************4I
```
- 下記のコマンドでimportしたaws_route53_zoneの詳細内容を確認
```sh
# 詳細確認
terraform state show route53.aws_route53_zone.example

# 表示内容
# module.route53.aws_route53_zone.example:
resource "aws_route53_zone" "example" {
    arn                 = "arn:aws:route53:::hostedzone/Z0**************4I"
    comment             = "Managed by Terraform"
    force_destroy       = false
    id                  = "Z0**************4I"
    name                = "example.jp"
    name_servers        = [
        "ns-****.awsdns-**.org",
        "ns-****.awsdns-**.co.uk",
        "ns-***.awsdns-**.com",
        "ns-***.awsdns-**.net",
    ]
    primary_name_server = "ns-***.awsdns-**.com"
    tags                = {}
    tags_all            = {}
    zone_id             = "Z0**************4I"
}
```

- `terraform state show`で取得した内容を下記に貼り付ける
- 必須は、`name`のみである為、ドメインのみ記載する 
```route53.tf
resource "aws_route53_zone" "example" {
  name = "example.jp"
}
```

## ACM
```acm.tf
# ACM (AWS Certificate Manager) に新しいSSL/TLS証明書を作成する
resource "aws_acm_certificate" "example" {
  domain_name               = "example.jp"
  validation_method         = "DNS"
  subject_alternative_names = ["*.example.jp"]
  lifecycle {
    create_before_destroy = true
  }   
}


# Route 53のホストゾーンに関する情報を取得するためのTerraformデータソースを定義
data "aws_route53_zone" "example" {
  name         = "example.com"
  private_zone = false
}

# Route 53にCNAMEレコードを作成して、ACM証明書のDNS検証に必要な設定を行う
resource "aws_route53_record" "example" {
    
    # ACM証明書のドメイン検証のオプションをループさせて、それぞれのドメイン名をキーにし、検証レコード情報をマッピングする
    # 各ドメイン名に対して必要なレコード名、レコードの値、レコードタイプをfor_eachブロックに格納し、Route 53のDNSレコードを作成する際に使用する
  for_each = {
    for dvo in aws_acm_certificate.example.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  allow_overwrite = true
  name            = each.value.name
  records         = [each.value.record]
  ttl             = 60
  type            = each.value.type
  zone_id         = data.aws_route53_zone.example.zone_id
}

# ACM証明書のDNS検証を完了させる
resource "aws_acm_certificate_validation" "example" {
  certificate_arn         = aws_acm_certificate.example.arn
  validation_record_fqdns = [for record in aws_route53_record.example : record.fqdn]
}
```
### aws_acm_certificate内で使用しているメソッドと説明

| メソッド/設定項目 | 説明 |
| :- | - |
| `domain_name` | 証明書を適用するドメイン名を指定する |
| `validation_method` | 証明書の有効性を確認する方法を指定する。今回は、`DNS`を使用する |
| `subject_alternative_names` | 証明書に追加のサブドメイン（ワイルドカードなど）を指定する |
| lifecycle.create_before_destroy | 新しいリソースを作成してから古いリソースを削除する |

### aws_route53_record内で使用しているメソッドと説明

| メソッド/設定項目 | 説明 |
| :- | - |
| `for_each` | `aws_acm_certificate`の`domain_validation_options`をループ処理し、ドメイン名をキーにして、レコード情報を作成する |
| `allow_overwrite` | レコードの上書きを許可する設定。`true`に設定する事で、既存のレコードを上書きできる |
| `name` | `DNS`レコードの名前を指定。ここでは `each.value.name`によりループ処理の値を使用する |
| `records` | レコードの値を指定する。`each.value.record`でループ処理で取得した値を配列として設定する |
| `ttl` | `Time To Live`(TTL)、レコードの有効期間を秒単位で指定する。今回は、60 秒を設定した |
| `type` | レコードのタイプを指定する。ループ処理で取得した `each.value.type`を使用する |
| `zone_id` | `Route 53`のホストゾーンの`ID`を指定する。`data.aws_route53_zone.example.zone_id`を使用して指定されたゾーンに登録する |

### aws_acm_certificate_validation内で使用しているメソッドと説明

| メソッド/プロパティ | 説明<br> |
| - | - |
| `certificate_arn` | `ACM`証明書の`ARN (Amazon Resource Name)`を指定し、この証明書を検証する為に使用する |
| `validation_record_fqdns` | 検証に使用する完全修飾ドメイン名(`FQDN`)のリストを指定する。`aws_route53_record.example`の`FQDN`をループして抽出し、`ACM`の`DNS`検証に使用する |

# 補足
## FQDN、ドメイン、リファラーの違いに関する比較表

| 用語 | 表示例 | 説明 |
| - | - | - |
| **FQDN (Fully Qualified Domain Name)** | `www.example.com.` | 完全修飾ドメイン名の略で、特定のホストをインターネット上で一意に識別するための完全な名前。例えば、`www.example.com.`のようにトップレベルドメインまで含めた名前を示す |
| **ドメイン (Domain)** | `example.com` | ネットワーク内で識別される名前の範囲。`example.com`や`example.org`のように、ウェブサイトやメールサービスの基盤となる名前。FQDNはこのドメイン名を含むが、必ずしもホスト名まで含まない事もある |
| **リファラー (Referrer)** | `https://source.example.com/page` | ウェブブラウザやHTTPリクエストで、あるウェブページにアクセスした際に、そのアクセス元のURLを示す情報。リンクをクリックした時に、どのページから来たかをサーバーに知らせる目的で使用する |

# 参考資料
- [[AWS公式] AWS Certificate Manager](https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/acm-overview.html)
- [[HashiCorp] Resource: aws_route53_zone](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_zone)
- [[HashiCorp] Resource: aws_acm_certificate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate)
- [[HashiCorp] Resource: aws_acm_certificate_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation)
- [[AWS公式] 新しいドメインの登録](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/domain-register.html)
- [【初心者】Amazon Route53 でドメインを取得する](https://qiita.com/mksamba/items/e50a423b8ab12271eb73)
- [Route 53に登録済みのドメインをTerraformで管理する](https://qiita.com/kaitokimuraofficial/items/14189186b5a3508e989b)
- [【AWS】ACM SSL/TLS認証ステータス保留中の検証のまま変わらない](https://qiita.com/mshkki/items/20948636bb4b00640d09)
- [Amazon Route 53で取得したドメインを使って、AWS Certificate Managerで証明書を発行してみた](https://dev.classmethod.jp/articles/issuing-certificate-with-acm/)

# まとめ
今回は、ドメイン発行やACMの設定を行いました。ドメインの知識や証明書に関する事が理解出来てとても良い経験になりました。