Amazon CloudFront VPCオリジンとは
Amazon CloudFront VPCオリジンは、今年の11月にリリースされたCloudFrontの新機能です。
これまではALB、NLB、EC2等をオリジンにする場合はパブリックサブネットに配置する必要がありましたが、この機能によりプライベートサブネット内に配置できるようになりました。
TerraformのAmazon CloudFront VPCオリジン対応
terraform-provider-awsのv5.82.0で aws_cloudfront_vpc_origin が追加され、TerraformでAmazon CloudFront VPCオリジンの設定が可能になりました。
Terraformでリソース作成
テスト用のALBを作成
今回は簡単にテストするために固定レスポンスを返すALBをプライベートサブネット内に作成します。
以下、いくつかポイントについて説明します。
- ALBのセキュリティグループにCloudFrontのマネージドプレフィックスリストを利用したルールを追加してCloudFrontからのアクセスを許可しておく
Update your security groups for the VPC private origins to explicitly allow the CloudFront managed prefix list. For more information, see Use the CloudFront managed prefix list. 
- インターネットからのトラフィックを受信するためにインターネットゲートウェイを作成する
Internet gateway – Required so that your VPC can receive traffic from the internet. The internet gateway is not used for routing traffic to origins inside the subnet, and you don’t need to update the routing policies. 
その他の要件についてはドキュメントを参照してください。
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-vpc-origins.html
locals {
  availability_zones = [
    "ap-northeast-1a",
    "ap-northeast-1c",
    "ap-northeast-1d",
  ]
}
data "aws_ec2_managed_prefix_list" "cloudfront" {
  name = "com.amazonaws.global.cloudfront.origin-facing"
}
resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
# インターネットからのトラフィックを受信するために必要
# 存在しないとVPCオリジン作成時にエラーになります。
resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id
}
resource "aws_subnet" "this" {
  for_each = toset(local.availability_zones)
  vpc_id            = aws_vpc.this.id
  availability_zone = each.key
  cidr_block        = cidrsubnet(aws_vpc.this.cidr_block, 8, index(local.availability_zones, each.key))
}
resource "aws_security_group" "private_alb" {
  vpc_id = aws_vpc.this.id
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    # CloudFrontからのアクセスを許可
    prefix_list_ids = [data.aws_ec2_managed_prefix_list.cloudfront.id]
  }
}
resource "aws_lb" "private_alb" {
  name               = "private-alb"
  internal           = true
  load_balancer_type = "application"
  security_groups    = [aws_security_group.private_alb.id]
  subnets            = [for subnet in aws_subnet.this : subnet.id]
}
resource "aws_lb_listener" "private_alb" {
  load_balancer_arn = aws_lb.private_alb.arn
  port              = 80
  protocol          = "HTTP"
  default_action {
    type = "fixed-response"
    fixed_response {
      content_type = "text/plain"
      message_body = "Hello"
      status_code  = "200"
    }
  }
}
VPCオリジンとディストリビューションを作成
ALBを指定してVPCオリジンを作成し、ディストリビューションにVPCオリジンをオリジンとして設定します。
resource "aws_cloudfront_vpc_origin" "private_alb" {
  vpc_origin_endpoint_config {
    name                   = "private-alb"
    arn                    = aws_lb.private_alb.arn
    http_port              = 80
    https_port             = 443
    origin_protocol_policy = "http-only"
    origin_ssl_protocols {
      quantity = 1
      items    = ["TLSv1.2"]
    }
  }
}
resource "aws_cloudfront_distribution" "private_alb" {
  origin {
    origin_id   = "private_alb"
    domain_name = aws_lb.private_alb.dns_name
    vpc_origin_config {
      vpc_origin_id = aws_cloudfront_vpc_origin.private_alb.id
    }
  }
  enabled = true
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "private_alb"
    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
    viewer_protocol_policy = "redirect-to-https"
  }
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
  viewer_certificate {
    cloudfront_default_certificate = true
  }
}
terraform apply してリソースを作成し、作成されたディストリビューションにアクセスすると、ALBにレスポンスとして設定した Hello という文字列が返ってくることが確認できます。
まとめ
Terraformで比較的簡単にCloudFront VPCオリジンの設定ができることを確認できました。
今後、CloudFrontを使用する際に活用していきたいと思います!
