EKS cluster, node groups, OIDC provider, addons
Provisions the EKS cluster, managed node groups, OIDC identity provider, and core Kubernetes addons. This module is the runtime environment where all application workloads run.
EKS Cluster (Kubernetes 1.29)
├── Managed Node Group "applications"
│ ├── EC2 instances in private subnets
│ ├── Auto Scaling Group (min / desired / max)
│ └── IAM node role (ECR pull, SSM, CloudWatch)
├── OIDC Identity Provider
│ └── Enables IRSA — pods assume IAM roles without static keys
└── Cluster Addons
├── coredns — DNS resolution inside the cluster
├── kube-proxy — network rules on each node
└── vpc-cni — gives each pod a real VPC IP address
The OIDC provider is the most important thing this module creates beyond the cluster itself. It registers the cluster as a trusted identity provider with AWS IAM, which enables IRSA (IAM Roles for Service Accounts).
Without IRSA, giving a pod access to AWS services (S3, Secrets Manager, etc.) requires embedding static access keys — a security risk. With IRSA, a pod's Kubernetes service account is mapped to an IAM role, and AWS issues short-lived credentials automatically. No keys stored anywhere.
The ALB Ingress Controller uses IRSA. Any future service that needs AWS access (read from S3, send to SQS, etc.) should use IRSA too.
module "eks" {
source = "../../modules/eks"
cluster_name = "aetherion-dev"
environment = "dev"
vpc_id = module.vpc.vpc_id
private_subnet_ids = module.vpc.private_subnet_ids
node_instance_type = "t3.medium"
node_desired_size = 2
node_min_size = 1
node_max_size = 5
}| Name | Type | Default | Description |
|---|---|---|---|
cluster_name |
string |
— | Name of the EKS cluster |
environment |
string |
— | dev or prod, applied as a node label and tag |
vpc_id |
string |
— | VPC to deploy the cluster into — from module.vpc.vpc_id |
private_subnet_ids |
list(string) |
— | Subnets for nodes — from module.vpc.private_subnet_ids |
cluster_version |
string |
"1.29" |
Kubernetes version. Changing this triggers a cluster upgrade |
node_instance_type |
string |
"t3.medium" |
EC2 instance type for worker nodes |
node_desired_size |
number |
2 |
Initial number of nodes |
node_min_size |
number |
1 |
Minimum nodes (ASG lower bound) |
node_max_size |
number |
5 |
Maximum nodes (ASG upper bound) |
| Name | Description | Consumed by |
|---|---|---|
cluster_name |
Cluster identifier | environments/*/outputs.tf, GHA workflow |
cluster_endpoint |
API server URL | kubectl, GHA workflow |
oidc_issuer_url |
Bare OIDC URL (no https://) |
modules/iam trust policies |
oidc_provider_arn |
Full ARN of the OIDC provider | modules/iam trust policies |
node_role_arn |
IAM role ARN attached to nodes | modules/iam if additional node policies needed |
Addons are AWS-managed components that run inside the cluster. Using managed addons means AWS handles version compatibility and patching.
| Addon | Purpose | Why before_compute = true on vpc-cni |
|---|---|---|
vpc-cni |
Assigns VPC IPs to pods | Must exist before nodes join or they boot without pod networking |
coredns |
In-cluster DNS | Pods resolve service-name.namespace.svc.cluster.local |
kube-proxy |
iptables rules on nodes | Service-to-pod routing |
cluster_addons = {
vpc-cni = {
most_recent = true
before_compute = true
resolve_conflicts_on_update = "OVERWRITE"
}
coredns = {
most_recent = true
resolve_conflicts_on_update = "OVERWRITE"
}
kube-proxy = {
most_recent = true
resolve_conflicts_on_update = "OVERWRITE"
}
}| Setting | Dev | Prod |
|---|---|---|
node_instance_type |
t3.medium |
t3.xlarge or larger |
node_desired_size |
2 |
4+ |
node_max_size |
5 |
20+ |
cluster_endpoint_public_access |
true |
true with CIDR restrictions |
In prod, consider restricting cluster_endpoint_public_access_cidrs to your office/VPN IP ranges so the Kubernetes API is not reachable from the public internet.
Once terraform apply completes, run this once to configure your local kubectl:
aws eks update-kubeconfig \
--name aetherion-dev \
--region us-east-2
# verify
kubectl get nodes- Never run nodes in public subnets. Nodes in public subnets get public IPs, which exposes the kubelet port (10250) and node ports to the internet. Always use private subnets.
- Cluster upgrades are in-place — changing
cluster_versiontriggers a rolling upgrade. EKS upgrades the control plane first, then you re-apply to upgrade node groups. Test in dev before applying to prod. - The node group is named
applications— this matches thekarpenter.sh/nodepool: applicationsnode selector in your Helm values files.