Terraform code I used to run hands-on DevOps training on AWS. Each student gets their own browser-based IDE, an isolated namespace on a shared EKS cluster, and IAM credentials scoped to it. GitLab runs in the same cluster so the class can practice real CI/CD without leaving the lab.
The repo is split into five layers that build on each other. Each layer has its own S3-backed Terraform state and is meant to be applied in order.
| Layer | What it builds |
|---|---|
00-vpceks |
VPC, EKS 1.30, managed node group for system workloads, ECR repo, Route53 zone, ACM cert, Karpenter IAM policy + SQS interruption queue |
01-k8s |
Cluster add-ons via Helm — VPC CNI, EBS CSI, AWS Load Balancer Controller, ExternalDNS, Karpenter |
02-karpenter |
Karpenter NodePool + EC2NodeClass (spot, c/m/r, gen >2), node IAM role, aws-auth patch |
03-gitlab |
GitLab CE via the official Helm chart, fronted by an ALB with the wildcard ACM cert |
04-webide |
One code-server per student: PVC, StatefulSet, ingress at <user>.students.<domain>, plus an IAM user, namespace, and RBAC bindings |
- One cluster, many students. Each user gets a dedicated namespace with full rights inside it, read-only on the
defaultnamespace, andget/list/watchon cluster-scoped resources like nodes and storage classes. They learnkubectlagainst a real cluster without being able to break each other's work. - Spot-only Karpenter pool. System pods stay on the small managed node group; everything else (GitLab, IDEs, student workloads) lands on spot via Karpenter.
aws-authis owned in two places. The EKS module creates it;02-karpenterand04-webidepatch it withkubernetes_config_map_v1_dataandprevent_destroy = trueso a strayterraform destroycannot lock the operator out.- Per-student secrets file.
04-webiderenderssecrets/<user>.txtwith the IDE URL and a randomly-generated password — drop it in the welcome email and you're done. - Custom IDE image.
04-webide/Dockerfileextendscodercom/code-serverwithawscliandkubectlso students start in a working environment.
cd 00-vpceks && terraform init && terraform apply
cd 01-k8s && terraform init && terraform apply
cd 02-karpenter && terraform init && terraform apply
cd 03-gitlab && terraform init && terraform apply
cd 04-webide && terraform init && terraform applyBefore 00-vpceks: create the S3 bucket referenced in every backend "s3" block (dmitriko-info-tf-state here — change it) and delegate the chosen domain to the Route53 zone created in stage 0 (the nameservers output tells you where to point it).
The student list lives in 04-webide/terraform.tfvars. Add or remove emails and re-apply — the rest is fully driven by for_each.
The setup is close to what I run in production, but EKS has moved on since I wrote this. Karpenter is now available as the managed Auto Mode autopilot, and access management has shifted from the aws-auth ConfigMap to native EKS access entries. If I were starting this lab today, both 02-karpenter and the aws-auth patches in 00-vpceks / 04-webide would go away.