A full re-architecture of the canonical Google Online Boutique microservice demo onto Amazon EKS, provisioned end-to-end through a three-state Terraform pipeline and handed off to Argo CD for GitOps-managed platform delivery, zero local terraform apply, zero local kubectl apply, everything driven by GitHub Actions via OIDC. This is the direct AWS-native successor to online-boutique-doks-pf (DigitalOcean Kubernetes) and shares its application repo, online-boutique-app.
| 3 Terraform states, isolated | 17 Argo CD-managed platform applications |
| 0 static cloud credentials, OIDC end to end | 4 distinct IRSA roles, each scoped to one controller |
| 0 NAT-Gateway-avoidance shortcuts at node scale | 1 shared ALB serving 3 hostnames via one group |
| 1 wildcard ACM cert, DNS-validated, zero downtime renewal | 1 external-dns install replaces all manual Route 53 work |
-
Three-state Terraform, isolated.
Account foundation (IAM/OIDC/ECR) → hardware/network (VPC/EKS/IRSA/ACM) → software mesh (namespaces/ALB Controller/external-dns/Argo CD). Each state is independently applied via its own GitHub Actions workflow, authenticated via OIDC, the account foundation's role does not have access to the cluster-scoped Terraform, and vice versa.
-
IRSA, scoped per controller.
No node-wide implicit AWS access. The ALB Controller, EBS CSI driver, external-dns, and External Secrets Operator each authenticate as their own IAM role via their own Kubernetes ServiceAccount external-dns can only touch the
suworks.mehosted zone, External Secrets Operator can only read secrets underonline-boutique/*. -
DNS-validated TLS, not ACME.
The DOKS build's own
operations.mddocuments deny-allNetworkPoliciesbreaking Let's Encrypt's HTTP-01 solver which is a structural conflict, not a misconfiguration. ACM's DNS-01 validation removes the live-traffic dependency entirely; the ALB terminates TLS directly against a wildcard cert requested once in Terraform. -
Zero-touch mTLS trust distribution.
Linkerd's identity trust anchor is generated by cert-manager and continuously synced into a ConfigMap by trust-manager (
identity.externalCA: true) no PEM ever lives in git, and a full cluster rebuild regenerates the entire chain automatically. -
Secrets outside the cluster's lifecycle.
AWS Secrets Manager + External Secrets Operator, IRSA-authenticated, replaces a sealed-secrets keypair that by design cannot survive a cluster rebuild or migration. This is not hypothetical: it's exactly what broke during this migration and is documented in
docs/doks-to-eks-migration.md. -
Defense in depth, unchanged from the DOKS build.
Kyverno (7 CEL admission policies: non-root, read-only rootfs, drop-all-capabilities, mandatory probes, mandatory resource limits, no
:latesttags, restricted seccomp), Falco eBPF runtime detection, Linkerd mTLS mesh, Cosign-signed images, ECR scan-on-push + Trivy CI gate all fully portable, zero AWS-specific changes required.
%%{init: {"flowchart": {"htmlLabels": true, "curve": "linear"}, "theme": "base"} }%%
flowchart TB
classDef ci fill:#E8F0FE,stroke:#4285F4,stroke-width:2px,color:#174EA6;
classDef tf fill:#FEF7E0,stroke:#F9AB00,stroke-width:2px,color:#B06000;
classDef gitops fill:#F3E8FD,stroke:#A142F4,stroke-width:2px,color:#681DA8;
classDef svc fill:#E6F4EA,stroke:#34A853,stroke-width:2px,color:#137333;
GH["GitHub Actions<br/>OIDC W/ zero static keys"]
B["Terraform: bootstrap<br/>VPC · EKS · IRSA · ACM · Secrets Manager"]
C["Terraform: cluster<br/>Namespaces · ALB Controller · external-dns · Argo CD"]
Handoff["kubectl apply root-app.yaml<br/>one-time Terraform → Argo CD handoff"]
Argo["Argo CD<br/>owns the cluster from here"]
Platform["Platform: Kyverno · Falco<br/>Linkerd · Loki · Prometheus"]
App["Boutique: 11 services<br/>from ECR"]
GH --> B --> C --> Handoff --> Argo
Argo --> Platform
Argo --> App
class GH ci;
class B,C tf;
class Handoff,Argo gitops;
class Platform,App svc;
online-boutique-eks-pf/
├── infrastructure/envs/prod/
│ ├── 01_iam.tf, 02_ecr-bootstrap.tf # Account foundation; state 1
│ ├── bootstrap/ # VPC, EKS, IRSA, ACM, Secrets Manager; state 2
│ └── cluster/ # Namespaces, ALB Controller, external-dns, Argo CD;state 3
├── clusters/boutique/
│ ├── argocd/ # root-app.yaml, the Terraform → Argo CD
│ ├── infrastructure-apps/ # 17 Argo CD Applications
│ └── platform-configs/ # Helm values, Kyverno policies, cert-manager/trust-manager, secrets
├── apps/boutique/ # Kustomize manifests, ECR-sourced images
├── .github/workflows/
│ ├── eks-bootstrap.yml
│ └── eks-cluster.yml
└── docs/
├── architecture.md
├── security.md
├── operations.md
└── doks-to-eks-migration.md
There is no supported local terraform apply or local kubectl apply path for this project. Every state change happens through GitHub Actions, authenticated via OIDC:
- Push to
infrastructure/envs/prod/bootstrap/**→eks-bootstrap.ymlprovisions VPC, EKS, IRSA, ACM, Secrets Manager - Push to
infrastructure/envs/prod/cluster/**orclusters/boutique/argocd/**→eks-cluster.ymlprovisions namespaces, the ALB Controller, external-dns, installs Argo CD, and applies the one-time root Application handoff - From that point forward, Argo CD, not Terraform, owns the cluster. Every platform component and every application workload is a git commit under
clusters/boutique/, reconciled automatically withselfHeal: true.
- Architecture full network design, IRSA role breakdown, Argo CD sync-wave ordering
- Security threat model, policy enforcement, secrets posture, documented risk acceptances
- Operations runbook for common failure patterns, health checks, cost controls
- DOKS → EKS Migration every architectural decision and every real incident hit during the port, with root cause
Built with Terraform (three-state, registry-module-first) · GitHub Actions OIDC · Amazon EKS · AWS Load Balancer Controller · external-dns · ACM · IRSA · AWS Secrets Manager · External Secrets Operator · Argo CD · Kyverno · Falco · Linkerd · kube-prometheus-stack · Loki · Grafana Alloy · cert-manager · trust-manager · Cosign



