TerraformAWS
AWS EKS Cluster
Managed Kubernetes cluster with managed node groups, OIDC provider for IRSA, and CoreDNS.
kubernetesekscontainersiam
Prerequisites
- •
VPC with private subnets (use aws-vpc template) - •
Terraform >= 1.5.0 - •
AWS provider >= 5.0 - •
kubectl installed locally
Template Code
# ─────────────────────────────────────────────────────────────────────────────
# AWS EKS Cluster with Managed Node Groups
# ─────────────────────────────────────────────────────────────────────────────
variable "cluster_name" { default = "production-cluster" }
variable "cluster_version" { default = "1.29" }
variable "region" { default = "us-east-1" }
# Assumes VPC/subnets created separately (e.g. via aws-vpc template)
variable "private_subnet_ids" {
type = list(string)
}
# ── IAM Role for EKS Control Plane ──────────────────────────────────────────
resource "aws_iam_role" "eks_cluster" {
name = "${var.cluster_name}-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" "eks_cluster_policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.eks_cluster.name
}
# ── EKS Cluster ───────────────────────────────────────────────────────────────
resource "aws_eks_cluster" "main" {
name = var.cluster_name
version = var.cluster_version
role_arn = aws_iam_role.eks_cluster.arn
vpc_config {
subnet_ids = var.private_subnet_ids
endpoint_private_access = true
endpoint_public_access = true # Set to false for private-only clusters
}
# Enable logging
enabled_cluster_log_types = ["api", "audit", "authenticator"]
depends_on = [aws_iam_role_policy_attachment.eks_cluster_policy]
}
# ── OIDC Provider (enables IRSA — IAM Roles for Service Accounts) ─────────────
data "tls_certificate" "eks" {
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
resource "aws_iam_openid_connect_provider" "eks" {
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = [data.tls_certificate.eks.certificates[0].sha1_fingerprint]
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}
# ── IAM Role for Node Group ───────────────────────────────────────────────────
resource "aws_iam_role" "node_group" {
name = "${var.cluster_name}-node-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "node_group_policies" {
for_each = toset([
"arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
"arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy",
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
])
policy_arn = each.value
role = aws_iam_role.node_group.name
}
# ── Managed Node Group ────────────────────────────────────────────────────────
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "main"
node_role_arn = aws_iam_role.node_group.arn
subnet_ids = var.private_subnet_ids
instance_types = ["t3.medium"]
scaling_config {
desired_size = 2
min_size = 1
max_size = 5
}
update_config {
max_unavailable = 1
}
depends_on = [aws_iam_role_policy_attachment.node_group_policies]
}
# ── Outputs ───────────────────────────────────────────────────────────────────
output "cluster_name" { value = aws_eks_cluster.main.name }
output "cluster_endpoint" { value = aws_eks_cluster.main.endpoint }
output "oidc_issuer_url" { value = aws_eks_cluster.main.identity[0].oidc[0].issuer }
Usage
terraform init terraform apply -var="cluster_version=1.29" # Update kubeconfig: aws eks update-kubeconfig --name production-cluster --region us-east-1