skip to content

Amazon EKS offers two AWS-side mechanisms for giving a pod its own IAM role: IAM Roles for Service Accounts (IRSA) and EKS Pod Identity. How do they differ operationally, and how would you choose between them?

level: seniorimportance: should knowfreq 44%

answer

  1. per-pod role, no static keys
  2. OIDC issuer versus service principal
  3. the sub condition is the fence
  4. the agent is a DaemonSet

basics

~20 s

IRSA federates the cluster's OIDC issuer into IAM, so each role's trust policy names that cluster's issuer and service account. EKS Pod Identity instead trusts the service principal pods.eks.amazonaws.com once and maps roles to service accounts through an EKS API association, which scales better across clusters.

solid answer

~50 s

Both hand a pod short-lived credentials for a role you control, and both are AWS-side plumbing rather than Kubernetes features. With IRSA, you register the cluster's OIDC issuer as an IAM identity provider; each role's trust policy allows `sts:AssumeRoleWithWebIdentity` from that issuer with conditions on the `sub` claim naming the namespace and service account. That means every role is pinned to one cluster's issuer URL, and reusing a role across clusters means editing its trust policy each time. EKS Pod Identity moves the mapping out of IAM: the role trusts the service principal `pods.eks.amazonaws.com` once, and you create an association through the EKS API binding a cluster, namespace and service account to that role. Pod Identity is easier to operate at scale and needs no per-cluster OIDC provider, but it requires the Pod Identity Agent add-on on nodes, so it does not work on Fargate — where IRSA remains the answer. Outside EKS, only IRSA-style OIDC federation applies.

code

json · 18 lines
json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com",
          "oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:payments:checkout"
        }
      }
    }
  ]
}

go deeper

for a junior

Know that on EKS a workload should get its own IAM role with temporary credentials, not a static access key and not the node's role, and that AWS provides IRSA and Pod Identity to do it.

for a middle

Explain the two mechanisms concretely: IRSA registers the cluster's OIDC issuer with IAM and uses a scoped trust policy, while Pod Identity trusts an AWS service principal and keeps the mapping in an EKS association plus a node agent.

for a senior

Show operational judgment — why per-cluster issuers in trust policies do not scale, why Fargate forces IRSA, how you would migrate service by service, and how you would spot a pod silently falling back to the node role.

for a principal

Own the standard: which mechanism is default across the estate, how roles are scoped and reviewed, whether pod access to instance metadata is blocked entirely, and how identity survives clusters being rebuilt.

## The problem both mechanisms solve A workload in an EKS cluster needs to call an AWS API — read an S3 object, publish to SNS, decrypt with KMS. The bad answers are a static access key in a Secret, or attaching a broad role to the node so that *every* pod on that node inherits it through the instance metadata service. AWS's answer is to give each workload identity its own IAM role with short-lived credentials. There are two implementations, and interviewers ask about the difference because both exist in the wild and the migration is a live topic. ## IRSA: federate the cluster's issuer into IAM Every EKS cluster publishes an OIDC issuer URL and serves signed service-account tokens. IRSA works by registering that issuer as an IAM OIDC identity provider in your account, then writing role trust policies that accept a token from it: ```json { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLE" }, "Action": "sts:AssumeRoleWithWebIdentity" } ``` The critical part is the condition block. Without a condition on the `sub` claim — which carries the value `system:serviceaccount:<namespace>:<name>` — *any* service account in the cluster could assume the role. A condition on `aud` (`sts.amazonaws.com`) belongs there too. Annotate the service account with `eks.amazonaws.com/role-arn`, and an admission webhook injects `AWS_ROLE_ARN` and `AWS_WEB_IDENTITY_TOKEN_FILE` into the pod; the AWS SDKs read the projected token from that path and call STS themselves. Credentials are short-lived and the token is refreshed by the kubelet. IRSA's operational cost is that identity is anchored to *the cluster's issuer URL*. A role usable from two clusters needs both issuers in its trust policy. Rebuild a cluster and the issuer changes, so every role trusting it must be edited. At a hundred roles across a dozen clusters, the trust policies become the thing that breaks. ## EKS Pod Identity: move the mapping into the EKS API Pod Identity, added at the end of 2023, inverts the arrangement. The role's trust policy names an AWS service principal once and never changes: ```json { "Effect": "Allow", "Principal": { "Service": "pods.eks.amazonaws.com" }, "Action": ["sts:AssumeRole", "sts:TagSession"] } ``` The cluster/namespace/service-account binding lives in an EKS *pod identity association* created with `aws eks create-pod-identity-association`. The Pod Identity Agent, installed as an EKS add-on, runs on each node and serves credentials over a link-local endpoint that the SDKs discover through `AWS_CONTAINER_CREDENTIALS_FULL_URI`. No OIDC provider per cluster, no trust-policy edit when a cluster is rebuilt, no service-account annotation carrying an account-specific ARN — which also means the same manifests deploy unchanged to dev and prod. Note `sts:TagSession` in the trust policy: Pod Identity attaches session tags for the cluster, namespace and service account, which you can use in policy conditions. ## How to choose - **Fargate pods: IRSA.** The Pod Identity Agent is a node-level DaemonSet, and Fargate pods run no DaemonSets, so Pod Identity does not apply there. - **Many clusters, many roles, clusters that get rebuilt: Pod Identity.** This is the case it was built for; the trust policy stops being per-cluster state. - **Anything outside EKS — self-managed Kubernetes, another cloud, an on-prem cluster federating into AWS: OIDC federation only.** Pod Identity is an EKS API and does not exist elsewhere. - **Existing IRSA that works: leave it.** Both mechanisms coexist in one cluster, and per-service migration is safe. If a service account has both, the Pod Identity association takes precedence. ## The failure modes worth naming An IRSA trust policy with no `sub` condition is a real privilege-escalation path — any pod in the cluster can mint that role. An SDK too old to understand the web-identity or container-credentials providers silently falls back to the node role and "works" with the wrong identity, which is worse than failing. And a pod that never gets credentials at all is usually a missing annotation, a missing association, or the Pod Identity Agent add-on not being installed on that node. What neither mechanism changes: the role's *permissions* are still an ordinary IAM policy problem, and the scoping discipline is the same one you would apply anywhere else.

  • What goes wrong if an IRSA role's trust policy omits the condition on the sub claim?
    Any service account in that cluster can assume the role, because the only remaining requirement is a token signed by the cluster's issuer. A workload in an unrelated namespace could then read whatever that role permits. The `sub` condition, pinned to `system:serviceaccount:<namespace>:<name>`, is what makes the role belong to one workload rather than the whole cluster.
  • A pod on EKS is getting AWS permissions it was never granted. What is the likely cause?
    It is almost certainly falling back to the node's instance profile through the instance metadata service, because the intended credential provider did not engage — a missing annotation or association, an SDK too old for the provider, or the Pod Identity Agent absent from that node. The fix is to make the workload path work and to block pod access to IMDS so this fallback cannot go unnoticed.
  • Can IRSA and EKS Pod Identity be used in the same cluster during a migration?
    Yes, and that is the normal way to migrate — service by service, rather than in one cutover. Both credential paths coexist, and if a single service account somehow has both an IRSA annotation and a Pod Identity association, the association wins. Migrate a low-risk workload first and verify the caller identity in CloudTrail before moving anything important.

saying these in an interview costs you the question

  • Storing a static IAM access key in a Kubernetes Secret instead
  • Relying on the node instance profile so every pod shares one role
  • Writing an IRSA trust policy with no condition on the sub claim
  • Assuming EKS Pod Identity works for pods on Fargate
  • Thinking Pod Identity exists outside EKS, on any Kubernetes cluster

context