☁️

TerraformでCloudFront + S3の構成を作る

2023/06/19に公開

はじめに

静的サイトジェネレータhugoをcloudfront + s3の構成で利用したく、
その準備方法を調べたのでメモします。(hugoについてはこの記事で触れません)
Terraformを使用して環境を用意します。

環境

terraform 1.4.6

方針

  • s3周辺リソースを作成する
  • cloudfrontディストリビューションを作成する

の流れでtfファイルをつくっていきます。

使用するコード

長いため折りたたみます。

main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.65.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
  default_tags {
    tags = {
      env       = "test"
      provision = "terraform"
    }
  }
}

locals {
  bucket_name = "xxxxxxxxxxxxxxxxxxxxx"
}

resource "aws_s3_bucket" "hugo_bucket" {
  bucket = local.bucket_name
}

resource "aws_s3_object" "index" {
  bucket = aws_s3_bucket.hugo_bucket.id
  key    = "index.html"
  source = "./index.html"
}

resource "aws_s3_bucket_policy" "policy" {
  depends_on = [
    aws_s3_bucket.hugo_bucket,
  ]
  bucket = aws_s3_bucket.hugo_bucket.id
  policy = data.aws_iam_policy_document.policy_document.json
}

data "aws_iam_policy_document" "policy_document" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }

    actions = [
      "s3:GetObject",
    ]

    resources = [
      aws_s3_bucket.hugo_bucket.arn,
      "${aws_s3_bucket.hugo_bucket.arn}/*",
    ]
    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudfront_distribution.hugo_cfront.arn]
    }
  }
}

resource "aws_cloudfront_distribution" "hugo_cfront" {
  enabled = true
  default_root_object = "index.html"

  origin {
    origin_id                = aws_s3_bucket.hugo_bucket.id
    domain_name              = aws_s3_bucket.hugo_bucket.bucket_regional_domain_name
    origin_access_control_id = aws_cloudfront_origin_access_control.hugo_oac.id
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }

  default_cache_behavior {
    target_origin_id       = aws_s3_bucket.hugo_bucket.id
    viewer_protocol_policy = "redirect-to-https"
    cached_methods         = ["GET", "HEAD"]
    allowed_methods        = ["GET", "HEAD"]
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
}

resource "aws_cloudfront_origin_access_control" "hugo_oac" {
  name                              = aws_s3_bucket.hugo_bucket.bucket_domain_name
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

output "cfront_domain_name" {
  value = aws_cloudfront_distribution.hugo_cfront.domain_name
}

ポイント

s3の設定

s3の設定はここを意識すればOKです

  • ウェブホスティングを有効化しない
  • Block public accessをすべて有効化
  • ACLはprivate

この後OACというs3へのアクセス制御を設定しますが、
そのおかげでs3をプライベートな環境に置くことが可能です。

cloudfrontディストリビューション

設定項目が多いですが、以下を参考に設定します。
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution
https://dev.classmethod.jp/articles/static-web-with-cf-s3-tf/

非常にわかりやすい記事です、参考にさせていただきました。

今回作成するs3バケットをオリジンにするのを忘れずに行いましょう。

オリジンアクセスコントロール(OAC)を設定する

ざっくりいうとs3に安全にアクセス出来る仕組みだと思います。
昨年新しくリリースされました。
それ以前もOAIという仕組みがあったのですが、基本的にOACを使うのが推奨されているようです。
https://aws.amazon.com/jp/blogs/news/amazon-cloudfront-introduces-origin-access-control-oac/

こちらもTerraformで設定します。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control

OACがs3バケットにアクセス出来るようにする

ということでs3のアクセスポリシーを設定します。

resource "aws_s3_bucket_policy" "policy" {
  depends_on = [
    aws_s3_bucket.hugo_bucket,
  ]
  bucket = aws_s3_bucket.hugo_bucket.id
  policy = data.aws_iam_policy_document.policy_document.json
}

data "aws_iam_policy_document" "policy_document" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }

    actions = [
      "s3:GetObject",
    ]

    resources = [
      aws_s3_bucket.hugo_bucket.arn,
      "${aws_s3_bucket.hugo_bucket.arn}/*",
    ]
    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudfront_distribution.hugo_cfront.arn]
    }
  }
}

要はソースがcloudfrontかどうかを見ているようです。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html

所感

  • cloudfrontの設定項目が多いため、AWS/Terraformのドキュメントをよく参照すること
    • はい、わたしも足りないので熟読します。
  • セキュリティのため、OACを設定すること

参考

https://zenn.dev/not75743/scraps/556a1c855be232

Discussion