EKS Auto Mode with Terraform

Amazon EKS Auto Mode fully automates Kubernetes cluster management for compute, storage, and networking on AWS. It comes with Karpenter pre-installed and a default NodePool that provisions AMD-based servers on demand.
Why opt for EKS Auto Mode?
EKS clusters require many add-ons and features to be installed for full functionality, and keeping versions compatible across components is challenging. With EKS Auto Mode, all of these are managed by AWS.
| Components managed by AWS in EKS Auto Mode | |
|---|---|
| VPC CNI | Load Balancer controller |
| EKS Pod Identity | Kube Proxy |
| Ingress controller (recommend using Gateway API instead) | Karpenter autoscaler with default NodePool |
EKS Auto Cluster build
With EKS Auto Mode, creating a cluster has minimal requirements. Terraform code for this example is at aws-eks-terraform → EKS-Cluster-auto-mode.
- VPC with at least 2 subnets and the required subnet tags
- EKS Auto Mode cluster with API authentication and access entry
Step 1. Create VPC
The VPC must have at least 2 subnets with the following tags so load balancers are created in the correct subnet:
public_subnet_tags = {
"kubernetes.io/role/elb" = "1" # Tag for external load balancers
}
private_subnet_tags = {
"kubernetes.io/role/internal-elb" = "1" # Tag for internal load balancers
}
Step 2. Provision the EKS Auto Mode cluster
IAM Roles
Two IAM roles are required before creating the cluster:
- Node role — with
AmazonEKSWorkerNodeMinimalPolicyandAmazonEC2ContainerRegistryPullOnly - Cluster role — with
AmazonEKSClusterPolicy,AmazonEKSComputePolicy,AmazonEKSBlockStoragePolicy,AmazonEKSLoadBalancingPolicy, andAmazonEKSNetworkingPolicy
iam.tf
resource "aws_iam_role" "node" {
name = "eks-auto-node-example"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = ["sts:AssumeRole"]
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
},
]
})
}
resource "aws_iam_role_policy_attachment" "node_AmazonEKSWorkerNodeMinimalPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodeMinimalPolicy"
role = aws_iam_role.node.name
}
resource "aws_iam_role_policy_attachment" "node_AmazonEC2ContainerRegistryPullOnly" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly"
role = aws_iam_role.node.name
}
resource "aws_iam_role" "cluster" {
name = "eks-cluster-eks-auto-demo"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = ["sts:AssumeRole", "sts:TagSession"]
Effect = "Allow"
Principal = { Service = "eks.amazonaws.com" }
},
]
})
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSClusterPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.cluster.name
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSComputePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSComputePolicy"
role = aws_iam_role.cluster.name
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSBlockStoragePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSBlockStoragePolicy"
role = aws_iam_role.cluster.name
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSLoadBalancingPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSLoadBalancingPolicy"
role = aws_iam_role.cluster.name
}
resource "aws_iam_role_policy_attachment" "cluster_AmazonEKSNetworkingPolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSNetworkingPolicy"
role = aws_iam_role.cluster.name
}
EKS Cluster
When creating an EKS Auto Mode cluster, compute_config, kubernetes_network_config (elastic load balancing), and storage_config must all be enabled. This instructs AWS to configure Karpenter, VPC-CNI, the LB controller, and the EBS CSI driver.
eks-auto.tf
resource "aws_eks_cluster" "eks-auto-demo" {
name = "eks-auto-demo"
access_config {
authentication_mode = "API"
}
role_arn = aws_iam_role.cluster.arn
version = "1.31"
bootstrap_self_managed_addons = false
compute_config {
enabled = true
node_pools = ["general-purpose"]
node_role_arn = aws_iam_role.node.arn
}
kubernetes_network_config {
elastic_load_balancing {
enabled = true
}
}
storage_config {
block_storage {
enabled = true
}
}
vpc_config {
endpoint_private_access = true
endpoint_public_access = true
subnet_ids = module.vpc.private_subnets
}
depends_on = [
aws_iam_role_policy_attachment.cluster_AmazonEKSClusterPolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSComputePolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSBlockStoragePolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSLoadBalancingPolicy,
aws_iam_role_policy_attachment.cluster_AmazonEKSNetworkingPolicy,
]
}
EKS Access entry
By default, the creator of an EKS cluster does not have Cluster Admin permissions. You must add users to aws_eks_access_entry and assign them appropriate permissions. The example below grants ClusterAdmin to the identity running Terraform.
access_entry.tf
data "aws_caller_identity" "current" {}
resource "aws_eks_access_entry" "aws_eks_access_entry" {
cluster_name = aws_eks_cluster.eks-auto-demo.name
principal_arn = data.aws_caller_identity.current.arn
type = "STANDARD"
}
resource "aws_eks_access_policy_association" "aws_eks_access_policy_association" {
cluster_name = aws_eks_cluster.eks-auto-demo.name
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
principal_arn = data.aws_caller_identity.current.arn
access_scope {
type = "cluster"
}
}
Apply Terraform
Connect to the EKS cluster and validate
aws eks --profile labs --region eu-west-1 update-kubeconfig --name eks-auto-demo
kubectl cluster-info
kubectl get nodepool,ingressclass,nodes
By default there will be a NodePool but no nodes will be created until an application is deployed. The ingress controller is installed; you must create an IngressClass before using it.
Deploy a sample app and you will see Karpenter spin up a new node as needed.
Why you should avoid the EKS default Ingress controller
EKS Auto Mode includes an ingress controller, but it is better to use a Gateway API implementation such as Envoy Gateway. When you create an Ingress resource, the AWS Ingress controller creates Target Groups and forwards traffic directly to your pods. However, the Target Group health checks are traditional and slower than Kubernetes-native checks, which can cause connection drops during deployments, scale-up, and scale-down events as the Target Group takes longer to detect pod changes.