Kubernetes on EKS with Terraform: From Zero to Production in One Click
Elastic Kubernetes Service (EKS) is AWS's managed Kubernetes offering, and it's genuinely powerful. It's also genuinely complex to set up with Terraform. By the time you've configured the cluster control plane, IAM roles for nodes and service accounts, managed node groups, the OIDC provider for IRSA (IAM Roles for Service Accounts), CoreDNS, kube-proxy, the VPC CNI add-on, and your kubeconfig — you've touched a dozen different AWS services and written several hundred lines of Terraform.
Most teams copy this from a previous project, stack overflow, or a Terraform module they half-understand. IaC Genius generates it correctly from scratch, validated and ready to deploy.
The EKS Setup Problem
Here's what a production EKS cluster actually requires. Each item is a potential misconfiguration:
Cluster IAM Role
The EKS cluster needs a service role with AmazonEKSClusterPolicy attached. Get the trust policy wrong and the cluster creation fails with a cryptic IAM error.
Node Group IAM Role
Worker nodes need their own IAM role — separate from the cluster role — with three managed policies:
AmazonEKSWorkerNodePolicyAmazonEC2ContainerRegistryReadOnlyAmazonEKS_CNI_Policy
Miss any one of these and nodes join the cluster but can't pull images or configure networking.
OIDC Provider for IRSA
Without an OIDC identity provider linked to the cluster, your pods can't use IAM roles. This means every pod that needs AWS access (S3, SQS, DynamoDB) either shares a node's EC2 instance profile (bad — over-privileged) or you skip IAM entirely (worse). The OIDC provider requires fetching the cluster's TLS certificate thumbprint and registering it — a step that's easy to skip and painful to add later.
Managed Add-ons
EKS clusters need coredns, kube-proxy, and vpc-cni add-ons installed at the right versions for your Kubernetes version. Version mismatches cause silent failures that only surface when workloads misbehave.
Node Group Sizing and Launch Templates
Node groups need instance types, scaling policies, disk sizes, and in production, a launch template for custom AMI configurations, security group overrides, or user data scripts.
Security Groups
The cluster security group, node security group, and your application security groups all need specific ingress/egress rules. The cluster API endpoint needs to be accessible from your CI/CD systems but not the open internet.
Writing all of this manually means spending a day reading EKS documentation and another day debugging why your nodes aren't Ready.
What IaC Genius Generates
The EKS template produces a complete, working cluster configuration. Here's a representative slice:
Cluster with Proper IAM
resource "aws_iam_role" "cluster" {
name = "${var.cluster_name}-cluster-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "eks.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "cluster_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.cluster.name
}
resource "aws_eks_cluster" "main" {
name = var.cluster_name
role_arn = aws_iam_role.cluster.arn
version = var.kubernetes_version
vpc_config {
subnet_ids = var.private_subnet_ids
endpoint_private_access = true
endpoint_public_access = var.enable_public_endpoint
public_access_cidrs = var.public_access_cidrs
}
depends_on = [aws_iam_role_policy_attachment.cluster_policy]
}
OIDC Provider for IRSA
data "tls_certificate" "cluster" {
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
resource "aws_iam_openid_connect_provider" "cluster" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [data.tls_certificate.cluster.certificates[0].sha1_fingerprint]
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
This is the piece most people forget. With the OIDC provider in place, you can create pod-level IAM roles that follow least-privilege — each service gets only the AWS permissions it actually needs.
Managed Node Groups
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "${var.cluster_name}-${var.node_group_name}"
node_role_arn = aws_iam_role.nodes.arn
subnet_ids = var.private_subnet_ids
instance_types = var.instance_types
disk_size = var.node_disk_size
scaling_config {
desired_size = var.node_desired_size
max_size = var.node_max_size
min_size = var.node_min_size
}
update_config {
max_unavailable = 1
}
depends_on = [
aws_iam_role_policy_attachment.node_worker_policy,
aws_iam_role_policy_attachment.node_cni_policy,
aws_iam_role_policy_attachment.node_ecr_policy,
]
}
The depends_on block is critical here. Without explicit dependencies, Terraform may try to create nodes before their IAM role policies are attached — causing a race condition that fails intermittently and is very hard to reproduce.
EKS Add-ons at Correct Versions
resource "aws_eks_addon" "coredns" {
cluster_name = aws_eks_cluster.main.name
addon_name = "coredns"
addon_version = var.coredns_version
depends_on = [aws_eks_node_group.main]
}
Add-ons depend on node groups being ready first — another ordering dependency that IaC Genius handles automatically.
Security Best Practices Built In
- Private API endpoint by default: The cluster API server is accessible within the VPC only. Public access is an explicit opt-in with a CIDR allowlist.
- Nodes in private subnets: Worker nodes run in private subnets with no direct internet exposure. Outbound traffic goes through NAT.
- IRSA ready: The OIDC provider is configured so workloads can use pod-level IAM roles from day one.
- IMDSv2 required: The launch template enforces IMDSv2 (Instance Metadata Service v2), which prevents SSRF attacks from being able to steal instance credentials.
- Envelope encryption for secrets: The cluster is configured to encrypt Kubernetes secrets at rest using a KMS key.
Customization Options
- Kubernetes version: Choose from the latest AWS-supported versions
- Node instance types: Single type or mixed instance policies for Spot cost optimization
- Node group scaling: Min, desired, and max sizes
- Public endpoint: Enabled with CIDR allowlist or fully private
- Multi-node-group: Separate node groups for system workloads and application workloads
- Managed add-on versions: Pinned to versions compatible with your Kubernetes version
What You Get
When you generate the EKS template on IaC Genius, you receive:
- A complete Terraform configuration that
terraform validatepasses cleanly - IAM roles and policies with correct trust relationships
- OIDC provider for pod-level IAM
- Managed node groups with proper dependencies
- EKS add-ons at correct versions
- Security group rules that follow least privilege
- Outputs for cluster name, endpoint, and kubeconfig generation
The cluster is production-ready on first deploy — no post-deployment manual steps, no "just add this IAM policy manually" footnotes.
Try the EKS template for free at app.iacgenius.com. Stop wrestling with Kubernetes IAM configuration and start deploying workloads.