以下の内容はhttps://kakakakakku.hatenablog.com/entry/2026/01/19/163654より取得しました。


Amazon CloudFront VPC origins を Terraform でデプロイする

Amazon CloudFront VPC origins を使うとプライベートサブネットにデプロイしたリソース(具体的には Application Load Balancer / Network Load Balancer / Amazon EC2)を Amazon CloudFront 経由で直接配信できる👌

パブリックサブネットにリソースをデプロイする必要がなくセキュリティ面でメリットがある.またパブリック IP を保有する必要がなくコスト面でも多少のメリットがある.他にもドキュメントやブログ記事に Amazon CloudFront VPC origins を使うメリットが紹介されていて,合わせて読むと良さそう📝

docs.aws.amazon.com

aws.amazon.com

最近 Terraform で Amazon CloudFront VPC origins x Application Load Balancer (ALB) の組み合わせを実装する機会があったのでまとめておこうと思う😀

アーキテクチャ図

個人的な検証環境のアーキテクチャ図はザックリこんな感じ❗️

  • Amazon CloudFront + AWS Certificate Manager(カスタムドメイン)
  • Amazon VPC のプライベートサブネットに Internal ALB(固定レスポンス)

Terraform(ALB 関連)

基本的にすべてのリソースを Terraform で構築しつつ,Amazon VPC は既にある個人環境を流用している✋️

まずは ALB 関連で,Internal ALB をプライベートサブネットにデプロイする.本来は Amazon ECS タスクなどを紐付けるけど,今回はサンプルとして固定レスポンスを返すようにした.

ここで1つポイントがある.Internal ALB で Amazon CloudFront からのトラフィックを受け付けるために「Amazon CloudFront マネージドプレフィックスリスト」を設定する方法と Amazon CloudFront VPC origins をデプロイすると自動的に作られる「CloudFront-VPCOrigins-Service-SG セキュリティグループ」を設定する方法がある.

docs.aws.amazon.com

今回は CloudFront-VPCOrigins-Service-SG セキュリティグループを使うことにした.Terraform 側で依存関係を明示するために aws_security_group データソース側に depends_on を設定してキレイに terraform apply が実行できるようになった👌

data "aws_security_group" "sandbox_vpc_origin" {
  filter {
    name   = "group-name"
    values = ["CloudFront-VPCOrigins-Service-SG"]
  }

  depends_on = [aws_cloudfront_vpc_origin.sandbox]
}

resource "aws_security_group" "sandbox_vpc_origin_alb" {
  name   = "sandbox-vpc-origin-alb"
  vpc_id = "vpc-xxxxxxxxxxxxxxxxx"
}

resource "aws_security_group_rule" "sandbox_vpc_origin_ingress_http" {
  security_group_id        = aws_security_group.sandbox_vpc_origin_alb.id
  type                     = "ingress"
  from_port                = "80"
  to_port                  = "80"
  protocol                 = "tcp"
  source_security_group_id = data.aws_security_group.sandbox_vpc_origin.id
}

resource "aws_security_group_rule" "sandbox_vpc_origin_egress" {
  security_group_id = aws_security_group.sandbox_vpc_origin_alb.id
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}

resource "aws_lb" "sandbox_vpc_origin" {
  name               = "sandbox-vpc-origin"
  internal           = true
  load_balancer_type = "application"

  security_groups = [
    aws_security_group.sandbox_vpc_origin_alb.id
  ]

  subnets = [
    "subnet-xxxxxxxxxxxxxxxxx",
    "subnet-xxxxxxxxxxxxxxxxx",
  ]

  drop_invalid_header_fields = true
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.sandbox_vpc_origin.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type = "fixed-response"

    fixed_response {
      content_type = "text/plain"
      message_body = "Hello, Amazon CloudFront VPC origins!"
      status_code  = "200"
    }
  }
}

Terraform(Amazon CloudFront 関連)

次は Amazon CloudFront をデプロイする.オリジンとしては Amazon CloudFront VPC origins と Amazon S3 バケットを設定してある.Amazon CloudFront VPC origins は aws_cloudfront_vpc_origin リソースを使って Internal ALB を紐付けておいて,あとは aws_cloudfront_distributionvpc_origin_config を設定すればヨシ❗️

resource "aws_cloudfront_distribution" "sandbox_vpc_origin" {
  enabled = true
  aliases = ["sandbox-vpc-origin.xxxxx.com"]

  origin {
    domain_name = aws_lb.sandbox_vpc_origin.dns_name
    origin_id   = aws_lb.sandbox_vpc_origin.id

    vpc_origin_config {
      vpc_origin_id = aws_cloudfront_vpc_origin.sandbox.id
    }
  }

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

  ordered_cache_behavior {
    path_pattern           = "/assets/*"
    target_origin_id       = aws_s3_bucket.assets.id
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }
  }

  default_cache_behavior {
    target_origin_id       = aws_lb.sandbox_vpc_origin.id
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    acm_certificate_arn      = aws_acm_certificate.sandbox_vpc_origin.arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }
}

resource "aws_cloudfront_vpc_origin" "sandbox" {
  vpc_origin_endpoint_config {
    name                   = "sandbox"
    arn                    = aws_lb.sandbox_vpc_origin.arn
    http_port              = 80
    https_port             = 443
    origin_protocol_policy = "http-only"

    origin_ssl_protocols {
      items    = ["TLSv1.2"]
      quantity = 1
    }
  }
}

resource "aws_s3_bucket" "assets" {
  bucket = "xxxxx"
}

resource "aws_s3_bucket_public_access_block" "assets" {
  bucket = aws_s3_bucket.assets.id

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

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

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

    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.assets.arn}/*"]

    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudfront_distribution.sandbox_vpc_origin.arn]
    }
  }
}

resource "aws_cloudfront_origin_access_control" "assets" {
  name                              = "sandbox-vpc-origin-oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

Terraform(Amazon Route 53 関連)

最後は Amazon Route 53 関連で Amazon CloudFront ディストリビューションにカスタムドメインを設定すれば OK👌

data "aws_route53_zone" "main" {
  name = "xxxxx.com"
}

resource "aws_acm_certificate" "sandbox_vpc_origin" {
  region = "us-east-1"

  domain_name       = "sandbox-vpc-origin.xxxxx.com"
  validation_method = "DNS"

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route53_record" "validation" {
  for_each = {
    for dvo in aws_acm_certificate.sandbox_vpc_origin.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

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

resource "aws_route53_record" "sandbox_vpc_origin" {
  zone_id = data.aws_route53_zone.main.zone_id
  name    = "sandbox-vpc-origin.xxxxx.com"
  type    = "A"

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

aws_acm_certificate はリソースレベルでリージョン us-east-1 を指定している👌

kakakakakku.hatenablog.com

デプロイ確認

Amazon CloudFront VPC origins

CloudFront-VPCOrigins-Service-SG セキュリティグループ

ENI

Internal ALB

動作確認

イイ感じ \( 'ω')/

その他

個人的な検証環境を使って勉強会を開催してみたりもした😀




以上の内容はhttps://kakakakakku.hatenablog.com/entry/2026/01/19/163654より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14