terraform { required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "gregh-terraform-state" key = "cdn-gregh-dev/terraform.tfstate" region = "us-east-1" encrypt = true use_lockfile = true } } provider "aws" { region = "us-east-1" profile = "production" } # S3 bucket for CDN content resource "aws_s3_bucket" "cdn" { bucket = "cdn.cloud.gregh.dev" tags = { Name = "cdn.cloud.gregh.dev" Environment = "production" } } resource "aws_s3_bucket_public_access_block" "cdn" { bucket = aws_s3_bucket.cdn.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_versioning" "cdn" { bucket = aws_s3_bucket.cdn.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_cors_configuration" "cdn" { bucket = aws_s3_bucket.cdn.id cors_rule { allowed_headers = ["*"] allowed_methods = ["GET", "HEAD"] allowed_origins = ["*"] expose_headers = ["ETag"] max_age_seconds = 3000 } } # CloudFront Origin Access Control resource "aws_cloudfront_origin_access_control" "cdn" { name = "cdn-gregh-dev-oac" description = "OAC for cdn.cloud.gregh.dev" origin_access_control_origin_type = "s3" signing_behavior = "always" signing_protocol = "sigv4" } # S3 bucket policy to allow CloudFront access resource "aws_s3_bucket_policy" "cdn" { bucket = aws_s3_bucket.cdn.id policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "AllowCloudFrontServicePrincipal" Effect = "Allow" Principal = { Service = "cloudfront.amazonaws.com" } Action = "s3:GetObject" Resource = "${aws_s3_bucket.cdn.arn}/*" Condition = { StringEquals = { "AWS:SourceArn" = aws_cloudfront_distribution.cdn.arn } } } ] }) } # ACM Certificate for CloudFront resource "aws_acm_certificate" "cdn" { domain_name = "cdn.cloud.gregh.dev" validation_method = "DNS" lifecycle { create_before_destroy = true } tags = { Name = "cdn.cloud.gregh.dev" } } # Data source for existing hosted zone data "aws_route53_zone" "main" { name = "gregh.dev." } # Route53 records for ACM validation resource "aws_route53_record" "cert_validation" { for_each = { for dvo in aws_acm_certificate.cdn.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } } allow_overwrite = true name = each.value.name records = [each.value.record] ttl = 60 type = each.value.type zone_id = data.aws_route53_zone.main.zone_id } # ACM certificate validation resource "aws_acm_certificate_validation" "cdn" { certificate_arn = aws_acm_certificate.cdn.arn validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn] } # CloudFront distribution resource "aws_cloudfront_distribution" "cdn" { enabled = true is_ipv6_enabled = true comment = "cdn.cloud.gregh.dev content" price_class = "PriceClass_100" aliases = ["cdn.cloud.gregh.dev"] origin { domain_name = aws_s3_bucket.cdn.bucket_regional_domain_name origin_id = "S3-cdn.cloud.gregh.dev" origin_access_control_id = aws_cloudfront_origin_access_control.cdn.id } default_cache_behavior { allowed_methods = ["GET", "HEAD", "OPTIONS"] cached_methods = ["GET", "HEAD"] target_origin_id = "S3-cdn.cloud.gregh.dev" viewer_protocol_policy = "redirect-to-https" compress = true forwarded_values { query_string = false cookies { forward = "none" } } min_ttl = 0 default_ttl = 86400 max_ttl = 31536000 } restrictions { geo_restriction { restriction_type = "none" } } viewer_certificate { acm_certificate_arn = aws_acm_certificate.cdn.arn ssl_support_method = "sni-only" minimum_protocol_version = "TLSv1.2_2021" } tags = { Name = "cdn.cloud.gregh.dev" Environment = "production" } } # Route53 A record for CDN subdomain resource "aws_route53_record" "cdn" { zone_id = data.aws_route53_zone.main.zone_id name = "cdn.cloud.gregh.dev" type = "A" alias { name = aws_cloudfront_distribution.cdn.domain_name zone_id = aws_cloudfront_distribution.cdn.hosted_zone_id evaluate_target_health = false } } # Outputs output "s3_bucket_name" { value = aws_s3_bucket.cdn.id } output "cloudfront_distribution_id" { value = aws_cloudfront_distribution.cdn.id } output "cdn_url" { value = "https://${aws_route53_record.cdn.name}" }