Introduction
In today’s fast-paced DevOps landscape, having a centralized hub for essential tools and resources can be invaluable. Whether you’re onboarding new team members, sharing best practices, or simply maintaining a quick reference guide for cloud and DevOps tools, a well-organized static website can be the perfect solution.
In this post, I’ll walk you through how to create and deploy a beautiful, responsive static website that showcases DevOps tools and cloud platforms using Terraform, AWS S3, and CloudFront. This approach is cost-effective, scalable, and follows infrastructure-as-code best practices.
Why Static Hosting with Terraform?
Before we dive into the technical details, let’s understand why this approach makes sense:
Cost-Effective: Static hosting on S3 is incredibly cheap. You’re looking at pennies per month for most use cases.
Zero Maintenance: No servers to manage, no databases to maintain, no security patches to apply.
Scalability: S3 and CloudFront automatically handle traffic spikes without any intervention.
Infrastructure as Code: Using Terraform makes your infrastructure reproducible, versionable, and auditable.
CDN Performance: CloudFront ensures your content is delivered quickly to users worldwide.
Security: With Origin Access Identity (OAI), your S3 bucket is never exposed to the internet directly.
Architecture Overview
Here’s what we’re building:
┌─────────────────┐
│ CloudFront │ (Global CDN, HTTPS)
│ Distribution │
└────────┬────────┘
│
┌────────▼────────┐
│ S3 Bucket │ (Stores index.html)
│ eu-central-1 │
└─────────────────┘
Key Components:
- S3 Bucket — Stores the static HTML files in the eu-central-1 region
- CloudFront Distribution — Global CDN that caches and distributes your content
- Origin Access Identity (OAI) — Securely grants CloudFront access to S3
- IAM Policy — Controls permissions between CloudFront and S3
Step 1: Create the Terraform Configuration
First, let’s create our infrastructure as code. Create a file called main.tf:
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "eu-central-1"
}# Get current AWS account ID
data "aws_caller_identity" "current" {}# S3 bucket for hosting
resource "aws_s3_bucket" "website" {
bucket = "devops-links-website-${data.aws_caller_identity.current.account_id}" tags = {
Name = "DevOps Links Website"
Environment = "production"
}
}# Block all public access
resource "aws_s3_bucket_public_access_block" "website" {
bucket = aws_s3_bucket.website.id block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}# Enable versioning
resource "aws_s3_bucket_versioning" "website" {
bucket = aws_s3_bucket.website.id versioning_configuration {
status = "Enabled"
}
}# CloudFront OAI
resource "aws_cloudfront_origin_access_identity" "website" {
comment = "OAI for DevOps Links Website"
}# S3 Bucket Policy
resource "aws_s3_bucket_policy" "website" {
bucket = aws_s3_bucket.website.id policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "CloudFrontGetObject"
Effect = "Allow"
Principal = {
AWS = aws_cloudfront_origin_access_identity.website.iam_arn
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.website.arn}/*"
}
]
}) depends_on = [aws_s3_bucket_public_access_block.website]
}# Upload HTML file
resource "aws_s3_object" "index" {
bucket = aws_s3_bucket.website.id
key = "index.html"
source = "${path.module}/index.html"
content_type = "text/html"
etag = filemd5("${path.module}/index.html") depends_on = [aws_s3_bucket_policy.website]
}# CloudFront Distribution
resource "aws_cloudfront_distribution" "website" {
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html" origin {
domain_name = aws_s3_bucket.website.bucket_regional_domain_name
origin_id = "S3Origin" s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.website.cloudfront_access_identity_path
}
} default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3Origin" forwarded_values {
query_string = false
cookies {
forward = "none"
}
} viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
compress = true
} restrictions {
geo_restriction {
restriction_type = "none"
}
} viewer_certificate {
cloudfront_default_certificate = true
} depends_on = [aws_s3_object.index]
}# Outputs
output "website_url" {
description = "Website URL"
value = "https://${aws_cloudfront_distribution.website.domain_name}"
}output "s3_bucket_name" {
description = "S3 bucket name"
value = aws_s3_bucket.website.id
}
Step 2: Create the HTML File
Create an index.html file with your DevOps resources. Here’s a sample structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DevOps & Cloud Resources</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%);
min-height: 100vh;
padding: 40px 20px;
}
header {
text-align: center;
color: #000;
margin-bottom: 50px;
background: rgba(255, 255, 255, 0.95);
padding: 40px;
border-radius: 15px;
}
.categories {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 30px;
max-width: 1200px;
margin: 0 auto;
}
.category {
background: white;
border-radius: 15px;
padding: 30px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<header>
<h1>DevOps & Cloud Resources</h1>
<p>Essential tools for cloud infrastructure</p>
</header>
<div class="categories">
<!-- Add your categories here -->
</div>
</body>
</html>
Step 3: Deploy with Terraform
Now for the exciting part! Deploy your infrastructure:
# Initialize Terraform
terraform init
# Preview the changes
terraform plan
# Apply the configuration
terraform apply
Terraform will create all the necessary AWS resources. Once it completes, you’ll see the output:
website_url = "https://d2c0tr4dt8mwgk.cloudfront.net"
s3_bucket_name = "devops-links-website-123456789"
Visit that URL, and your website is live! 🚀
Key Features of This Setup
1. Security First
- S3 bucket is completely blocked from public access
- Only CloudFront can access it via Origin Access Identity
- HTTPS by default through CloudFront
2. Performance
- CloudFront caches your content globally
- Default TTL of 3600 seconds (1 hour)
- Automatic compression of responses
3. Cost Optimization
- Typically costs less than $1/month
- S3 storage: ~$0.023 per GB
- CloudFront egress: ~$0.085 per GB (first 10TB)
4. Versioning
- S3 versioning enabled for disaster recovery
- Easy rollback if needed
5. Infrastructure as Code
- Entire setup in version control
- Reproducible across environments
- Easy to maintain and update
Repo URL : https://github.com/muralikrishna-sunkara/s3-static-website
Troubleshooting Common Issues
AccessDenied Errors
If you encounter AccessDenied when accessing CloudFront:
- Ensure
default_root_objectis set in CloudFront distribution - Verify the S3 bucket policy grants the OAI proper permissions
- Invalidate CloudFront cache:
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths "/*"
- Wait 5–10 minutes for the changes to propagate
File Upload Issues
Make sure your index.html file is in the same directory as your Terraform files, and the path in aws_s3_object is correct.
Next Steps: Enhancements
Add Custom Domain: Attach a custom domain to CloudFront Add SSL Certificate: Use AWS Certificate Manager for HTTPS Add Error Pages: Configure 404 error handling Monitoring: Set up CloudFront metrics in CloudWatch
Conclusion
We’ve successfully created a secure, scalable, and cost-effective static website hosting solution using Terraform and AWS. This approach is perfect for:
- DevOps team resource hubs
- Documentation sites
- Personal portfolios
- Project showcases
- Internal knowledge bases
The beauty of Infrastructure as Code is that you can now version control your entire infrastructure, make changes with confidence, and replicate this setup across different environments or organizations.
Want to extend this? Try adding:
- A custom domain name
- AWS Certificate Manager for custom SSL certificates
- Multiple HTML pages with routing
- GitHub Actions for automated deployments
Resources
- Terraform AWS Provider Documentation
- AWS S3 Static Website Hosting
- CloudFront Documentation
- Terraform Best Practices
Happy Infrastructure Coding! 🚀
If you found this helpful, please share it with your team. Questions? Drop them in the comments below!