Terraform + Amazon EKS
The Terraform EKS module: what it builds, and what's still yours to do
terraform-aws-modules/eks/aws is the standard way to stand up Amazon EKS with Terraform. It gets you a running cluster in one apply. It does not get you a cluster your team can actually ship to — that part is still on you.
By Muhammad Soliman, Founder at SeaGit ·
What does the Terraform EKS module actually create?
The terraform-aws-modules/eks/aws module creates an Amazon EKS control plane, its IAM role, an OIDC identity provider for IRSA, a KMS key for secrets encryption, and CloudWatch log groups for control plane logging. If you pass eks_managed_node_groups, it also creates EC2 Auto Scaling groups, their launch templates and node IAM roles. If you pass addons, it enables the named EKS add-ons — such as vpc-cni, kube-proxy and coredns — as managed resources on the cluster. If you pass access_entries, it maps IAM principals to Kubernetes RBAC permissions. It does not create a VPC, an ingress controller, a load balancer controller, external-dns records, cert-manager or TLS certificates, or any autoscaler beyond what a managed node group’s own ASG provides — those stay separate installs against the cluster this module hands back. kubectl access works the moment apply finishes; a public URL for an application, and a second engineer’s access, do not until you add them yourself.
A minimal working example
The module is versioned on the Terraform Registry and developed on GitHub. Its current major version is 21, and version 21 renamed several inputs from earlier majors — the cluster name input is now name (not cluster_name), the version input is kubernetes_version (not cluster_version), and add-ons are configured under addons (not cluster_addons). A v19- or v20-era example copied from an old blog post or Stack Overflow answer will not plan cleanly against v21 — check which major version an example targets before pasting it in.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 21.0"
name = "my-cluster"
kubernetes_version = "1.34"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
control_plane_subnet_ids = module.vpc.intra_subnets
endpoint_public_access = true
# Grants the identity running Terraform admin access via an
# access entry, instead of a manual aws-auth edit.
enable_cluster_creator_admin_permissions = true
addons = {
coredns = {}
kube-proxy = {}
eks-pod-identity-agent = { before_compute = true }
vpc-cni = { before_compute = true }
}
eks_managed_node_groups = {
default = {
ami_type = "AL2023_x86_64_STANDARD"
instance_types = ["m5.large"]
min_size = 2
max_size = 6
desired_size = 2
}
}
tags = {
Environment = "production"
Terraform = "true"
}
}On terraform apply, this produces a running control plane and one managed node group of two AL2023 nodes. The module exposes what you built through outputs — among them cluster_endpoint, cluster_certificate_authority_data, cluster_arn and cluster_addons — which is what you wire into a Kubernetes or Helm provider block, or into kubectl via aws eks update-kubeconfig. You can kubectl get nodes at this point. You cannot yet deploy an application and give it a working URL.
What the module doesn't give you
None of this is a criticism of the module — it is good at the one job it is scoped to do, and doing more would make it a worse module, not a better one. The gap is simply real, and it is where most of the actual setup time goes.
Node group sizing and pod-driven autoscaling
eks_managed_node_groups takes fixed min_size, max_size and desired_size values. Nothing scales those numbers in response to unscheduled pods — that is a separate controller, either the Kubernetes Cluster Autoscaler or Karpenter, both installed and configured after the cluster exists. Picking instance types, right-sizing requests and limits, and tuning scale-down behaviour so nodes do not flap is ongoing work, not a one-time setting.
Ingress, DNS and TLS
A default managed node group setup gives you a cluster with no way in from the internet. The AWS Load Balancer Controller (or an alternative ingress controller), external-dns to publish records for your domains, and cert-manager (or another ACME client) for certificates are three more Helm releases, each with its own IAM permissions to wire up — typically via IRSA or pod identity. EKS Auto Mode — opted into through the module's compute_config input instead of eks_managed_node_groups — narrows this: AWS documents application load balancing and cluster DNS as managed core components under Auto Mode. Custom-domain TLS and external DNS records for names outside the cluster are still yours either way.
EKS add-on selection and versions
The module will enable whatever you list under addons, but choosing that list is a decision the module does not make for you. Creating a cluster through the AWS console auto-adds kube-proxy, the VPC CNI and CoreDNS; creating one through Terraform does not — you declare each one explicitly, and each entry carries its own version behaviour (most_recent defaults to true, or pin a version with addon_version). Storage drivers like the EBS or EFS CSI add-ons, if your workloads need persistent volumes, are opt-in the same way.
Access management beyond the creator
enable_cluster_creator_admin_permissions covers the identity that ran terraform apply. Everyone else on the team needs their own entry under access_entries, each one naming a policy association and an access scope. On a cluster created before EKS access entries existed, migrating off a hand-edited aws-auth ConfigMap is a manual, one-by-one process — AWS only carries the original cluster creator over automatically.
Upgrades
Bumping kubernetes_version and re-applying moves the control plane. Node groups need their own rollout to actually run the new version — new launch template, new nodes, old nodes drained. Amazon EKS keeps a rolling set of minor versions on standard support at any time (1.34 through 1.36 as of this writing, per AWS's release notes), each with its own deprecations to read before you upgrade — older versions move to paid extended support rather than disappearing outright. None of that cadence is enforced by the module; it applies whatever version you tell it to.
Where SeaGit fits, if you'd rather not own that list
Everything in the section above is real work, and it is recurring work — every new environment repeats it, and every upgrade touches most of it again. SeaGit provisions Amazon EKS with Terraform inside your own AWS account — the same control plane and node groups this module would build — and then manages the layer this post just walked through: node group sizing, EKS add-ons, an ingress controller, external-dns and cert-manager for DNS and TLS, cluster upgrades, and scheduled scale-down for non-production clusters so they are not billed around the clock. AWS is the only cloud SeaGit provisions into today; Azure and GCP are on the roadmap, not available now.
If you are already comfortable owning that list, the module above is a solid foundation to build it on yourself — see SeaGit's networks documentation for how the VPC layer this module assumes gets built, or compare platforms if you are weighing a managed option against doing it by hand.
Read next
Terraform EKS module — FAQ
Do I need to write the VPC myself before using the EKS module?
- Yes. The terraform-aws-modules/eks/aws module takes vpc_id, subnet_ids and, optionally, control_plane_subnet_ids as inputs — it does not create a VPC for you. Most setups pair it with the companion terraform-aws-modules/vpc/aws module, or an existing VPC managed elsewhere. Read the module’s vpc_id and subnet_ids input docs on GitHub before writing the module block.
What is the difference between EKS managed node groups and EKS Auto Mode?
- Managed node groups (the eks_managed_node_groups input) give you EC2 Auto Scaling groups that AWS patches and drains for you, but you still choose instance types, min/max/desired size, and you still need a separate autoscaler for pod-driven scaling. EKS Auto Mode, enabled through the module’s compute_config input, hands AWS the compute autoscaling, pod and service networking, application load balancing and cluster DNS as managed core components instead of workloads you run yourself. See AWS’s EKS Auto Mode documentation for the full feature list.
Should I use access entries or the aws-auth ConfigMap?
- Access entries are the current AWS-native way to map IAM principals to Kubernetes RBAC, managed through the EKS API rather than a ConfigMap you edit by hand. The module’s authentication_mode input defaults to API_AND_CONFIG_MAP, which supports both during a migration. AWS’s access entries documentation covers migrating existing aws-auth entries, since they are not moved over automatically when you switch modes.
Does the module install an ingress controller or set up DNS and TLS for me?
- No. The module’s job ends at the cluster, its node groups or Auto Mode compute, and the EKS add-ons you explicitly list under addons (things like vpc-cni, kube-proxy and coredns). An ingress controller, external-dns for public DNS records and cert-manager (or another ACME client) for TLS certificates are separate pieces you install afterward — either by hand, through another Terraform module, or through a platform that manages that layer for you.
How do I upgrade the Kubernetes version afterward?
- Bump the kubernetes_version input and run terraform apply again — the module updates the EKS control plane. Managed node groups need their own rollout to pick up the new version, and each entry under addons carries its own version behaviour (most_recent defaults to true, or pin with addon_version). Check AWS’s Kubernetes version release notes before upgrading; each minor version carries its own deprecations and required actions.