Key Takeaways
- Terraform EKS provisioning turns a manual, console-driven Kubernetes cluster build into a repeatable, version-controlled process.
- The official
terraform-aws-modules/eks/awsmodule handles the cluster, node groups, and IAM roles in a fraction of the raw-resource code. - Cluster creation alone isn't enough — you need the
aws-authConfigMap (or EKS access entries) wired correctly forkubectlaccess, or you'll get "Unauthorized" errors. - State locking (S3 + DynamoDB, or Terraform Cloud) is mandatory for any team running
terraform-eksbeyond a single operator.
Why Terraform for EKS Instead of the Console or eksctl
eksctl is fast for throwaway clusters, and the console works for a one-off experiment. Neither gives you:
- A diffable, reviewable record of exactly what infrastructure exists
- Safe, repeatable environment promotion (dev → staging → prod with the same code)
- Drift detection when someone manually changes a setting
Terraform EKS modules close that gap by treating the cluster, its networking, and its node groups as versioned code alongside the rest of your infrastructure.
Step 1: VPC Foundation
EKS needs subnets across at least two Availability Zones, tagged so the Kubernetes cloud-controller can discover them for load balancer provisioning.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "eks-prod-vpc"
cidr = "10.20.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.20.1.0/24", "10.20.2.0/24", "10.20.3.0/24"]
public_subnets = ["10.20.101.0/24", "10.20.102.0/24", "10.20.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = false
public_subnet_tags = {
"kubernetes.io/role/elb" = "1"
}
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = "1"
}
}
Step 2: The EKS Cluster and Node Groups
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "prod-cluster"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
cluster_endpoint_public_access = true
eks_managed_node_groups = {
general = {
min_size = 2
max_size = 6
desired_size = 3
instance_types = ["m5.large"]
capacity_type = "ON_DEMAND"
}
}
enable_cluster_creator_admin_permissions = true
}
Apply it:
terraform init
terraform plan -out=eks.plan
terraform apply eks.plan
A full terraform-eks build like this typically takes 12–18 minutes, mostly waiting on the EKS control plane to become active.
Step 3: Connect kubectl
aws eks update-kubeconfig --region us-east-1 --name prod-cluster
kubectl get nodes
If this returns "Unauthorized," your IAM principal isn't mapped into the cluster's RBAC. With the module's enable_cluster_creator_admin_permissions = true, the Terraform-executing identity gets admin access automatically; for additional users, add EKS access entries:
resource "aws_eks_access_entry" "developer" {
cluster_name = module.eks.cluster_name
principal_arn = "arn:aws:iam::123456789012:role/developer-role"
}
resource "aws_eks_access_policy_association" "developer_view" {
cluster_name = module.eks.cluster_name
principal_arn = aws_eks_access_entry.developer.principal_arn
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy"
access_scope { type = "cluster" }
}
Architecture of a Terraform-Provisioned EKS Cluster
<svg viewBox="0 0 760 320" xmlns="http://www.w3.org/2000/svg" style="width:100%;max-width:760px;font-family:sans-serif;color:currentColor">
<style>
.box { fill: none; stroke: currentColor; stroke-width: 1.5; }
.lbl { fill: currentColor; font-size: 13px; }
.sub { fill: currentColor; font-size: 11px; opacity: 0.7; }
.arrow { stroke: currentColor; stroke-width: 1.5; marker-end: url(#a3); fill: none; }
</style>
<defs>
<marker id="a3" markerWidth="8" markerHeight="8" refX="6" refY="3" orient="auto">
<path d="M0,0 L6,3 L0,6 Z" fill="currentColor" />
</marker>
</defs>
<rect class="box" x="20" y="20" width="180" height="50" rx="6" />
<text class="lbl" x="35" y="48">main.tf (modules)</text>
<rect class="box" x="260" y="20" width="180" height="50" rx="6" />
<text class="lbl" x="275" y="48">terraform apply</text>
<rect class="box" x="500" y="20" width="220" height="50" rx="6" />
<text class="lbl" x="515" y="48">S3 + DynamoDB state lock</text>
<path class="arrow" d="M200,45 H260" />
<path class="arrow" d="M440,45 H500" />
<rect class="box" x="60" y="130" width="640" height="150" rx="8" />
<text class="sub" x="75" y="150">VPC (3 AZ, private + public subnets)</text>
<rect class="box" x="90" y="170" width="160" height="60" rx="6" />
<text class="lbl" x="105" y="195">EKS Control Plane</text>
<text class="sub" x="105" y="213">managed by AWS</text>
<rect class="box" x="300" y="170" width="160" height="60" rx="6" />
<text class="lbl" x="315" y="195">Managed Node Group</text>
<text class="sub" x="315" y="213">m5.large x3</text>
<rect class="box" x="510" y="170" width="160" height="60" rx="6" />
<text class="lbl" x="525" y="195">IAM + Access Entries</text>
<text class="sub" x="525" y="213">RBAC mapping</text>
<path class="arrow" d="M400,70 V130" />
</svg>
Common Terraform-EKS Pitfalls
- Forgetting subnet tags — the AWS Load Balancer Controller silently fails to provision ALBs without
kubernetes.io/role/elbtags. - Using local state — one laptop crash or two engineers running
applysimultaneously corrupts state; always use remote state with locking. - Pinning no version constraints —
~> 20.0style constraints prevent aterraform init -upgradefrom jumping major versions and breaking your config. - Skipping
terraform planreview — EKS node group replacements can cause unplanned downtime; always review the plan for node group recreation before applying.
FAQ
Can I manage an existing (console-created) EKS cluster with Terraform? Yes, via terraform import, but it's tedious for complex clusters. It's far easier to adopt Terraform EKS from the start for new clusters.
Does Terraform manage Kubernetes resources (Deployments, Services) too? It can, via the kubernetes or helm Terraform providers, but most teams split concerns: Terraform for infrastructure (cluster, nodes, IAM), and kubectl/Helm/Argo CD for in-cluster workloads.
How do I upgrade the EKS cluster version with Terraform? Bump cluster_version and run terraform plan — Terraform orchestrates the control plane upgrade, but you must upgrade node groups separately afterward to match.
Is the terraform-aws-modules/eks/aws module officially supported by AWS? It's community-maintained, not an official AWS product, but it's the de facto standard, widely used, and tracks EKS feature releases closely.
Provision Your EKS Cluster the Right Way
Getting terraform-eks right the first time — networking, IAM, and node groups — saves weeks of later rework. Talk to f9infotech's AWS and Kubernetes specialists about a production-ready EKS build.