A production-grade AWS EKS platform built with Terraform, following GitOps principles and AWS Well-Architected Framework. Designed to host microservices workloads with security, observability, and operational maturity baked in from day one.
┌─────────────────────────────────────────────────────────────────┐
│ AWS us-east-1 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ VPC │ │
│ │ 10.0.0.0/16 │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Public AZ-A │ │ Public AZ-B │ │ │
│ │ │ 10.0.1.0/24 │ │ 10.0.2.0/24 │ │ │
│ │ │ [NAT GW] │ │ [NAT GW] │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ │ │
│ │ │ │ │ │
│ │ ┌──────▼───────┐ ┌──────▼───────┐ │ │
│ │ │ Private AZ-A │ │ Private AZ-B │ │ │
│ │ │ 10.0.10.0/24 │ │ 10.0.11.0/24 │ │ │
│ │ │ [EKS Nodes] │ │ [EKS Nodes] │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ EKS Control Plane │ │ │
│ │ │ v1.29 | Private Endpoint │ │ │
│ │ │ OIDC | IRSA | Managed Node Groups │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ ECR │ │ KMS │ │ S3 │ │ CloudWatch │ │
│ │(registry)│ │(secrets) │ │(tf state)│ │ (logging) │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
- Multi-environment — Dev, Staging, Prod with isolated state and variable overrides
- Least-privilege IAM — IRSA (IAM Roles for Service Accounts) per workload, no node-level over-permissioning
- Private EKS — Control plane endpoint restricted; nodes in private subnets only
- Encrypted at rest — KMS CMK for EKS secrets, EBS volumes, and S3 state backend
- GitOps-ready CI/CD — GitHub Actions pipeline with plan-on-PR, apply-on-merge
- Secure remote state — S3 backend with DynamoDB locking and versioning enabled
- Managed node groups — Auto-scaling with launch templates, IMDSv2 enforced
- Network isolation — Security groups follow zero-trust; no 0.0.0.0/0 ingress on nodes
.
├── modules/
│ ├── vpc/ # VPC, subnets, NAT GW, route tables, VPC flow logs
│ ├── eks/ # EKS cluster, node groups, OIDC, add-ons
│ ├── iam/ # Cluster roles, node roles, IRSA helper
│ └── bastion/ # Optional SSM-based bastion (no SSH keys)
├── environments/
│ ├── dev/ # Dev-specific tfvars and backend config
│ ├── staging/ # Staging-specific tfvars and backend config
│ └── prod/ # Prod-specific tfvars and backend config
├── policies/ # IAM policy JSON documents
└── .github/
└── workflows/ # CI/CD pipeline definitions
| Tool | Version |
|---|---|
| Terraform | >= 1.6.0 |
| AWS CLI | >= 2.x |
| kubectl | >= 1.29 |
| helm | >= 3.x |
Before first apply, create the S3 backend and DynamoDB table:
cd environments/dev
./bootstrap.shterraform init -backend-config=backend.hcl
terraform plan -var-file=terraform.tfvars -out=tfplanterraform apply tfplanaws eks update-kubeconfig \
--region us-east-1 \
--name $(terraform output -raw cluster_name)| Environment | Node Size | Min Nodes | Max Nodes | Notes |
|---|---|---|---|---|
| dev | t3.medium | 1 | 3 | Single AZ acceptable |
| staging | t3.large | 2 | 5 | Mirror of prod topology |
| prod | m5.xlarge | 3 | 10 | Multi-AZ, PodDisruptionBudget |
The GitHub Actions workflow enforces the following gates:
PR Opened/Updated
│
├── terraform fmt --check
├── terraform validate
├── tflint
├── tfsec (security scan)
└── terraform plan → posted as PR comment
PR Merged to main
│
└── terraform apply (auto-approve, environment-scoped)
Workflow files: .github/workflows/
- IMDSv2 enforced on all EC2 nodes via launch template (
http_tokens = required) - Secrets encryption via AWS KMS customer-managed key
- IRSA used for all workload AWS access — no static credentials
- Private endpoint only for EKS API server in staging/prod
- VPC Flow Logs enabled and shipped to CloudWatch
See each environment's terraform.tfvars for full variable documentation.
Key variables:
| Variable | Description |
|---|---|
cluster_name |
EKS cluster name |
cluster_version |
Kubernetes version |
vpc_cidr |
VPC CIDR block |
node_instance_types |
List of EC2 instance types for nodes |
min_size |
Minimum node group size |
max_size |
Maximum node group size |
desired_size |
Desired node group size |
| Output | Description |
|---|---|
cluster_name |
EKS cluster name |
cluster_endpoint |
EKS API server endpoint |
cluster_ca_certificate |
Base64 cluster CA cert |
oidc_provider_arn |
OIDC provider ARN for IRSA |
node_group_role_arn |
IAM role ARN attached to node group |
vpc_id |
VPC ID |
private_subnet_ids |
List of private subnet IDs |
- Branch from
main - Run
terraform fmtandtflintlocally before pushing - PR triggers plan automatically — review before merging
- Never commit
.tfstateor.tfvarscontaining secrets
MIT