Module: eks-cluster
Terraform module to provision an EKS cluster on AWS.
This Terraform module provisions a fully configured AWS EKS (Elastic Kubernetes Service) cluster. It's engineered to integrate smoothly with Karpenter and EKS addons, forming a critical part of Cloud Posse's reference architecture. Ideal for teams looking to deploy scalable and manageable Kubernetes clusters on AWS with minimal fuss.
Introduction
The module provisions the following resources:
- EKS cluster of master nodes that can be used together with the
terraform-aws-eks-node-group and
terraform-aws-eks-fargate-profile
modules to create a full-blown EKS/Kubernetes cluster. You can also use the terraform-aws-eks-workers
module to provision worker nodes for the cluster, but it is now rare for that to be a better choice than to use
terraform-aws-eks-node-group
. - IAM Role to allow the cluster to access other AWS services
- EKS access entries to allow IAM users to access and administer the cluster
Usage
For a complete example, see examples/complete.
For automated tests of the complete example using bats and Terratest (which tests and deploys the example on AWS), see test/src.
Other examples:
- terraform-aws-components/eks/cluster - Cloud Posse's service catalog of "root module" invocations for provisioning reference architectures
provider "aws" {
region = var.region
}
# Note: This example creates an explicit access entry for the current user,
# but in practice, you should use a static map of IAM users or roles that should have access to the cluster.
# Granting access to the current user in this way is not recommended for production use.
data "aws_caller_identity" "current" {}
# IAM session context converts an assumed role ARN into an IAM Role ARN.
# Again, this is primarily to simplify the example, and in practice, you should use a static map of IAM users or roles.
data "aws_iam_session_context" "current" {
arn = data.aws_caller_identity.current.arn
}
locals {
# The usage of the specific kubernetes.io/cluster/* resource tags below are required
# for EKS and Kubernetes to discover and manage networking resources
# https://aws.amazon.com/premiumsupport/knowledge-center/eks-vpc-subnet-discovery/
# https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/docs/deploy/subnet_discovery.md
tags = { "kubernetes.io/cluster/${module.label.id}" = "shared" }
# required tags to make ALB ingress work https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html
public_subnets_additional_tags = {
"kubernetes.io/role/elb" : 1
}
private_subnets_additional_tags = {
"kubernetes.io/role/internal-elb" : 1
}
# Enable the IAM user creating the cluster to administer it,
# without using the bootstrap_cluster_creator_admin_permissions option,
# as an example of how to use the access_entry_map feature.
# In practice, this should be replaced with a static map of IAM users or roles
# that should have access to the cluster, but we use the current user
# to simplify the example.
access_entry_map = {
(data.aws_iam_session_context.current.issuer_arn) = {
access_policy_associations = {
ClusterAdmin = {}
}
}
}
}
module "label" {
source = "cloudposse/label/null"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
namespace = var.namespace
name = var.name
stage = var.stage
delimiter = var.delimiter
tags = var.tags
}
module "vpc" {
source = "cloudposse/vpc/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
ipv4_primary_cidr_block = "172.16.0.0/16"
tags = local.tags
context = module.label.context
}
module "subnets" {
source = "cloudposse/dynamic-subnets/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
availability_zones = var.availability_zones
vpc_id = module.vpc.vpc_id
igw_id = [module.vpc.igw_id]
ipv4_cidr_block = [module.vpc.vpc_cidr_block]
nat_gateway_enabled = true
nat_instance_enabled = false
public_subnets_additional_tags = local.public_subnets_additional_tags
private_subnets_additional_tags = local.private_subnets_additional_tags
tags = local.tags
context = module.label.context
}
module "eks_node_group" {
source = "cloudposse/eks-node-group/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
instance_types = [var.instance_type]
subnet_ids = module.subnets.private_subnet_ids
health_check_type = var.health_check_type
min_size = var.min_size
max_size = var.max_size
cluster_name = module.eks_cluster.eks_cluster_id
# Enable the Kubernetes cluster auto-scaler to find the auto-scaling group
cluster_autoscaler_enabled = var.autoscaling_policies_enabled
context = module.label.context
}
module "eks_cluster" {
source = "cloudposse/eks-cluster/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
subnet_ids = concat(module.subnets.private_subnet_ids, module.subnets.public_subnet_ids)
kubernetes_version = var.kubernetes_version
oidc_provider_enabled = true
addons = [
# https://docs.aws.amazon.com/eks/latest/userguide/managing-vpc-cni.html#vpc-cni-latest-available-version
{
addon_name = "vpc-cni"
addon_version = var.vpc_cni_version
resolve_conflicts_on_create = "OVERWRITE"
resolve_conflicts_on_update = "OVERWRITE"
service_account_role_arn = var.vpc_cni_service_account_role_arn # Creating this role is outside the scope of this example
},
# https://docs.aws.amazon.com/eks/latest/userguide/managing-kube-proxy.html
{
addon_name = "kube-proxy"
addon_version = var.kube_proxy_version
resolve_conflicts_on_create = "OVERWRITE"
resolve_conflicts_on_update = "OVERWRITE"
service_account_role_arn = null
},
# https://docs.aws.amazon.com/eks/latest/userguide/managing-coredns.html
{
addon_name = "coredns"
addon_version = var.coredns_version
resolve_conflicts_on_create = "OVERWRITE"
resolve_conflicts_on_update = "OVERWRITE"
service_account_role_arn = null
},
]
addons_depends_on = [module.eks_node_group]
context = module.label.context
cluster_depends_on = [module.subnets]
}
Module usage with two unmanaged worker groups:
locals {
# Unfortunately, the `aws_ami` data source attribute `most_recent` (https://github.com/cloudposse/terraform-aws-eks-workers/blob/34a43c25624a6efb3ba5d2770a601d7cb3c0d391/main.tf#L141)
# does not work as you might expect. If you are not going to use a custom AMI you should
# use the `eks_worker_ami_name_filter` variable to set the right kubernetes version for EKS workers,
# otherwise the first version of Kubernetes supported by AWS (v1.11) for EKS workers will be selected, but the
# EKS control plane will ignore it to use one that matches the version specified by the `kubernetes_version` variable.
eks_worker_ami_name_filter = "amazon-eks-node-${var.kubernetes_version}*"
}
module "eks_workers" {
source = "cloudposse/eks-workers/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
attributes = ["small"]
instance_type = "t3.small"
eks_worker_ami_name_filter = local.eks_worker_ami_name_filter
vpc_id = module.vpc.vpc_id
subnet_ids = module.subnets.public_subnet_ids
health_check_type = var.health_check_type
min_size = var.min_size
max_size = var.max_size
wait_for_capacity_timeout = var.wait_for_capacity_timeout
cluster_name = module.label.id
cluster_endpoint = module.eks_cluster.eks_cluster_endpoint
cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data
cluster_security_group_id = module.eks_cluster.eks_cluster_managed_security_group_id
# Auto-scaling policies and CloudWatch metric alarms
autoscaling_policies_enabled = var.autoscaling_policies_enabled
cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent
cpu_utilization_low_threshold_percent = var.cpu_utilization_low_threshold_percent
context = module.label.context
}
module "eks_workers_2" {
source = "cloudposse/eks-workers/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
attributes = ["medium"]
instance_type = "t3.medium"
eks_worker_ami_name_filter = local.eks_worker_ami_name_filter
vpc_id = module.vpc.vpc_id
subnet_ids = module.subnets.public_subnet_ids
health_check_type = var.health_check_type
min_size = var.min_size
max_size = var.max_size
wait_for_capacity_timeout = var.wait_for_capacity_timeout
cluster_name = module.label.id
cluster_endpoint = module.eks_cluster.eks_cluster_endpoint
cluster_certificate_authority_data = module.eks_cluster.eks_cluster_certificate_authority_data
cluster_security_group_id = module.eks_cluster.eks_cluster_managed_security_group_id
# Auto-scaling policies and CloudWatch metric alarms
autoscaling_policies_enabled = var.autoscaling_policies_enabled
cpu_utilization_high_threshold_percent = var.cpu_utilization_high_threshold_percent
cpu_utilization_low_threshold_percent = var.cpu_utilization_low_threshold_percent
context = module.label.context
}
module "eks_cluster" {
source = "cloudposse/eks-cluster/aws"
# Cloud Posse recommends pinning every module to a specific version
# version = "x.x.x"
subnet_ids = concat(module.subnets.private_subnet_ids, module.subnets.public_subnet_ids)
kubernetes_version = var.kubernetes_version
oidc_provider_enabled = true # needed for VPC CNI
access_entries_for_nodes = {
EC2_LINUX = [module.eks_workers.workers_role_arn, module.eks_workers_2.workers_role_arn]
}
context = module.label.context
}
Release 4.0.0
contains major breaking changes that will require you to update your existing EKS cluster
and configuration to use this module. Please see the v3 to v4 migration path for more information.
Release 2.0.0
(previously released as version 0.45.0
) contains some changes that,
if applied to a cluster created with an earlier version of this module,
could result in your existing EKS cluster being replaced (destroyed and recreated).
To prevent this, follow the instructions in the v1 to v2 migration path.
Prior to v4 of this module, AWS did not provide an API to manage access to the EKS cluster, causing numerous challenges. With v4 of this module, it exclusively uses the AWS API, resolving many issues you may read about that had affected prior versions. See the version 2 README and release notes for more information on the challenges and workarounds that were required prior to v3.