お名前ドットコムで取得した独自ドメインでAWSにhttpsのサイトを公開 with terraform

# 手軽にサイトを公開したい
サイトを作りたいとか作って欲しいという依頼はよくある
この時の手軽さというのはかなり大切になる
手軽に再現性がある方法を紹介する
ただしドメインはお名前ドットコムで取得した場合のことを考える

# 構成
![a.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221536/5acff35a-a30e-c6a3-0931-c5a7faae3f88.png)
詳細は記事内で説明する


# ドメインの取得
[お名前ドットコム](https://www.onamae.com/?utm_source=Google&utm_medium=paidsearch&banner_id=102_kwt_domain_domainbrand-onamae_rsa_006&waad=Ev0lmQOs&flow=normal&gad=1&gclid=CjwKCAjwu4WoBhBkEiwAojNdXrcZURsO7WSKoQ9HO-Gx0FGyme6pM3jZestPDERdM1NOpCU5GIJeOBoCF8IQAvD_BwE)で独自ドメインを取得する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221536/280a6dba-55e4-6b0b-0b19-366239832f04.png)


# Route53との紐づけ
[お名前.comで取得したドメインをRoute53のネームサーバで管理設定してみた](https://dev.classmethod.jp/articles/route53-domain-onamae/)
お名前.comで取得したドメインのホストゾーンをRoute53で作成することで、独自ドメインをAWSで設定できるようにする
Route53のホストゾーンの作成はterraformで行う
```terraform:hostzone.tf
resource "aws_route53_zone" "host_zone" {
  provider     = aws.virginia
  name         = var.site_domain
}
```
作成が完了したらAWSマネジメントコンソールでRoute53にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221536/956c0839-d3fb-18bd-d21c-7410c8fb5710.png)
ホストゾーンが作成されているので、その中から4件のNSレコードをお名前ドットコムに登録することで、AWSとお名前ドットコムを繋げる
※お名前ドットコムで登録が完了するまでは時間がかかる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221536/dc4d0b59-0bf1-65af-ef4c-6998242ef68b.png)


# ネームサーバの変更確認
```
nslookup -type=NS [取得したドメイン名]
```
ネームサーバにAWSの値が表示されれば変更が完了している。


# ACMの発行
https通信を行うためにACMを発行する
ACMの発行はterraformで行う
先ほど追加した4件のNSレコードが必要となるステップ
証明書というのはただ発行してもドメインの所有者である証拠が無い、つまり証明書に効力が無い状態
例えばGoogleの証明書であっても発行するだけであれば誰でも自由に出来る
本当にドメインの所有者である場合は任意のDNS設定をできるはず
そのためAWSが指定するレコードを登録することを、AWSが確認できた時に初めて証明書を有効化することができる
```terraform:acm.tf
# https通信のためにACM証明書を定義
resource "aws_acm_certificate" "cert" {
  provider          = aws.virginia
  domain_name       = var.site_domain
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }

  tags = {
    Environment = var.environment
  }
}

# ACMのDNS検証用レコードの生成
resource "aws_route53_record" "cert" {
  for_each = {
    for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }
  provider        = aws.virginia
  allow_overwrite = true
  name            = each.value.name
  type            = each.value.type
  records         = [each.value.record]
  zone_id         = aws_route53_zone.host_zone.zone_id
  ttl             = 60

  depends_on = [aws_acm_certificate.cert, aws_route53_zone.host_zone]
}

# ACMのDNS検証用レコードのチェック
resource "aws_acm_certificate_validation" "cert" {
  provider                = aws.virginia
  certificate_arn         = aws_acm_certificate.cert.arn
  validation_record_fqdns = flatten([values(aws_route53_record.cert)[*].fqdn])

  depends_on = [aws_acm_certificate.cert, aws_route53_record.cert]
}
```


# S3バケットの作成
公開したいリソースを置くための場所を作成する。
```terraform:s3.tf
resource "aws_s3_bucket" "s3_static_bucket" {
  bucket = "${var.project}-${var.environment}-${var.site_domain}"
  force_destroy = true
}

# CloudFrontからのオリジンアクセスアイデンティティ付きアクセスに対してReadのみを許可する
resource "aws_s3_bucket_policy" "s3_static_bucket" {
  bucket = aws_s3_bucket.s3_static_bucket.id
  policy = data.aws_iam_policy_document.s3_static_bucket.json
  depends_on = [ aws_s3_bucket_public_access_block.s3_static_bucket ]
}

data "aws_iam_policy_document" "s3_static_bucket" {
  statement {
    effect    = "Allow"
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.s3_static_bucket.arn}/*"]
    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.site.iam_arn]
    }
  }
}

resource "aws_s3_bucket_public_access_block" "s3_static_bucket" {
  bucket                  = aws_s3_bucket.s3_static_bucket.id
  block_public_acls       = true
  block_public_policy     = false
  ignore_public_acls      = true
  restrict_public_buckets = false
}
```


# cloudfrontの作成
https通信のために発行したACM(SSL証明書)をアタッチするためにcloudfrontを利用(S3にはアタッチ不可能)
また独自ドメインとcloudfrontを紐づけることで、独自ドメインでのアクセスを可能とする
```terraform:cloudfront.tf
# CloudFrontの配信元の識別子
locals {
  s3_origin_id = "s3-origin-${var.site_domain}"
}

# PrivateなS3 Bucketにアクセスするためにオリジンアクセスアイデンティティを利用する
resource "aws_cloudfront_origin_access_identity" "site" {
  comment = local.s3_origin_id
}

# CloudFrontのディストリビューション設定
resource "aws_cloudfront_distribution" "s3_distribution" {
  origin {
    domain_name = aws_s3_bucket.s3_static_bucket.bucket_regional_domain_name
    origin_id   = local.s3_origin_id
    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.site.cloudfront_access_identity_path
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = var.site_domain
  default_root_object = "index.html"

  aliases = [var.site_domain]

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = local.s3_origin_id

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  price_class = "PriceClass_200"

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  tags = {
    Environment = "${var.environment}"
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate_validation.cert.certificate_arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1"
  }
}

#Aliasレコード
resource "aws_route53_record" "site" {
  zone_id = aws_route53_zone.host_zone.zone_id
  name    = var.site_domain
  type    = "A"

  alias {
    name                   = aws_cloudfront_distribution.s3_distribution.domain_name
    zone_id                = aws_cloudfront_distribution.s3_distribution.hosted_zone_id
    evaluate_target_health = false
  }
}
```


# ブラウザから独自ドメインによるhttps通信ができるか確認
今回作成したS3バケットにルートにindex.htmlを配備して、アクセスしてみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221536/3570d803-cdbe-1947-bbb1-8edd03977cf9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1221536/52144fd3-b673-9e50-ef93-b0da4118acf8.png)
問題無くアクセスできた


# ドメイン取得をRoute53にする方が気軽
今回はAWSとお名前ドットコムの2画面を触る必要があった
terraformだけで完結させる場合、ドメインの取得をお名前ドットコムではなくRoute53にする必要がある
ここはコストとの兼ね合いとなるが、気軽さを追求するならRoute53でのドメイン取得に軍配が上がる
[お名前.comからAmazon Route 53へドメインを移管](https://dev.classmethod.jp/articles/onamae-to-route53/)