TerraformでALBをオリジンとするCloudFront構築

## はじめに
Terraformを使用して、ALBをオリジンにCloudFrontで配信するWebサーバを構築してみました。
前回ALBまで作成したので今回はALBの変更、CloudFrontディストリビューション、Route53ホストゾーンの作成となります。

###### ※今回の構成では、CloudFrontのIPは不確定でALBのSGではCloudFrontに限定したインバウンドアクセス制御ができないため、ALBへのアクセス制限はない状態です。
###### ※ALBに直接アクセスさせないためにはHostヘッダーの制限をCloudFrontのWhitelistとALBリスナールールで行う必要があります。



## 構成
![ネットワーク構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/4e288da3-99fd-4afb-a3f4-c2227cb085f7.png)






## ALBリスナーとSGの修正
- HTTPリスナーのデフォルトアクションの変更（forward→redirect）
- HTTPSリスナーの追加
※リスナーで競合エラーが出たら、HTTPリスナーを一度削除してからもう再度追加したらいけます
- SGインバウンドルール追加（HTTPS）


```
resource "aws_lb" "alb" {
  name               = "MyLoadBalancer"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb_sg.id]
  subnets            = [
    aws_subnet.public1.id,
    aws_subnet.public2.id
  ]
  enable_deletion_protection = false
} 

# HTTPSリスナーでターゲットグループに転送
resource "aws_lb_listener" "https_listner" {
  load_balancer_arn = aws_lb.alb.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn = "arn:aws:acm:ap-northeast-1:xxxxxxxxxxxx:certificate/b7f2c39b-71e8-4bb3-8595-8a0cfe3738da" # ALBリージョンの証明書

  default_action {
    type = "forward"
    target_group_arn = aws_lb_target_group.lb_tg.arn
  }
}

# HTTPリスナーでHTTPSにリダイレクト
resource "aws_lb_listener" "http_listner" {
  load_balancer_arn = aws_lb.alb.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "redirect"
    redirect {
      protocol = "HTTPS"
      port     = "443"
      status_code = "HTTP_301"
    }
  }
}

resource "aws_lb_target_group" "lb_tg" {
  name = "test-lb-tg"
  port = 80
  protocol = "HTTP"
  vpc_id = aws_vpc.main.id

  health_check {
    path = "/health"
  }
}

resource "aws_lb_target_group_attachment" "instance1" {
  target_group_arn = aws_lb_target_group.lb_tg.arn
  target_id = aws_instance.instance1.id
  port = 80
}

resource "aws_lb_target_group_attachment" "instance2" {
  target_group_arn = aws_lb_target_group.lb_tg.arn
  target_id = aws_instance.instance2.id
  port = 80
}

resource "aws_security_group" "alb_sg" {
  name = "alb-sg"
  description = "Allow HTTP and SSH"
  vpc_id = aws_vpc.main.id
  
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] 
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
```
## CloudFrontディストリビューション
- オリジン：ALB
- プロトコルポリシー：HTTPS ONLY
- キャッシュ：テスト目的のため無効化
- プロトコルポリシー：Redirect HTTP to HTTPS
- 代替ドメイン：example.com
- 証明書：us-east-1で発行した証明書
- 地理的制限：なし
```
resource "aws_cloudfront_distribution" "test_cf" {
  origin {
    domain_name = aws_lb.alb.dns_name
    origin_id   = aws_lb.alb.dns_name
    custom_origin_config {
      origin_protocol_policy    = "https-only"
      https_port                = 443
      http_port                 = 80 
      origin_ssl_protocols      = ["TLSv1.2"]
    }
  }

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    cache_policy_id  = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" # Caching Disabled
    origin_request_policy_id = "216adef6-5c7f-47e4-b989-5492eafa07d3" # All Viewer
    target_origin_id = aws_lb.alb.dns_name
    viewer_protocol_policy = "redirect-to-https"
  }

  enabled = true # ディストリビューションがリクエストを受け付ける

  price_class = "PriceClass_200" # 北米、ヨーロッパ、アジアのエッジロケーションのみ使用

  aliases = ["example.com"]
  viewer_certificate {
    acm_certificate_arn = "arn:aws:acm:us-east-1:xxxxxxxxxxxx:certificate/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # us-east-1の証明書
    ssl_support_method = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

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

## Route53
- シンプルルーティング
- ホストゾーンを作成してAレコードを追加
- エイリアス：CloudFrontのDNS名
```
resource "aws_route53_zone" "test_zone" {
  name = "example.com"
}

resource "aws_route53_record" "test_record" {
  zone_id = aws_route53_zone.test_zone.id
  name    = "example.com"
  type    = "A"
  alias {
    name                   = aws_cloudfront_distribution.test_cf.domain_name
    zone_id                = aws_cloudfront_distribution.test_cf.hosted_zone_id
    evaluate_target_health = false
  }
}
```

## 確認
#### ALB
![202503023_000001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/b243727b-17dc-4a8d-92ce-d91b51141c17.jpeg)

#### リクエスト処理
```
$ curl -I  https://example.com
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 615
Connection: keep-alive
Date: Sun, 23 Mar 2025 07:49:39 GMT
Accept-Ranges: bytes
Server: nginx/1.22.1 
Last-Modified: Tue, 27 Aug 2024 13:55:08 GMT
ETag: "66cddabc-267"
X-Cache: Miss from cloudfront # リクエストがオリジンに送信されている
Via: 1.1 ccec8f7c8547a994f6578dd30ad7acce.cloudfront.net (CloudFront) # CloudFrontを経由して処理
X-Amz-Cf-Pop: NRT57-P6
X-Amz-Cf-Id: jBOQzTgmJg-WR5HPrRle_fXzuhwa0rRsrU0og1Su6zsB3D8cIY23Bw==
```

![202503023_000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812406/6d12c175-8877-4148-85db-561551ddd36d.jpeg)

##