
Amazon CloudFront VPC origins を使うとプライベートサブネットにデプロイしたリソース(具体的には Application Load Balancer / Network Load Balancer / Amazon EC2)を Amazon CloudFront 経由で直接配信できる👌
パブリックサブネットにリソースをデプロイする必要がなくセキュリティ面でメリットがある.またパブリック IP を保有する必要がなくコスト面でも多少のメリットがある.他にもドキュメントやブログ記事に Amazon CloudFront VPC origins を使うメリットが紹介されていて,合わせて読むと良さそう📝
最近 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 セキュリティグループ」を設定する方法がある.
今回は 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_distribution の vpc_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 を指定している👌
デプロイ確認




動作確認
イイ感じ \( 'ω')/

その他
個人的な検証環境を使って勉強会を開催してみたりもした😀
お手伝いしてるチームで勉強会したやつ(デモしながら喋るだけだからスライドは表紙のみw) pic.twitter.com/0l4OpSotMH
— カック (@kakakakakku) 2026年1月16日