Hugo + CloudFront + S3 + GitHub Actions でCI/CD付きブログサイトを構築してみる Part3 terraformでAWSリソースの作成

## 概要
参考サイトのcloudformationの分解が完了したので、今回からterraformに落とし込みます。
まずs3バケット(静的)/s3バケット(CloudFrontログ保存用)/CloudFrontの３つです。
公式リファレンスと自分の記事をもとに作成します。

https://qiita.com/Tanamachi_kaoru/items/0686b3a451a5d86d9196

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185852/b017d04e-b7b4-c304-eef1-6d3a7cd9d9f1.png)
この記事はそのメモやまとめです。
すでに設定済みの項目がいくつかある。
試行錯誤しながらやったので、手順として不要かもしれない。**あくまでメモ用。**

# ざっくりとした流れ
s3バケット(静的サイト) → s3バケット(CloudFrontログ保存用) → cloudfront → s3バケット(静的サイト)でOAC作成 
の順でリソースの作成を行いました。
tfファイルを作成してすぐにterraformで
```
terraform init
terraform validate
terraform fmt
terraform plan
terraform apply
```
を実行してリソースの作成を行いました。

# 完成した資材
<details>
<summary>s3バケット(静的サイト)</summary>

```terraform
resource "aws_s3_bucket" "s3_hugo" {
  bucket = "hogehoge-hugo"
}

# オブジェクト所有者を「バケット所有者の強制」にすることでACLの無効化
resource "aws_s3_bucket_ownership_controls" "s3_hugo" {
  bucket = aws_s3_bucket.s3_hugo.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

# パブリックアクセスをブロック
resource "aws_s3_bucket_public_access_block" "s3_hugo" {
  bucket = aws_s3_bucket.s3_hugo.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# バージョニング設定
resource "aws_s3_bucket_versioning" "s3_hugo" {
  bucket = aws_s3_bucket.s3_hugo.id

  versioning_configuration {
    status = "Disabled"
  }
}

# s3側での暗号化
resource "aws_s3_bucket_server_side_encryption_configuration" "s3_hugo" {
  bucket = aws_s3_bucket.s3_hugo.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# lifecycleの設定
resource "aws_s3_bucket_lifecycle_configuration" "s3_hugo" {
  bucket = aws_s3_bucket.s3_hugo.id

  rule {
    id     = "purge-noncurrent-version"
    status = "Enabled"

    abort_incomplete_multipart_upload {
      days_after_initiation = 7
    }

    noncurrent_version_expiration {
      newer_noncurrent_versions = 3
      noncurrent_days           = 1
    }
  }
}

# OAC バケットポリシー
resource "aws_s3_bucket_policy" "static_hosting" {
  bucket = aws_s3_bucket.s3_hugo.id
  policy = data.aws_iam_policy_document.bucket_static_hosting.json
}

data "aws_iam_policy_document" "bucket_static_hosting" {
  version = "2012-10-17"
  statement {
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.s3_hugo.arn}/*"]
    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudfront_distribution.hugo_distribution.arn]
    }
  }
}
```
</details>
<details>
<summary>s3バケット(CloudFrontログ保存用)</summary>

```terraform
resource "aws_s3_bucket" "s3_cloudfront_log" {
  bucket = "hogehoge-hugo-cloudfront-log"
}

# ACL有効
resource "aws_s3_bucket_ownership_controls" "s3_cloudfront_log" {
  bucket = aws_s3_bucket.s3_cloudfront_log.id

  rule {
    object_ownership = "ObjectWriter"
  }
}

# パブリックアクセスをブロック
resource "aws_s3_bucket_public_access_block" "s3_cloudfront_log" {
  bucket = aws_s3_bucket.s3_cloudfront_log.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# バージョニング設定
resource "aws_s3_bucket_versioning" "s3_cloudfront_log" {
  bucket = aws_s3_bucket.s3_cloudfront_log.id

  versioning_configuration {
    status = "Enabled"
  }
}

# s3側での暗号化
resource "aws_s3_bucket_server_side_encryption_configuration" "s3_cloudfront_log" {
  bucket = aws_s3_bucket.s3_cloudfront_log.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# lifecycleの設定
resource "aws_s3_bucket_lifecycle_configuration" "s3_cloudfront_log" {
  bucket = aws_s3_bucket.s3_cloudfront_log.id

  rule {
    id     = "cloudfront_log"
    status = "Enabled"

    expiration {
      days = 1095
    }

    transition {
      ## オブジェクトが作成されてからSTANDARD_IAに移行するまでの日数。
      days          = 30
      storage_class = "STANDARD_IA"
    }

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    noncurrent_version_expiration {
      noncurrent_days = 365
    }

    noncurrent_version_transition {
      noncurrent_days = 30
      storage_class   = "STANDARD_IA"
    }

    noncurrent_version_transition {
      noncurrent_days = 60
      storage_class   = "GLACIER"
    }

    abort_incomplete_multipart_upload {
      days_after_initiation = "7"
    }
  }
}

```
</details>
<details>
<summary>CloudFront</summary>

```terraform
# OACの設定を作成
resource "aws_cloudfront_origin_access_control" "hugo_oac" {
  name                              = "hugo_oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_cloudfront_distribution" "hugo_distribution" {
  origin {
    domain_name              = "hogehoge-hugo.s3.ap-northeast-1.amazonaws.com"
    origin_id                = "s3origin"
    origin_access_control_id = aws_cloudfront_origin_access_control.hugo_oac.id
  }

  enabled             = true
  is_ipv6_enabled     = false
  http_version        = "http2"
  price_class         = "PriceClass_200" # 価格クラスの設定。
  default_root_object = "index.html"
  wait_for_deployment = true # ディストリビューションのステータスが「InProgress」→「Deployed」に変わることを待つかどうかの設定。
  aliases             = ["hogehoge.com"]

  default_cache_behavior {
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    target_origin_id       = "s3origin"
    compress               = false
    viewer_protocol_policy = "redirect-to-https"
    cache_policy_id        = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # CachingDisabled
    #cache_policy_id  = "658327ea-f89d-4fab-a63d-7e88639e58f6" # CachingOptimized
  }

  # ログの設定
  logging_config {
    include_cookies = false
    bucket          = "hogehoge-hugo-cloudfront-log.s3.ap-northeast-1.amazonaws.com" #ログ用のs3バケットを指定
    prefix          = "cloudfront/"
  }

  # 日本からのみアクセス可能
  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["JP"] # アクセスを許可する国コードを追加
    }
  }

  # SSL証明書の設定
  viewer_certificate {
    cloudfront_default_certificate = false
    acm_certificate_arn            = "証明書arn"
    minimum_protocol_version       = "TLSv1.2_2019"
    ssl_support_method             = "sni-only"
  }
}
```
</details>

# ハマったところとか
- 一番初めのplan実行した際に、これとは関係のないterraform資材がdestroyとして出てしまった。原因はbackendの変更し忘れ。
- cloudfrontログ保存用のs3はACLが有効が必要。

https://dev.classmethod.jp/articles/cloudfront-access-log-dont-choose-no-acl-s3-bucket/

- 作成したcloudfrontに対してドメインでアクセスしても、エラーページが表示されず到達できない旨のエラーになる。cloudfrontとroute53のレコード設定がされていなかった。CloudFrontの`Distribution domain name`と自分が利用したいドメインをroute53でAレコードで登録することによって解決。

https://dev.classmethod.jp/articles/amazon-cloudfront-cname-and-host-header-test/



# 次にやること
- GitHub Actionsの設定
- HUGOでHPをいい感じに編集する
- テスト記事の投稿