🟢 Welcome to my World!
WORKSPACE
TIMELINE
root/welcome.hi

IRSA vs. EKS Pod Identity: A Side-by-Side Terraform Comparison for Granting AWS Permissions to Pods

IRSA vs. EKS Pod Identity: A Side-by-Side Terraform Comparison for Granting AWS Permissions to Pods


If you’re running workloads on Amazon EKS (Elastic Kubernetes Service), the managed Kubernetes platform on AWS (Amazon Web Services), you eventually have to answer a fundamental question: How do my pods securely access other AWS services? For years, the gold standard has been IRSA (IAM Roles for Service Accounts). IAM (Identity and Access Management) is AWS’s system for controlling who can do what across your cloud resources, and a Service Account is a Kubernetes identity assigned to a pod so it can authenticate with other systems. IRSA is secure, widely adopted, and strictly adheres to the principle of least privilege—the security practice of granting only the minimum permissions a workload needs to do its job. However, configuring IRSA across multiple clusters can feel like a chore.

Enter EKS Pod Identity—AWS’s newer, more streamlined approach that promises to simplify the way we grant AWS permissions to Kubernetes pods.

In this post, we’ll explore both methods side-by-side using actual Terraform code from a live project that uniquely uses both approaches. By the end, you’ll understand the architectural differences, the developer experience of each, and how to decide which one is right for your use case.

The Core Problem: How Do Pods Get AWS Permissions?

Before we look at any code, let’s understand the problem both IRSA and Pod Identity are solving.

Your Kubernetes pods frequently need to talk to AWS services—pulling container images from ECR (Elastic Container Registry), reading files from S3 (Simple Storage Service), managing load balancers, or fetching secrets from Secrets Manager. But how does a pod prove to AWS that it’s allowed to do these things?

The naive approach is to let every pod inherit the IAM role attached to the EC2 (Elastic Compute Cloud) worker node it runs on. An IAM role is a set of permissions that defines what actions are allowed on which AWS resources. This approach is like giving every employee in a company the CEO’s master key—sure, everyone can open every door, but it completely violates the principle of least privilege. A compromised pod on a node could access any AWS resource the node role permits.

Both IRSA and Pod Identity solve this by giving each pod (or more precisely, each Kubernetes Service Account) its own specific keycard—a scoped IAM role with only the permissions that specific workload needs. They just go about it in very different ways.

1. The Classic Approach: IAM Roles for Service Accounts (IRSA)

IRSA leverages OIDC (OpenID Connect), a standard protocol that lets one service verify a user’s or workload’s identity through another trusted service, to establish a trust relationship between your EKS cluster and AWS IAM. With IRSA, a Kubernetes Service Account assumes an IAM role via an STS (Security Token Service) AssumeRoleWithWebIdentity call. STS is an AWS service that issues short-lived, temporary security credentials. In practice, the pod exchanges a Kubernetes-issued OIDC token for those temporary AWS credentials.

The Setup

To use IRSA, you first need to configure an OIDC provider in your AWS account that points to your EKS cluster’s OIDC issuer URL. The OIDC provider acts as a bridge: it tells AWS, “I trust tokens issued by this specific Kubernetes cluster.”

Here is how we set it up in our project (19-openid-connect-provider.tf):

data "tls_certificate" "eks" {
    url = aws_eks_cluster.eks.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.eks.identity[0].oidc[0].issuer
}

Once the OIDC provider is established, you can create IAM roles that trust it. The tricky part is the trust policy—a JSON document attached to an IAM role that defines who is allowed to assume that role. You must restrict the trust policy so that only a specific Service Account in a specific Kubernetes namespace can assume the role.

IRSA in Action: EFS CSI Driver

Let’s look at how we grant permissions to the Amazon EFS (Elastic File System) CSI (Container Storage Interface) Driver using IRSA (20-efs.tf). The CSI Driver is a Kubernetes component that lets pods mount external storage systems—in this case, AWS’s managed network file system:

data "aws_iam_policy_document" "efs_csi_driver" {
    statement {
        actions = ["sts:AssumeRoleWithWebIdentity"]
        effect = "Allow"

        condition {
            test = "StringEquals"
            # Scoping the trust relationship to a specific service account
            variable = "${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:sub"
            values = ["system:serviceaccount:kube-system:efs-csi-controller-sa"]
        }

        principals {
            type = "Federated"
            identifiers = [aws_iam_openid_connect_provider.eks.arn]
        }
    }
}

resource "aws_iam_role" "efs_csi_driver" {
    name = "${aws_eks_cluster.eks.name}-efs-csi-driver"
    assume_role_policy = data.aws_iam_policy_document.efs_csi_driver.json
}

Finally, you must explicitly link the IAM role to the Kubernetes Service Account using an annotation. This is often done via Helm values. Helm is a package manager for Kubernetes that lets you install and configure applications using templated manifests, and “Helm values” are the configuration parameters you pass in to customize a deployment:

    set = [{
        name  = "controller.serviceAccount.name"
        value = "efs-csi-controller-sa"
    },{
        name  = "controller.serviceAccount.annotation.eks\\.amazonaws\\.com/role-arn"
        value = aws_iam_role.efs_csi_driver.arn
    }]

The IRSA Experience: While IRSA is incredibly secure, the OIDC provider dependency and the complex condition block in the trust policy create friction—especially in multi-cluster environments where each cluster requires its own OIDC provider and corresponding IAM trust policy updates.

2. The Modern Approach: EKS Pod Identity

Recognizing the operational overhead of IRSA, AWS introduced EKS Pod Identity. Instead of relying on OIDC federation and Mutating Admission Webhooks (a Kubernetes mechanism that automatically modifies pod specifications—such as injecting environment variables or tokens—before the pod is created), Pod Identity uses a managed EKS add-on running on your nodes. This agent intercepts credential requests from pods and handles the STS authentication transparently, without requiring any changes to your pod specs or Service Account annotations.

The Setup

To enable Pod Identity, you just need to install the eks-pod-identity-agent add-on (13-pod-Identity-addon.tf):

resource "aws_eks_addon" "pod_identity" {
    cluster_name  = aws_eks_cluster.eks.name
    addon_name    = "eks-pod-identity-agent"
    addon_version = "v1.3.10-eksbuild.3"
}

With Pod Identity, the IAM trust policy becomes delightfully simple. You no longer reference cluster-specific OIDC provider URLs or complex condition strings. Instead, you simply trust the EKS service principal (pods.eks.amazonaws.com).

Pod Identity in Action: Cluster Autoscaler

Let’s look at how we grant permissions to the Cluster Autoscaler using Pod Identity (14-cluster-autoscaler.tf):

resource "aws_iam_role" "cluster_autoscaler" {
    name = "${aws_eks_cluster.eks.name}cluster-autoscaler"

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

Notice how clean that trust policy is compared to the IRSA version—no OIDC URLs, no StringEquals conditions. But wait, how does AWS know which pod is allowed to assume this role if we removed the OIDC conditions?

Instead of embedding the service account in the IAM policy and relying on Kubernetes annotations, Pod Identity introduces a new first-class AWS resource: the Pod Identity Association.

resource "aws_eks_pod_identity_association" "cluster_autoscaler" {
    cluster_name    = aws_eks_cluster.eks.name
    namespace       = "kube-system"
    service_account = "cluster-autoscaler"
    role_arn        = aws_iam_role.cluster_autoscaler.arn
}

That’s it. You no longer need to annotate the Service Account in Kubernetes:

resource "helm_release" "cluster_autoscaler" {
    # ... 
    set = [
        {
            name  = "rbac.serviceAccount.name"
            value = "cluster-autoscaler"
        },
        # No eks.amazonaws.com/role-arn annotation needed!
    ]
}

The Pod Identity Experience: Pod Identity shifts the mapping between Kubernetes identities and AWS identities entirely into the AWS API (via the Association resource). This means you can manage a single IAM role and attach it to multiple clusters without ever touching the IAM trust policy again.

3. Side-by-Side Comparison

To highlight the differences, let’s summarize how these two approaches handle key components:

FeatureIRSAEKS Pod Identity
Trust PrincipleOIDC Provider Federationpods.eks.amazonaws.com Service Principal
IAM Trust PolicyComplex. Requires StringEquals condition matching OIDC URL and SA name.Simple. Just trust the EKS Pods service.
Multi-Cluster ReusabilityPoor. Must update IAM trust policy for every new cluster’s OIDC URL.Excellent. IAM role stays the same; just create new Associations.
Kubernetes ConfigurationRequires eks.amazonaws.com/role-arn annotation on Service Accounts.No annotations required. Mapping is done via AWS API.
Under the HoodToken injected via Mutating Admission Webhook.Agent intercepts requests on the node.

Gotchas and Practical Tips

  1. The Pod Identity Agent runs as a DaemonSet. A DaemonSet is a Kubernetes workload type that ensures one copy of a pod runs on every node in the cluster. This means the Pod Identity Agent will consume CPU and memory on every node. If your nodes are resource-constrained (e.g., small t3.small instances), account for its footprint when planning node capacity.

  2. When migrating from IRSA to Pod Identity, remove the old annotation. If you leave the eks.amazonaws.com/role-arn annotation on your Kubernetes Service Account after switching to Pod Identity, the pod may still attempt to use the IRSA credential chain (the sequence of steps the AWS SDK follows to find valid credentials). Always clean up the annotation to avoid confusing credential resolution behavior.

Conclusion: Which Should You Choose?

Having both methods active in our project highlights a transitional reality for many DevOps (Development and Operations) teams:

  1. Use EKS Pod Identity for New Projects: If you are building a new EKS cluster today, start with Pod Identity. The simplified IAM trust policies and the elimination of OIDC dependencies make multi-cluster management and terraform modularity vastly superior.
  2. Keep IRSA for Compatibility (For Now): Some third-party applications or older SDK (Software Development Kit) versions may not fully support the credential provider chain required by the Pod Identity agent yet. In our project, we maintained IRSA for EFS (20-efs.tf) and the Secrets Store CSI Driver (21-secrets-store-csi-driver.tf), while moving modern add-ons like the AWS Load Balancer Controller and Cluster Autoscaler to Pod Identity.
  3. Migration is Easy: The transition from IRSA to Pod Identity is straightforward. You remove the OIDC condition from your role, remove the Kubernetes annotation, and create an aws_eks_pod_identity_association.

By understanding both IRSA and EKS Pod Identity, you can leverage the granular security of OIDC where necessary, while adopting the streamlined developer experience of Pod Identity for the future.

TERMINAL
1: zsh
sirishgurung@portfolio:-$cat ./contact.txt
▄▄   ▄▄▄▄ ▄▄▄▄ ▄▄  ▄▄▄      ▄▄ ▄ ▄ ▄▄▄▄ ▄▄▄▄ ▄▄ ▄      ▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄ ▄▄ ▄ ▄▄▄▄ ▄▄▄▄ ▄▄
██   ██ ▀  ██  ▀  ██▀       ██ █ █ ██ █ ██ █ ██ █       ██  ██ █ ██    ██ ▀  ██  ██ █ ██ ▀ ██ █ ██
██   ██▀   ██     ▀██▄      ██ █ █ ██ █ ██▄▀ ██▄▀       ██  ██ █ ██ ▄▄ ██▀   ██  ██▄█ ██▀  ██▄▀ ██
 █▄▄  █ █  ▐█      ▄▀▀       █ █ ▀  █ █ ▀█ █  █ █       ▐█   █ █ ▐▀ ▀▌  █ █  ▐█   █ █  █ █ ▀█ █ ▀▀
▀▀▀▀ ▀▀▀▀  ▀▀     ▀▀▀       ▀▀▀▀▀▀ ▀▀▀▀ ▀▀ ▀ ▀▀ ▀       ▀▀  ▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀  ▀▀  ▀▀ ▀ ▀▀▀▀ ▀▀ ▀ ▀▀
      
I'd love to hear from you!>Get in touch