DEV Community

Uwais
Uwais

Posted on • Edited on

I Built a Serverless Resume on AWS with Terraform, Lambda, and GitHub Actions

# How I Built an Automated Serverless Resume on AWS (Cloud Resume Challenge)

The Cloud Resume Challenge is a practical exercise in moving beyond tutorials into production-style cloud engineering. It forces you to design, deploy, and integrate multiple AWS services into a single working system.

This project evolved from a manually deployed serverless setup into a fully automated Infrastructure-as-Code pipeline on AWS using Terraform and GitHub Actions.

---

## πŸ—οΈ Architecture Overview

The system is fully serverless, globally distributed, and provisioned entirely through Infrastructure-as-Code:

**S3 β†’ CloudFront β†’ API Gateway β†’ Lambda β†’ DynamoDB**

### Core Components

#### 1. Frontend (S3)
- Static resume site built with HTML and Tailwind CSS
- Hosted in a private Amazon S3 bucket
- No direct public access; served via CloudFront only

#### 2. CDN (CloudFront)
- Global content delivery via AWS edge locations
- HTTPS enforced by default
- Aggressive caching for performance optimization

#### 3. Database (DynamoDB)
- Serverless NoSQL table in pay-per-request mode
- Stores a persistent visitor counter (`ID: visitors`)
- Low-latency reads/writes for API interactions

#### 4. Backend (Lambda)
- Python-based AWS Lambda function using `boto3`
- Performs atomic read/increment operations on DynamoDB
- Stateless compute layer triggered via API Gateway

#### 5. API Layer (API Gateway)
- HTTP API with `/counter` POST route
- Proxy integration directly to Lambda
- CORS enabled for browser-based frontend access

#### 6. Infrastructure as Code (Terraform)
- Entire AWS stack defined declaratively in `main.tf`
- Uses `terraform state` to manage resource mapping
- Handles IAM roles, API routes, Lambda packaging, and S3 deployment

#### 7. CI/CD (GitHub Actions)
- Automated deployment pipeline for frontend assets
- Push-to-deploy workflow into S3
- CloudFront cache invalidation after every release

---

## 🧠 Key Engineering Challenges & Fixes

### 1. Git Push Failure: 100MB File Limit (Terraform Provider Bloat)

While staging the repository, Git accidentally tracked `.terraform/` directories. This introduced a large AWS provider binary (~600MB+), exceeding GitHub’s 100MB file limit.

#### Root Cause
Terraform initializes large provider binaries locally, which can be mistakenly committed if not ignored early.

#### Fix
Even after updating `.gitignore`, the file history still contained the large binaries. A full index reset was required:

```bash
git rm -r --cached .
git reset --mixed <clean-commit-hash>
git add .
git commit -m "chore: clean terraform state and restructure repo"
git push origin main
Enter fullscreen mode Exit fullscreen mode

This removed the cached large files from Git tracking and rebuilt a clean commit history.


2. Migrating to Infrastructure-as-Code (Terraform Import)

A major challenge was converting manually created AWS resources into Terraform-managed infrastructure without duplication errors.

Approach

  • Defined equivalent resources in main.tf
  • Used terraform import to map existing AWS resources into Terraform state
  • Gradually aligned live infrastructure with declarative definitions

This transitioned the system from β€œclick-ops” to fully reproducible infrastructure.


3. API Gateway + Lambda Response Handling

Initial frontend integration produced errors like:

  • Views: undefined
  • SyntaxError: "undefined" is not valid JSON

Root Cause

HTTP API proxy integration already returns parsed JSON. The frontend incorrectly attempted redundant parsing of response.body.

Fix

const response = await fetch(apiURL, { method: "POST" });
const data = await response.json();

document.getElementById("counter").innerText = data.count;
Enter fullscreen mode Exit fullscreen mode

Removing unnecessary parsing resolved the issue and stabilized the UI.


πŸ’‘ Key Takeaways

Automation-first architecture

Shifting from manual deployments to Git-driven Infrastructure-as-Code enables full environment reproducibility in seconds.

IAM least privilege design

Lambda execution role was explicitly scoped to only the required DynamoDB table, minimizing blast radius.

Real-world DevOps friction

The project surfaced production-grade issues:

  • Git history cleanup for large binaries
  • CORS and API integration debugging
  • CDN cache invalidation strategies
  • Terraform state management and imports

πŸ”— Live Project Links


πŸ“Œ Closing

This project demonstrates how a set of core AWS services can be composed into a production-grade serverless system.

The main outcome was not the resume itself, but the operational model behind it: fully automated, reproducible cloud infrastructure built with Infrastructure-as-Code, CI/CD, and stateless compute design.





Enter fullscreen mode Exit fullscreen mode

Top comments (0)