DEV Community

Cover image for Build a Production-Ready Kubernetes Infrastructure on AWS with Terraform (EKS in 10 Minutes)
Nishath J P
Nishath J P

Posted on

Build a Production-Ready Kubernetes Infrastructure on AWS with Terraform (EKS in 10 Minutes)

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"
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. Enable the cluster's OIDC Provider.
  2. Create an IAM Role trusted by that OIDC provider.
  3. 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)