Minimal Terraform demo: host a Single Page App (SPA) in S3, serve via CloudFront, keep S3 private.
- S3 bucket for app assets
- S3 public access fully blocked
- S3 default encryption (SSE-S3)
- S3 versioning enabled
- CloudFront distribution in front of S3
- CloudFront Origin Access Control (OAC)
- Bucket policy allowing
s3:GetObjectonly from this CloudFront distribution - Bucket policy denying non-TLS S3 requests
- SPA fallback (
403->/index.html) - CloudFront managed cache policy + managed security headers policy
- App files are not public in S3.
- Users hit CloudFront only.
- HTTP requests are redirected to HTTPS at CloudFront.
- Deep links and page refreshes work for SPAs because unknown paths return
index.html.
Browser -> CloudFront (HTTPS, cache, security headers) -> S3 (private via OAC)
- Terraform
~> 1 - AWS provider
~> 6(handled byterraform init) - AWS credentials configured (
aws configure, SSO, env vars, etc.) - IAM permissions for S3, CloudFront, IAM policy documents
bucket_name(required)region(optional, defaultus-east-1)
- Initialize:
terraform init- Apply:
terraform apply -var="bucket_name=zxcvmbn123u8oakdfa"- Upload your SPA files:
aws s3 cp . s3://zxcvmbn123u8oakdfa --recursive- Get URL:
terraform output cloudfront_domain_nameOpen https://<cloudfront_domain_name>.
Upload new files again:
aws s3 cp . s3://zxcvmbn123u8oakdfa --recursiveOptionally invalidate cache:
aws cloudfront create-invalidation --distribution-id <DIST_ID> --paths "/*"terraform destroy -var="bucket_name=zxcvmbn123u8oakdfa"- No custom domain/ACM in this demo; it uses the default CloudFront domain and certificate.
- If you need custom domain aliases or stricter TLS policy control, add ACM in
us-east-1and Route53 records.
Invalidate cache:
aws cloudfront create-invalidation --distribution-id <DIST_ID> --paths "/*"