2025 · Case study

RoboShop — VM-Based Multi-Environment AWS Infrastructure

The foundation. Before touching Kubernetes, I built the entire platform on EC2 — 15 Terraform modules, 13 security groups, Ansible-pull config, custom AMI baking, and dev/prod parity.

01

Overview

An 11-service e-commerce stack provisioned end-to-end on AWS with modular Terraform, configured via Ansible-pull, deployed to Auto Scaling Groups behind internal + external ALBs, and promoted dev → prod through a Jenkins pipeline. This is where I learned why infrastructure has to be code, not clicks.

02

Problem

Rebuilding infrastructure by hand takes hours and drifts between engineers. Codifying it makes a full stand-up a 15-minute apply with an audit trail — and forces you to understand every SG rule, every route, every IAM boundary.

03

Architecture

Interactive architecture — click a component
pipeline · Terraform (15 modules)

Root + child modules. Remote state on S3, locked via DynamoDB.

04

CI/CD Flow

  1. 01Terraform reviewed via PR with tflint + tfsec in CI; plan posted to the PR as a comment.
  2. 02Merge to main triggers Jenkins to apply against dev; promotion job applies to staging/prod.
  3. 03AMI baking pipeline: EC2 → Ansible configure → stop → bake AMI → delete → Launch Template → ASG.
  4. 04Catalogue service CI: version detection from package.json → npm build → SonarQube + SAST → Nexus publish → downstream CD.
05

Infrastructure

  • 15 Terraform modules with S3 remote state + DynamoDB locking; cross-module data via SSM Parameter Store — zero hardcoded values.
  • Custom VPC across 2 AZs: public, private, and database subnets — all module-managed.
  • 13 least-privilege security groups. Service-to-service on exact ports; SSH restricted to a VPN CIDR.
  • ASGs with 50% CPU target tracking; internal ALB with host-header routing per service; external ALB + Route 53 on stallions.space.
  • Ansible-pull model — instances pull their own config at boot. No manual SSH, no drift.
06

Technologies Used

Terraform (15 modules, remote state + locking)Ansible (pull-based)JenkinsAWS EC2 · ALB · ASG · Route 53 · SSM · S3AlmaLinuxSonarQube · Nexus
07

Challenges

Challenge
Service discovery across 11 EC2s was brittle.
Resolution
Adopted a Route 53 private hosted zone and generated A records from Terraform outputs — services resolve each other by DNS, not IP.
Challenge
Configuration drift after manual hotfixes.
Resolution
Switched to ansible-pull at boot and applied an IAM boundary policy that blocked out-of-band console changes. Enforced tfsec in CI as a hard gate.
Challenge
One VPC module, two very different environments (dev vs prod).
Resolution
Wrote the VPC as a dual-mode module — same code path, different variable set. Region-agnostic, input-validated, reusable across every future project.
08

Lessons Learned

  • Modules over monoliths. One root, many small versioned modules — never copy-paste HCL.
  • State is production data. Remote backend + locking from day one.
  • If you can't Terraform it, you don't understand it. The console hides too much.
Source code
Full repository on GitHub
Open repo ↗