eks-patterns
EKS cluster patterns and best practices for Terraform. Provides cluster, node group, add-on, and IRSA scaffolds. Use when developing EKS infrastructure.
When & Why to Use This Skill
This Claude skill provides production-ready Terraform patterns and best practices for Amazon EKS (Elastic Kubernetes Service). It offers comprehensive scaffolds for cluster configuration, diverse node group strategies (Spot, GPU, Graviton), Fargate profiles, and secure IAM Roles for Service Accounts (IRSA), enabling developers to deploy scalable and secure Kubernetes infrastructure following AWS industry standards.
Use Cases
- Provisioning a production-grade EKS cluster using the latest terraform-aws-eks modules with integrated logging and encryption.
- Configuring cost-optimized compute environments by mixing On-Demand and Spot instance node groups with appropriate taints and labels.
- Implementing high-performance workloads using specialized GPU-accelerated or ARM-based Graviton node groups.
- Setting up fine-grained security and identity management through IRSA for system add-ons like VPC CNI and EBS CSI drivers.
- Deploying advanced autoscaling capabilities with Karpenter and managing cluster access permissions via the EKS Access Entry API.
| name | eks-patterns |
|---|---|
| description | EKS cluster patterns and best practices for Terraform. Provides cluster, node group, add-on, and IRSA scaffolds. Use when developing EKS infrastructure. |
EKS Patterns
Terraform patterns for Amazon EKS infrastructure development.
Before Generating Code
ALWAYS use doc-researcher or Terraform MCP to verify:
- Current terraform-aws-eks module version
- EKS add-on versions
- Kubernetes version compatibility
Primary Module Reference
Use terraform-aws-modules/eks/aws (v20+):
- Registry: https://registry.terraform.io/modules/terraform-aws-modules/eks/aws
- GitHub: https://github.com/terraform-aws-modules/terraform-aws-eks
Naming Convention
Use {project}-{environment}-eks pattern:
locals {
cluster_name = "${var.project}-${var.environment}-eks"
}
Complete EKS Cluster
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = local.cluster_name
cluster_version = "1.31"
# Networking
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
# Access
cluster_endpoint_public_access = true
cluster_endpoint_private_access = true
enable_cluster_creator_admin_permissions = true
# Logging
cluster_enabled_log_types = ["api", "audit", "authenticator", "controllerManager", "scheduler"]
# Encryption
cluster_encryption_config = {
provider_key_arn = aws_kms_key.eks.arn
resources = ["secrets"]
}
# Managed Node Groups
eks_managed_node_groups = {
general = {
name = "${local.cluster_name}-general"
instance_types = ["m6i.large", "m5.large"]
capacity_type = "ON_DEMAND"
min_size = 2
max_size = 10
desired_size = 3
disk_size = 100
disk_type = "gp3"
labels = {
role = "general"
"node.kubernetes.io/capacity-type" = "on-demand"
}
update_config = {
max_unavailable_percentage = 33
}
}
}
# Add-ons
cluster_addons = {
coredns = {
most_recent = true
configuration_values = jsonencode({
replicaCount = 2
})
}
kube-proxy = {
most_recent = true
}
vpc-cni = {
most_recent = true
before_compute = true
service_account_role_arn = module.vpc_cni_irsa.iam_role_arn
configuration_values = jsonencode({
env = {
ENABLE_PREFIX_DELEGATION = "true"
WARM_PREFIX_TARGET = "1"
}
})
}
aws-ebs-csi-driver = {
most_recent = true
service_account_role_arn = module.ebs_csi_irsa.iam_role_arn
}
}
tags = var.tags
}
Node Group Patterns
On-Demand with Multiple Instance Types
eks_managed_node_groups = {
on_demand = {
name = "${local.cluster_name}-on-demand"
instance_types = ["m6i.large", "m5.large", "m5a.large"]
capacity_type = "ON_DEMAND"
min_size = 2
max_size = 10
desired_size = 3
disk_size = 100
disk_type = "gp3"
labels = {
role = "general"
"node.kubernetes.io/capacity-type" = "on-demand"
}
update_config = {
max_unavailable_percentage = 33
}
}
}
Spot Instances
eks_managed_node_groups = {
spot = {
name = "${local.cluster_name}-spot"
instance_types = ["m6i.large", "m5.large", "m5a.large", "m5n.large"]
capacity_type = "SPOT"
min_size = 0
max_size = 20
desired_size = 3
labels = {
role = "spot"
"node.kubernetes.io/capacity-type" = "spot"
}
taints = [{
key = "spot"
value = "true"
effect = "NO_SCHEDULE"
}]
}
}
GPU Node Group
eks_managed_node_groups = {
gpu = {
name = "${local.cluster_name}-gpu"
instance_types = ["g4dn.xlarge", "g4dn.2xlarge"]
capacity_type = "ON_DEMAND"
min_size = 0
max_size = 5
desired_size = 0
ami_type = "AL2_x86_64_GPU"
disk_size = 200
labels = {
"nvidia.com/gpu" = "true"
"node.kubernetes.io/capacity-type" = "on-demand"
}
taints = [{
key = "nvidia.com/gpu"
value = "true"
effect = "NO_SCHEDULE"
}]
}
}
ARM/Graviton Instances
eks_managed_node_groups = {
graviton = {
name = "${local.cluster_name}-graviton"
instance_types = ["m6g.large", "m6g.xlarge"]
capacity_type = "ON_DEMAND"
min_size = 2
max_size = 10
desired_size = 3
ami_type = "AL2_ARM_64"
labels = {
"kubernetes.io/arch" = "arm64"
"node.kubernetes.io/capacity-type" = "on-demand"
}
}
}
Fargate Profile Pattern
fargate_profiles = {
kube_system = {
name = "${local.cluster_name}-kube-system"
selectors = [
{
namespace = "kube-system"
labels = {
k8s-app = "kube-dns"
}
}
]
}
serverless = {
name = "${local.cluster_name}-serverless"
selectors = [
{ namespace = "serverless" },
{
namespace = "batch"
labels = { compute = "fargate" }
}
]
}
}
IRSA Patterns
VPC CNI IRSA
module "vpc_cni_irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.0"
role_name = "${local.cluster_name}-vpc-cni"
attach_vpc_cni_policy = true
vpc_cni_enable_ipv4 = true
oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["kube-system:aws-node"]
}
}
tags = var.tags
}
EBS CSI IRSA
module "ebs_csi_irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.0"
role_name = "${local.cluster_name}-ebs-csi"
attach_ebs_csi_policy = true
oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"]
}
}
tags = var.tags
}
Custom Application IRSA
module "app_irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.0"
role_name = "${local.cluster_name}-app"
oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["app-namespace:app-service-account"]
}
}
role_policy_arns = {
s3_read = aws_iam_policy.s3_read.arn
sqs_send = aws_iam_policy.sqs_send.arn
}
tags = var.tags
}
Karpenter Pattern
module "karpenter" {
source = "terraform-aws-modules/eks/aws//modules/karpenter"
version = "~> 20.0"
cluster_name = module.eks.cluster_name
enable_v1_permissions = true
create_pod_identity_association = true
node_iam_role_use_name_prefix = false
node_iam_role_name = "${local.cluster_name}-karpenter-node"
node_iam_role_additional_policies = {
AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
tags = var.tags
}
# Karpenter Helm release (optional - can be done via GitOps)
resource "helm_release" "karpenter" {
namespace = "karpenter"
create_namespace = true
name = "karpenter"
repository = "oci://public.ecr.aws/karpenter"
chart = "karpenter"
version = "1.0.0"
values = [
yamlencode({
settings = {
clusterName = module.eks.cluster_name
clusterEndpoint = module.eks.cluster_endpoint
interruptionQueue = module.karpenter.queue_name
}
})
]
}
Access Entry Pattern (API Mode)
access_entries = {
admin_role = {
principal_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/AdminRole"
policy_associations = {
admin = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy"
access_scope = {
type = "cluster"
}
}
}
}
developer_role = {
principal_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/DeveloperRole"
policy_associations = {
edit = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy"
access_scope = {
type = "namespace"
namespaces = ["dev", "staging"]
}
}
}
}
readonly_role = {
principal_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/ReadOnlyRole"
policy_associations = {
view = {
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy"
access_scope = {
type = "cluster"
}
}
}
}
}
EKS KMS Key
resource "aws_kms_key" "eks" {
description = "KMS key for EKS cluster ${local.cluster_name}"
deletion_window_in_days = 7
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow EKS Service"
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
Resource = "*"
}
]
})
tags = merge(var.tags, { Name = "${local.cluster_name}-eks" })
}
resource "aws_kms_alias" "eks" {
name = "alias/${local.cluster_name}-eks"
target_key_id = aws_kms_key.eks.key_id
}
Common Outputs
output "cluster_name" {
description = "EKS cluster name"
value = module.eks.cluster_name
}
output "cluster_endpoint" {
description = "EKS cluster endpoint"
value = module.eks.cluster_endpoint
}
output "cluster_certificate_authority_data" {
description = "Base64 encoded cluster CA certificate"
value = module.eks.cluster_certificate_authority_data
}
output "oidc_provider_arn" {
description = "OIDC provider ARN for IRSA"
value = module.eks.oidc_provider_arn
}
output "cluster_security_group_id" {
description = "Cluster security group ID"
value = module.eks.cluster_security_group_id
}
output "node_security_group_id" {
description = "Node security group ID"
value = module.eks.node_security_group_id
}
kubectl Configuration
# Update kubeconfig
aws eks update-kubeconfig --name ${local.cluster_name} --region ${data.aws_region.current.name}
# Verify connection
kubectl get nodes
kubectl get pods -A