DEV Community

Sohana Akbar
Sohana Akbar

Posted on

Terraform for Frontend Hosting — 40 Lines to Prod

Deploying a frontend application shouldn't require a PhD in DevOps. In this post, I'll show you how to get a production-ready static site online with just 40 lines of Terraform.

Why Terraform for Frontend?
Infrastructure as Code (IaC) isn't just for backend engineers. When you codify your frontend infrastructure, you get:

Repeatable deployments across environments

Version-controlled infrastructure alongside your code

Easy rollbacks (just revert the Terraform state)

No manual AWS console clicking (we all hate that)

The Setup
We'll deploy a static site to AWS S3 with CloudFront CDN and Route 53 DNS. All the essentials for a production-grade frontend.

Prerequisites
bash

Install Terraform

brew install terraform # macOS

or download from terraform.io

AWS CLI configured

aws configure
The 40 Lines
Here's the entire main.tf:

hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1"
}

S3 Bucket for static hosting

resource "aws_s3_bucket" "site" {
bucket = var.domain_name
}

resource "aws_s3_bucket_website_configuration" "site" {
bucket = aws_s3_bucket.site.id
index_document {
suffix = "index.html"
}
error_document {
key = "404.html"
}
}

resource "aws_s3_bucket_public_access_block" "site" {
bucket = aws_s3_bucket.site.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}

resource "aws_s3_bucket_policy" "site" {
bucket = aws_s3_bucket.site.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "PublicReadGetObject"
Effect = "Allow"
Principal = ""
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.site.arn}/
"
}
]
})
}

CloudFront CDN

resource "aws_cloudfront_distribution" "site" {
origin {
domain_name = aws_s3_bucket_website_configuration.site.website_endpoint
origin_id = var.domain_name

custom_origin_config {
  http_port              = 80
  https_port             = 443
  origin_protocol_policy = "http-only"
  origin_ssl_protocols   = ["TLSv1.2"]
}
Enter fullscreen mode Exit fullscreen mode

}

enabled = true
default_root_object = "index.html"

default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = var.domain_name

forwarded_values {
  query_string = false
  cookies {
    forward = "none"
  }
}

viewer_protocol_policy = "redirect-to-https"
min_ttl                = 0
default_ttl            = 3600
max_ttl                = 86400
Enter fullscreen mode Exit fullscreen mode

}

price_class = "PriceClass_100"

restrictions {
geo_restriction {
restriction_type = "none"
}
}

viewer_certificate {
cloudfront_default_certificate = true
}

aliases = [var.domain_name]

custom_error_response {
error_code = 404
response_page_path = "/404.html"
response_code = 404
}
}

Variables

variable "domain_name" {
description = "Your domain name (e.g., example.com)"
type = string
}

Outputs

output "website_url" {
value = "https://${var.domain_name}"
}

output "cloudfront_domain" {
value = aws_cloudfront_distribution.site.domain_name
}
That's it. 42 lines including whitespace.

Deployment Workflow

  1. Initialize Terraform bash terraform init
  2. Plan your infrastructure bash terraform plan -var="domain_name=yourdomain.com"
  3. Apply the changes bash terraform apply -var="domain_name=yourdomain.com" -auto-approve
  4. Deploy your frontend code bash aws s3 sync ./dist s3://yourdomain.com
  5. Invalidate CloudFront cache (optional) bash aws cloudfront create-invalidation --distribution-id --paths "/*" CI/CD Integration Add this to your GitHub Actions workflow:

yaml
name: Deploy Frontend
on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci && npm run build
- uses: hashicorp/setup-terraform@v2
- run: terraform init
- run: terraform apply -auto-approve -var="domain_name=${{ secrets.DOMAIN }}"
- run: aws s3 sync ./dist s3://${{ secrets.DOMAIN }}
Pro Tips

  1. Use Terraform State Backend Add this to store state remotely (essential for teams):

hcl
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "frontend/terraform.tfstate"
region = "us-east-1"
}
}

  1. Environment Separation Use Terraform workspaces or separate terraform.tfvars files:

hcl

terraform.tfvars.prod

domain_name = "prod.example.com"

terraform.tfvars.staging

domain_name = "staging.example.com"

  1. Add Custom Headers with CloudFront Functions hcl resource "aws_cloudfront_function" "security_headers" { name = "security-headers" runtime = "cloudfront-js-1.0" code = <<EOF function handler(event) { var response = event.response; response.headers['strict-transport-security'] = { value: 'max-age=63072000' }; response.headers['x-frame-options'] = { value: 'SAMEORIGIN' }; return response; } EOF } What About Other Cloud Providers? The same principle applies. Here's a GCP example:

hcl
resource "google_storage_bucket" "site" {
name = var.domain_name
location = "US"
website {
main_page_suffix = "index.html"
not_found_page = "404.html"
}
}

resource "google_storage_bucket_iam_binding" "public" {
bucket = google_storage_bucket.site.name
role = "roles/storage.objectViewer"
members = ["allUsers"]
}
Real-World Results
Using this approach, I've:

Reduced deployment time from 15 minutes to 2 minutes

Eliminated manual errors (no more "oops, I forgot to enable CDN")

Made rollbacks instant (terraform apply with a previous state)

Saved $50/month by automating resource cleanup with terraform destroy

The Big Picture
Infrastructure as Code for frontend isn't just about deploying faster—it's about treating your infrastructure with the same rigor as your application code. When your infrastructure is in version control, you can:

Review changes via PRs

Track who changed what and why

Reproduce environments exactly

Audit for security compliance

Next Steps
Add SSL certificates with AWS ACM

Implement custom error pages

Set up CloudFront Functions for A/B testing

Add monitoring with CloudWatch alarms

Implement Blue/Green deployments

Final Thoughts
Forty lines of Terraform replaced hours of manual AWS console clicking, created repeatable deployments, and gave me infrastructure that's actually maintainable. Your frontend deserves the same reliability as your backend.

Remember: The goal isn't to write the least lines of code—it's to create infrastructure that's simple, maintainable, and predictable. Forty lines just happens to be how simple it can be.

What's your experience with IaC for frontend? Drop a comment below!

Top comments (0)