🚀 Complete Guide to EKS with Terraform: Add-ons, Nodes, VPCs, and More

BinereetDevopsBinereetDevops
5 min read

Amazon EKS is a managed Kubernetes service that lets you run Kubernetes clusters at scale on AWS. But doing it manually through the console? Tedious. That's where Terraform becomes your best friend.

In this blog, we’ll cover:

✅ Setting up EKS with Terraform
✅ Installing cluster add-ons
✅ Managing EKS nodes (with autoscaling)
✅ Configuring your VPC via Terraform

📁 Project Structure

Here’s the structure we’ll be working with:


├── provider.tf
├── locals.tf
├── vpc.tf
├── eks.tf ✅
├── outputs.tf
└── variables.tf

Let’s now go deep into eks.tf.


🧩 Complete eks.tf File

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.31"

  cluster_name    = local.name
  cluster_version = "1.31"

  cluster_endpoint_public_access = true
  public_access_cidrs = ["203.0.113.0/24", "0.0.0.0/0"]

  vpc_id                     = module.vpc.vpc_id
  subnet_ids                = module.vpc.private_subnets
  control_plane_subnet_ids  = module.vpc.intra_subnets

  ###########################
  # Cluster Add-ons Section
  ###########################
  cluster_addons = {
    vpc-cni = {
      most_recent = true
    }
    kube-proxy = {
      most_recent = true
    }
    core-dns = {
      most_recent = true
    }
  }

  ##########################################################
  # Managed Node Groups - Default & Specific Configuration
  ##########################################################
  eks_managed_node_group_defaults = {
    instance_types = ["t2.medium"]
    attach_cluster_primary_security_group = true
  }

  eks_managed_node_groups = {
    cluster-ng = {
      instance_types = ["t2.medium"]

      min_size     = 2
      max_size     = 3
      desired_size = 2

      capacity_type = "SPOT"

      tags = {
        Environment = local.env
        Terraform   = "true"
      }
    }
  }

  tags = {
    Terraform   = "true"
    Environment = local.env
  }
}

✅ Explanation:

  • module "eks": This creates a reusable module named eks.

  • source: Points to the Terraform registry module for EKS.

  • version: Ensures compatibility by locking the version to ~> 20.31.

  • cluster_name: Takes its value from the locals.tf file (e.g., eks-cluster).

  • cluster_version: Explicitly sets the Kubernetes version. If not set, the latest version will be used.

  • cluster_endpoint_public_access = true: This allows you to access the Kubernetes API from outside the VPC (e.g., your laptop or Jenkins).

  • public_access_cidrs: Limits who can access the public endpoint by IP range. 0.0.0.0/0 opens it to everyone (not recommended for production).
    vpc_id: Passes the VPC ID from your vpc.tf module.

  • subnet_ids: These are the private subnets where your worker nodes (EC2 instances) will live.

  • control_plane_subnet_ids: These are infra subnets, used only for the EKS control plane (for ENIs, not pods).

  • Enables essential Kubernetes components:

    • vpc-cni: Handles pod networking via AWS VPC.

    • kube-proxy: Enables networking between services and pods.

    • core-dns: Allows DNS-based service discovery inside the cluster.

  • most_recent = true: Ensures you're always using the latest stable version of each addon.

  • eks_managed_node_group_defaults: Applies defaults to all node groups.

    • instance_types: Defines EC2 type for nodes.

    • attach_cluster_primary_security_group: Ensures all nodes join the same security group as the cluster.

    • eks_managed_node_groups: Defines one or more autoscaling worker node groups.

    • cluster-ng: The name of this node group.

    • instance_types: Nodes will be launched using t2.medium instances.

    • min_size, max_size, desired_size: This node group will auto-scale between 2 and 3 nodes, maintaining 2 by default.

    • capacity_type = "SPOT": Uses spot instances to save cost.

    • tags: Helps label and filter resources (e.g., by environment).

    • These are global tags applied to all resources created by the EKS module.

    • Terraform = "true": Marks the resource as managed by Terraform.

    • Environment = local.env: Takes value like "dev" or "prod" from locals.tf.

🧠 Reference: locals.tf

locals {
  region          = "us-east-2"
  name            = "eks-cluster"
  vpc_cidr        = "10.0.0.0/16"
  azs             = ["us-east-2a", "us-east-2b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
  intra_subnets   = ["10.0.5.0/24", "10.0.6.0/24"]
  env             = "dev"
}

🌐 Reference: vpc.tf

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name            = "${local.name}-vpc"
  cidr            = local.vpc_cidr
  azs             = local.azs
  private_subnets = local.private_subnets
  public_subnets  = local.public_subnets
  intra_subnets   = local.intra_subnets

  enable_nat_gateway = true
  enable_vpn_gateway = true

  tags = {
    Terraform   = "true"
    Environment = local.env
  }
}

NAT Gateway allows private subnets to reach the internet (e.g., for pulling Docker images).
VPN Gateway enables secure connections to on-prem infrastructure.

🔐 Why Use Private Subnets for EKS?

EKS worker nodes and control plane components should live in private subnets for better security. They don’t need public IPs — they just require outbound access via a NAT Gateway to fetch container images.

🧠 Public subnets are best for:

  • Load Balancers (ALB, NLB)

  • Bastion Hosts

  • Public-facing services


🔄 What are Infra Subnets?

Infra subnets are dedicated private subnets used only by the EKS control plane's ENIs (Elastic Network Interfaces).

🛡 Why separate them?

  • Network Isolation: Separate management traffic from application traffic.

  • Compliance: Some industries (finance, healthcare) demand network-level isolation.

  • IP Management: Avoid subnet IP exhaustion in large clusters.

If you're building an enterprise-scale or regulated environment, use infra subnets.

🤖 eksctl vs Terraform: VPC Creation

If you run:

eksctl create cluster

...from a Jenkins server in a default VPC, eksctl will create a new VPC by default unless you specify --vpc-id.

Even if Jenkins and EKS live in different VPCs, Jenkins can still communicate with the EKS API if:

  • The EKS endpoint is public.

  • Jenkins has internet access.

  • You’ve set aws eks update-kubeconfig.

🌐 CoreDNS — The Invisible Glue in Kubernetes

CoreDNS is Kubernetes’ internal DNS service. It lets pods and services resolve names like:

my-service.default.svc.cluster.local

Without CoreDNS:

  • Pods couldn’t find each other by name.

  • Services wouldn’t be discoverable.

  • Autoscaling and service meshes would break.

Even if you define services (e.g., LoadBalancer or ClusterIP), CoreDNS is what makes name resolution work.


🌉 vpc.tf — NAT & VPN Gateways

Two crucial flags:

  • enable_nat_gateway = true
    → Allows private subnets to reach the internet (e.g., pull images from GitHub).

  • enable_vpn_gateway = true
    → Enables secure communication with on-prem environments over a VPN.

If your tools like ArgoCD are in private subnets, they need a NAT Gateway to access external services like GitHub.

🧩 Final Thoughts

Setting up EKS with Terraform is powerful but requires attention to networking, access control, and best practices like using private and infra subnets. Whether you're deploying for a startup or a regulated enterprise, these patterns will keep your infrastructure scalable, secure, and compliant.

💬 Got Questions?

Leave a comment or ping me on LinkedIn!
Let’s keep the DevOps conversation rolling. 🚀

My LinkedIn Profile - https://www.linkedin.com/in/binereet-singh-9a7685316/

0
Subscribe to my newsletter

Read articles from BinereetDevops directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

BinereetDevops
BinereetDevops