Skip to content
← all projects

$ cat projects/portfolio-infrastructure.md

This Site, and Its Infrastructure

Angular prerendered to static HTML, on AWS I provisioned and deploy myself.

period: 2026 — Present

stack: Angular Terraform AWS CloudFront Lambda GitHub Actions

// 01 problem

Problem

For most of my career a DevOps team owned deployment. I could describe what happened after a merge, but I had never built it. That is a real gap, and reading about IAM is not the same as being denied by it.

// 02 approach

Approach

Angular prerenders every route to its own HTML file, so the site is genuinely static and crawlable rather than an empty shell that fills in later. It is served from a private S3 bucket through CloudFront, with the contact form's API Gateway attached to the same distribution under /api/* — same origin, so there is no CORS to configure. Terraform provisions all of it in two stages, since the bucket holding Terraform's own state has to be created before Terraform can use it. GitHub Actions deploys on push using OIDC, so no AWS credentials are stored anywhere.

// 03 outcome

Outcome

Live, deploying in under a minute on push, with no long-lived credentials. The most instructive part was the failure: GitHub's OIDC token identifies a repository by numeric ID rather than by name, so a trust policy that looked correct in every console view rejected every deploy. Finding it meant reading the actual denied request in CloudTrail. The gap it was meant to close is closed: my team has since moved toward owning its own deployments, and I am provisioning and deploying my own infrastructure at work rather than only here.