🦔

[メモ] Amazon S3 + CloudFrontの2つのパターンとTerraformテンプレート

に公開

今回はAmazon S3 + CloudFrontのパターンの整理と、それぞれの構成をTerraformで作成するテンプレートを記録しておきます。

Amazon S3 + CloudFrontのパターン

現時点でのパターンは大きく2種類あるように見えています。なおレガシー扱いの別手段、ACM/Route53などとの組み合わせは考慮していません。

  • S3 の静的ウェブホスティングを有効化: S3/CloudFrontどちらからもアクセス可能
  • S3 の静的ウェブホスティングを無効化: CloudFrontからのみアクセス可能

それぞれの設定内容の違いは以下の通り。

種別 リソース 設定
静的ウェブホスティングを有効化 S3 ブロックパブリックアクセス オフ
静的ウェブサイトホスティング オン
bucket policy s3:GetObjectを全リソースに許可
CloudFront オリジンドメイン <bucket>.s3-website-<region>.amazonaws.com (Webサイトのエンドポイントを使用)
静的ウェブホスティングを無効化 S3 ブロックパブリックアクセス オン
静的ウェブサイトホスティング オフ
bucket policy s3:GetObjectを特定のCloudFrontにのみ許可
CloudFront オリジンドメイン <bucket>.s3.<region>.amazonaws.com
オリジンアクセス Origin access control settings

Terraformテンプレート

それぞれのテンプレートの例は以下の通り。

静的ウェブホスティングを有効化

terraform {
  required_version = ">= 1.0.0, < 2.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# S3
resource "aws_s3_bucket" "example" {
  bucket = "terraform-example-s3-cloudfront"
}

resource "aws_s3_bucket_public_access_block" "example" {
  bucket = aws_s3_bucket.example.id
}

resource "aws_s3_bucket_website_configuration" "example" {
  bucket = aws_s3_bucket.example.id

  index_document {
    suffix = "index.html"
  }
}

resource "aws_s3_object" "example" {
  bucket = aws_s3_bucket.example.id
  key    = "index.html"
  source = "./index.html"
  content_type = "text/html"
}

resource "aws_s3_bucket_policy" "example" {
  bucket = aws_s3_bucket.example.id
  policy = data.aws_iam_policy_document.example.json
}

data "aws_iam_policy_document" "example" {
  statement {
    sid = ""
    effect = "Allow"

    principals {
      identifiers = ["*"]
      type = "*"
    }

    actions = [
      "s3:GetObject"
    ]

    resources = [
      "${aws_s3_bucket.example.arn}/*",
    ]
  }
}

# CloudFront
data "aws_cloudfront_cache_policy" "example" {
  name = "Managed-CachingOptimized"
}

data "aws_region" "current" {}

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

  origin {
    origin_id                = aws_s3_bucket.example.id
    domain_name = "${aws_s3_bucket.example.bucket}.s3-website-${data.aws_region.current.name}.amazonaws.com"
    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "http-only"
      origin_ssl_protocols   = ["SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"]
      origin_read_timeout = 30
      origin_keepalive_timeout = 5
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }

  default_cache_behavior {
    target_origin_id       = aws_s3_bucket.example.id
    viewer_protocol_policy = "redirect-to-https"
    cached_methods         = ["GET", "HEAD"]
    allowed_methods        = ["GET", "HEAD"]
    cache_policy_id = data.aws_cloudfront_cache_policy.example.id
  }

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

静的ウェブホスティングを無効化

terraform {
  required_version = ">= 1.0.0, < 2.0.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# S3
resource "aws_s3_bucket" "example" {
  bucket = "terraform-sandbox-example-s3-cloudfront"
}

resource "aws_s3_bucket_public_access_block" "example" {
  bucket = aws_s3_bucket.example.id
  block_public_acls = true
  block_public_policy = true
  ignore_public_acls = true
  restrict_public_buckets = true
}

resource "aws_s3_object" "example" {
  bucket = aws_s3_bucket.example.id
  key    = "index.html"
  source = "./index.html"
  content_type = "text/html"
}

resource "aws_s3_bucket_policy" "example" {
  bucket = aws_s3_bucket.example.id
  policy = data.aws_iam_policy_document.example.json
}

data "aws_iam_policy_document" "example" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"]
    }
    actions   = [
      "s3:GetObject"
    ]
    resources = [
      "${aws_s3_bucket.example.arn}/*"
    ]
    condition {
      test = "StringEquals"
      variable = "aws:SourceArn"
      values = [
        aws_cloudfront_distribution.example.arn,
      ]
    }
  }
}

# CloudFront
data "aws_cloudfront_cache_policy" "example" {
  name = "Managed-CachingOptimized"
}

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

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

  viewer_certificate {
    cloudfront_default_certificate = true
  }

  default_cache_behavior {
    target_origin_id       = aws_s3_bucket.example.id
    viewer_protocol_policy = "redirect-to-https"
    cached_methods         = ["GET", "HEAD"]
    allowed_methods        = ["GET", "HEAD"]
    cache_policy_id = data.aws_cloudfront_cache_policy.example.id
  }

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

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

参考ドキュメント

Discussion