VOOZH about

URL: https://dev.to/jayakrishnayadav24/terraform-modular-eks-istio-part-2-abi

⇱ Terraform Modular EKS + Istio β€” Part 2 - DEV Community


IAM Module (IRSA, OIDC, and Why This Controls Everything)

In the previous part, we built the VPC.

Now we move to something that causes the most confusion in EKS setups:

πŸ‘‰ IAM

This is not just β€œpermissions”.

This module controls:

  • how EKS works
  • how nodes behave
  • how pods access AWS services

If this is wrong:

  • ALB won’t work
  • CSI drivers fail
  • Pods can’t access AWS
  • Debugging becomes painful

πŸ“‚ Module Files

modules/iam/
β”œβ”€β”€ main.tf
β”œβ”€β”€ variables.tf
└── outputs.tf

πŸ“„ variables.tf

variable "cluster_name" {
 description = "Name of the EKS cluster"
 type = string
}

variable "oidc_provider_arn" {
 description = "ARN of the OIDC provider"
 type = string
}

variable "oidc_provider" {
 description = "OIDC provider URL"
 type = string
}

🧠 What these inputs mean

  • cluster_name
    β†’ used to name roles

  • oidc_provider_arn
    β†’ comes from EKS module

  • oidc_provider
    β†’ used for IRSA condition matching

πŸ‘‰ Important:

This module depends on EKS
Because OIDC is created inside the EKS module.


πŸ“„ main.tf (Core IAM Logic)


1. EKS Cluster Role

resource "aws_iam_role" "eks_cluster" {
 name = "${var.cluster_name}-cluster-role"

 assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
 {
 Action = "sts:AssumeRole"
 Effect = "Allow"
 Principal = {
 Service = "eks.amazonaws.com"
 }
 }
 ]
 })
}

🧠 What this actually does

This role is used by:

πŸ‘‰ EKS Control Plane (managed by AWS)


Key line

Service = "eks.amazonaws.com"

πŸ‘‰ Means:

β€œEKS service is allowed to assume this role”


Attach Policy

resource "aws_iam_role_policy_attachment" "eks_cluster_policy" {
 policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
 role = aws_iam_role.eks_cluster.name
}

Why this policy?

This allows EKS to:

  • manage nodes
  • communicate with AWS
  • create resources

2. Node Group Role

resource "aws_iam_role" "eks_nodes" {
 name = "${var.cluster_name}-node-group-role"

 assume_role_policy = jsonencode({
 Version = "2012-10-17"
 Statement = [
 {
 Action = "sts:AssumeRole"
 Effect = "Allow"
 Principal = {
 Service = "ec2.amazonaws.com"
 }
 }
 ]
 })
}

🧠 What this role is for

πŸ‘‰ Used by EC2 instances (worker nodes)


Key line

Service = "ec2.amazonaws.com"

πŸ‘‰ Means:

EC2 instances can assume this role


3. Node Policies

Now we attach multiple policies.


a. Worker Node Policy

resource "aws_iam_role_policy_attachment" "eks_worker_node_policy" {
 policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}

πŸ‘‰ Allows nodes to:

  • join cluster
  • communicate with control plane

b. CNI Policy

resource "aws_iam_role_policy_attachment" "eks_cni_policy" {
 policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
}

πŸ‘‰ This is very important

Allows:

  • Pod networking
  • ENI management

πŸ‘‰ Without this:
Pods won’t get IPs


c. ECR Access

resource "aws_iam_role_policy_attachment" "eks_container_registry_policy" {
 policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
}

πŸ‘‰ Allows nodes to:

  • pull Docker images

d. SSM Access

resource "aws_iam_role_policy_attachment" "eks_ssm_policy" {
 policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

πŸ‘‰ Allows:

  • SSM access
  • no need for SSH

πŸ‘‰ This is production best practice


4. EBS CSI Driver Role (πŸ”₯ Most Important Part)

resource "aws_iam_role" "ebs_csi_driver" {
 name = "${var.cluster_name}-ebs-csi-driver-role"

This is NOT for nodes.

This is for:

πŸ‘‰ Kubernetes Pod (EBS CSI controller)


πŸ”₯ This is IRSA (Core Concept)

Action = "sts:AssumeRoleWithWebIdentity"

πŸ‘‰ This is different from EC2 roles

This allows:

πŸ‘‰ Pods β†’ assume IAM role


πŸ”₯ OIDC Trust

Principal = {
 Federated = var.oidc_provider_arn
}

πŸ‘‰ This links:

  • EKS cluster
  • IAM

πŸ”₯ Condition (VERY IMPORTANT)

"${var.oidc_provider}:sub" = "system:serviceaccount:kube-system:ebs-csi-controller-sa"

πŸ‘‰ This means:

ONLY this service account can assume the role:

kube-system / ebs-csi-controller-sa

Why this matters

πŸ‘‰ This is fine-grained security

Instead of:

❌ giving full access to nodes

You do:

βœ… give access only to specific pod


Attach Policy

resource "aws_iam_role_policy_attachment" "ebs_csi_policy" {
 policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
}

What this enables

  • Create volumes
  • Attach volumes
  • Manage storage

πŸ“„ outputs.tf

output "eks_cluster_role_arn" {
 value = aws_iam_role.eks_cluster.arn
}

output "eks_nodes_role_arn" {
 value = aws_iam_role.eks_nodes.arn
}

output "ebs_csi_driver_role_arn" {
 value = aws_iam_role.ebs_csi_driver.arn
}

🧠 Why outputs matter

These are used in:

  • EKS module
  • Node module
  • CSI module

Example:

cluster_role_arn = module.iam.eks_cluster_role_arn

πŸ‘‰ This creates dependency automatically.


πŸ”₯ Real Architecture (What You Built)

EKS Control Plane β†’ uses cluster role

EC2 Nodes β†’ use node role

Pods (CSI) β†’ use IRSA role (OIDC)

⚠️ Real Mistakes People Make

  • Giving full IAM to nodes (bad security)
  • Not using IRSA
  • Wrong OIDC condition β†’ role not assumed
  • Forgetting CNI policy β†’ pods fail

🧠 Key Takeaways

  • IAM is not optional β€” it defines system behavior
  • Nodes and pods should have separate roles
  • IRSA is the correct way to give AWS access to pods
  • OIDC is what connects Kubernetes to IAM

πŸš€ Next

In Part 3:

πŸ‘‰ EKS Cluster Module
πŸ‘‰ How control plane is created
πŸ‘‰ What OIDC actually does internally


If you understand this module, you understand how AWS + Kubernetes actually connect behind the scenes.