From zero AWS infrastructure to a production-ready Amazon EKS cluster using reusable Terraform modules.
I know that when we create an AWS EKS cluster manually through AWS console, it is a hassle because it involves VPC's, subnets, IAM roles, security groups, node groups, ECR repositories, storage drivers... it quickly becomes repetitive.
So, I wanted a setup where I could deploy a complete Kubernetes infrastructure for any new project by changing just a few variables.
What why I built a reusable Terraform template that provisions an entire production-style Kubernetes environment on AWS.
The result?
- Fully modular infrastructure.
- Reusable across projects.
- Deployable in about 5–10 minutes.
- Ready for GitOps and CI/CD workflows.
Here's how it works.
What gets created automatically?
Running a single terraform apply provisions the complete Kubernetes platform.
Infrastructure Provisioned
Kubernetes Cluster Amazon EKS
Networking Custom VPC with Public & Private Subnets
Internet Access Internet Gateway + NAT Gateway
Routing Route Tables + Associations
Security Network ACLs + Security Groups
Compute Managed Node Groups with Auto Scaling
Identity IAM Roles + OIDC Provider (IRSA)
Storage Amazon EBS CSI Driver
Logging CloudWatch Log Groups
Container Registry Amazon ECR repositories
Everything is created using Infrastructure as Code.
Architecture Overview
The infrastructure follows a typical production Kubernetes architecture with:
- Public subnets expose the Load Balancer.
- Private subnets host Kubernetes worker nodes.
- NAT Gateway provides outbound internet for private workloads.
- EKS Control Plane manages Kubernetes.
- ECR stores application images.
- CloudWatch collects cluster logs.
- IRSA securely connects workloads to AWS services.
This separation keeps worker nodes private while exposing only the required application endpoints.
terraform-eks-template/
│
├── modules/
│ ├── networking/
│ ├── iam/
│ ├── eks/
│ ├── ecr/
│
├── terraform.tfvars
├── provider.tf
├── versions.tf
├── outputs.tf
└── main.tf
Each module has a single responsibility.
This makes the template easy to extend later with RDS, EFS, ALB Controller, or Secrets Manager.
Configure a New Project in Minutes
The only file that changes between projects is terraform.tfvars.
project_name = "revenuepilot"
cluster_name = "revenuepilot-eks"
aws_region = "ap-south-1"
node_instance_type = "t3.medium"
desired_capacity = 2
ecr_repositories = [
"frontend",
"backend",
"ai-service"
]
That's it.
No Terraform code changes.
Deploy Everything
Initialize Terraform.
terraform init
Review the infrastructure plan.
terraform plan
Create all AWS resources.
terraform apply
Terraform provisions every dependency in the correct order.
Within a few minutes the cluster is ready.
Verify the Cluster
Update kubeconfig.
aws eks update-kubeconfig \
--region ap-south-1 \
--name revenuepilot-eks
Check nodes.
kubectl get nodes
Example output:
NAME STATUS ROLES AGE
ip-10-0-1-101.ap-south-1.compute.internal Ready <none> 4m
ip-10-0-2-134.ap-south-1.compute.internal Ready <none> 4m
Your Kubernetes cluster is live.
Built-in Engineering Practices
I wanted this repository to resemble how infrastructure is managed in real projects.
Modular Architecture
Every AWS service is isolated into reusable modules.
Benefits:
Easier maintenance.
Reusable in multiple repositories.
Clear separation of concerns.
Parameterized Configuration
Everything configurable lives inside variables.
Examples include:
AWS Region
Cluster name
Instance type
Node scaling
Repository names
CIDR ranges
Consistent Resource Tagging
Every resource receives common tags.
tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
}
Useful for cost tracking and resource discovery.
IAM Separation
Separate IAM roles exist for:
- EKS Control Plane.
- Worker Nodes.
- Kubernetes Workloads via IRSA.
This follows the principle of least privilege.
The Most Interesting Challenge: IRSA + Amazon EBS CSI Driver
The infrastructure deployed successfully.
Persistent volumes didn't.
Pods trying to create EBS volumes failed with authentication errors.
Example error:
AccessDenied:
Not authorized to perform ec2:CreateVolume
The issue wasn't Kubernetes.
It was IAM.
Why It Happened
The EBS CSI Driver runs inside Kubernetes.
It needs AWS credentials to create EBS volumes.
Using node IAM permissions works, but it's not recommended.
The correct solution is IAM Roles for Service Accounts (IRSA).
Fix
The solution involved three Terraform resources:
- Enable the cluster's OIDC Provider.
- Create an IAM Role trusted by that OIDC provider.
- Attach the role to the Kubernetes service account.
Terraform handled the trust relationship automatically.
After applying the configuration:
- CSI Driver authenticated successfully.
- PersistentVolumeClaims bound correctly.
- Dynamic EBS volumes were created automatically.
This was one of the best learning experiences in connecting Kubernetes identity with AWS IAM.
Why IRSA Matters
Instead of giving every pod node-level AWS permissions:
Without IRSA
- Every workload shares node IAM permissions.
- Larger blast radius.
With IRSA
- Each workload gets its own IAM role.
- Fine-grained AWS permissions.
- More secure production architecture.
IRSA is essential when applications need access to S3, Secrets Manager, DynamoDB, SQS, or EBS.
GitHub Repository
The complete Terraform template is available on GitHub.
GitHub: https://github.com/Nishath06/devops-starter-kit/tree/main/devops-starter-kit/terraform/modules
If you're learning DevOps, Kubernetes, or Terraform, I'd love to hear your feedback or suggestions for improving the template.
Happy building!




Top comments (0)