case study · serverless delivery · live
Cloud portfolio infrastructure
The infrastructure behind this portfolio: private static content through CloudFront, a serverless visitor API, and separate OIDC workflows for reviewing and applying Terraform changes.
problem
Make the portfolio itself verifiable infrastructure work
The site needed global HTTPS delivery, no public storage bucket, a small dynamic visitor counter and a deployment path that did not rely on console changes or permanent AWS credentials.
Astro is used only as a static build system. The deployed output is HTML, CSS and minimal JavaScript served from S3 through CloudFront.
architecture
Private origin with a serverless API path

Route 53 and ACM provide the custom domain and TLS certificate. CloudFront is the only public delivery layer; direct S3 object access remains blocked.
decisions and tradeoffs
Key design decisions
- Static output instead of an application server
- Pages are cheap to cache and have no runtime to patch. Dynamic behavior is limited to isolated serverless endpoints.
- CloudFront origin access control
- The bucket stays private and grants reads only to the distribution, adding policy setup in exchange for removing direct object exposure.
- Separate plan and apply workflows
- Pull requests can review Terraform changes without receiving apply permissions. Merge-time automation has the narrower deployment role.
- OIDC instead of static AWS secrets
- GitHub assumes short-lived roles subject to repository and branch conditions; no AWS access key is stored in Actions.
what broke
Preview paths exposed base-path assumptions
The same static build is served both at the production root and under a preview prefix. Root-relative links worked in production but escaped the preview path. Centralizing URL generation around Astro's configured base fixed navigation, resume and asset links.
The visitor script is generated with the deployed API URL by Terraform. A local Astro preview therefore cannot report a real count unless that deployment-generated script is injected; the interface intentionally shows a neutral placeholder rather than a fabricated value.
delivery
Infrastructure and content move together
- 100%infrastructure defined in Terraform
- 0public S3 buckets
- 2OIDC deployment roles
- 1serverless visitor endpoint