Why I did it
I finished my Computer Engineering Diploma at Singapore Polytechnic in March 2026. Uni only started in August, so I had about five months of time to apply for jobs and wait for uni offers.
By then, I had done three cloud and infra certs while doing my diploma, Terraform Associate, AWS Cloud Practitioner and AWS Solutions Architect Associate. All three were multiple choice. And I had never built something that I had to keep running. So I picked the Cloud Resume Challenge to get real hands-on experience. It's a project created by Forrest Brazeal where you put your resume on the cloud as a real website with a visitor counter, then build everything around it such as infrastructure as code, CI/CD, tests and monitoring. I completed all 16 steps and the DevOps mods, and if I have time, I want to try the other mods too.
The result is this portfolio website. It's a static site in a private S3 bucket behind CloudFront, with a visitor counter (Python Lambda, API Gateway, DynamoDB), five CloudWatch alarms that end up in Slack, and everything in Terraform. GitLab CI deploys it into two AWS accounts using OIDC, so no AWS access keys are stored anywhere. Test deploys automatically, and prod after I approve it. I also did the book's extra credit: my AWS Organization and SSO access are set up as code with org-formation, which the book warns is hard for beginners but I did it anyways.
Choices I made myself (compared to what the CRC suggests)
The book states what steps to take to complete the challenge and suggests the mods for extra hands-on too. Along the way, I made a few decisions of my own.
- Terraform with state in HCP Terraform: The book's original spec uses SAM, but the book itself suggests swapping it for a more common IaC tool. I picked Terraform from its list because it isn't AWS-only, and I had already passed the Terraform Associate Cert. I also moved the state to HCP Terraform after I almost committed my state file to git.
- A private bucket instead of S3 website hosting: The book's set up is an S3 static website with CloudFront in front for HTTPS. I changed it so the bucket is fully private and only my CloudFront distribution can read it, using Origin Access Control. The tradeoff: S3 website hosting automatically serves index.html for folder paths, and a private bucket doesn't. I had to add a small CloudFront function to rewrite those URLs myself.
- One Repo: The book suggests separate repos for the website and the backend. I kept everything in one, and it turned out to matter. The website build needs the API URL that Terraform creates. So the pipeline has to go Infra first, then build, then deploy, and in one repo that's just one pipeline in the right order.
Things that broke
- The teardown that deleted CI: For the multi-environment mod I added a destroy job for the test environment. It ran, and the destroy took the OIDC provider and the CI role down with it, which meant CI had no identity left to rebuild anything. I had to recover access by hand through SSO before the pipeline could stand the test environment back up.
That's why prevent_destroy is on those two now. It was not a clever upfront design decision. I added it later, after a destroy had already removed the exact identity CI needed.
- The website that downloaded instead of opening. I uploaded my site with Terraform's aws_s3_object, and pages downloaded as files instead of opening. Terraform didn't set the Content-Type, so S3 served everything as
binary/octet-stream
I switched to aws s3 sync in the pipeline, which detects file types correctly. Later, reading other people's Cloud Resume Challenge posts, I found I wasn't the only one who hit this.
What I'd tell someone starting
Months later I tried to explain my own pipeline and couldn't. It had grown in layers, and prod wasn't even waiting for test. I rebuilt it as one straight line: test first, then prod after one manual click. My advice would be to look at the big picture, not only the part you're stuck on. I spent too long getting single pieces right and not enough time checking how they fit together. There's still more to do, like scoping the CI role down, but now I know how it all connects.

Top comments (0)