Platform Engineering · Cloud & DevOps
Production AWS Portfolio Platform
This website is itself an engineering project. It runs on AWS as a secure static site with a serverless contact backend, defined entirely in Terraform and deployed automatically through GitHub Actions using short-lived OIDC credentials — no stored AWS keys.
Goal
A portfolio that proves the skills it describes.
The objective was not simply to publish a website, but to run it the way production infrastructure should be run: reproducible from code, secured by default, observable, automatically deployed, and free of long-lived credentials. The site therefore doubles as a working reference for the cloud and DevOps practices described across the rest of this portfolio.
Architecture
Static delivery, a serverless API, and a single edge.
Visitors are served through CloudFront over HTTPS. The static site lives in a private S3 bucket reachable only through CloudFront via Origin Access Control. The same distribution routes/api/*to the contact backend, so the whole site sits behind one WAF and one certificate.
Internet
↓
Route 53 (DNSSEC signed)
↓
CloudFront + ACM TLS + WAF + Security Headers
↓ ┌───────────────┐
Static site ← private S3 (OAC / SigV4)
/api/* → API Gateway → Lambda → SES → private inbox
Delivery
Everything ships through an automated, credential-free pipeline.
All infrastructure is defined in modular Terraform with remote state in S3 using native state locking. GitHub Actions authenticates to AWS through OpenID Connect, assuming short-lived roles — a read-only role for pull-request plans and a separate deploy role scoped to the production environment. There are no static AWS access keys.
Pull request
↓ terraform fmt · module & composition tests · validate
↓ plan (OIDC) · TFLint · Checkov · Trivy
Merge to main
↓ production environment (manual approval)
↓ terraform apply (OIDC)
↓ Astro build → S3 sync → CloudFront invalidation
↓ HTTPS + contact-API smoke test
The deploy role holds no IAM self-modification permissions; the Lambda execution role and OIDC roles are provisioned separately in a security plane. UI-only changes skip the Terraform apply step to avoid unnecessary control-plane activity.
Security
Secure by default at every layer.
The S3 origin blocks all public access and is reachable only through CloudFront. TLS is enforced (1.2+), with AWS-managed security response headers including HSTS.
A CloudFront-scoped WAF applies the AWS managed common rule set, the Known Bad Inputs set (covering Log4j), and an IP rate-based rule in front of both the site and the contact API.
The contact Lambda validates input, uses a honeypot field to drop bots, applies rate limiting, and sends through SES from a verified domain identity — the destination inbox is never exposed publicly.
DNS is signed with DNSSEC (a KMS-backed key-signing key), and the delegation-signer record is published at the registrar, so the chain of trust validates end to end.
Infrastructure-as-code security scanning (TFLint, Checkov, Trivy) runs on every change, and each finding is classified as implemented, accepted risk, or documented false positive in a written security policy.
Observability
Logs, alarms, and a cost guardrail.
CloudFront access logs, S3 server access logs, WAF logs, and Route 53 query logs are delivered to a governed, private log destination. A CloudWatch alarm watches the contact Lambda for errors, notifications are delivered through SNS, and a monthly AWS Budget provides an early cost-overrun signal.
Results
A reproducible, self-operating site.
The entire platform is reproducible from code and deploys on merge with a single manual approval gate. Frontend quality checks, Terraform validation and tests, and IaC security scans are required before any change reaches production.
Each deploy verifies itself: the pipeline confirms the homepage returns HTTP 200 with the expected security headers and that the contact API correctly rejects invalid input — without sending a real email.
Lessons
Small platform decisions compound.
OIDC federation removes an entire class of credential-management risk compared with stored access keys.
Keeping IAM changes out of the deploy role's permissions preserves a clear separation between the delivery pipeline and the security plane.
Security scanners are most useful when their findings are decided deliberately and documented, rather than chased blindly toward a green badge.
Treating routing, caching, and per-object cache headers explicitly keeps a static site both fast and always up to date after deploys.
Technologies