EKS Pod Identity versus IRSA — associations and role trust
IRSA wires IAM through the cluster OIDC issuer and a ServiceAccount annotation. Pod Identity wires IAM through an association object and a shared EKS service principal.
<!-- hal:authoritative:yaml -->
IRSA wires IAM through the cluster OIDC issuer and a ServiceAccount annotation. Pod Identity wires IAM through an association object and a shared EKS service principal.
§I — Frame
On EKS, a Pod that needs AWS API access should not inherit the node instance role. Two first-party patterns exist.
IRSA (2026-09-06): attach an OIDC identity provider to the cluster issuer, annotate the ServiceAccount with eks.amazonaws.com/role-arn, and write an IAM trust policy that Federates to that OIDC provider with StringEquals on sub and usually aud.
EKS Pod Identity: run the Amazon EKS Pod Identity Agent as a DaemonSet, call CreatePodIdentityAssociation (cluster name + namespace + ServiceAccount name + role ARN), and write an IAM trust policy that trusts pods.eks.amazonaws.com with the right condition keys. No role-arn annotation on the ServiceAccount. No per-cluster OIDC provider required for this path.
This is not GKE Gateway (09-18), not AKS PSA (09-12), and not NetworkPolicy (09-09). It is the successor-or-sibling of IRSA on the same ServiceAccount shelf.
§II — Two trust shapes
| Piece | IRSA | Pod Identity |
|---|---|---|
| Cluster prep | OIDC provider for the cluster issuer URL | Pod Identity Agent DaemonSet (add-on or manifest) |
| Binding handle | SA annotation eks.amazonaws.com/role-arn | Association API / Console / CLI / Terraform resource |
| Trust principal | Federated OIDC provider ARN | Service principal pods.eks.amazonaws.com |
| Typical condition | sub = system:serviceaccount:NS:SA, aud = sts.amazonaws.com | eks.amazonaws.com / association namespace, service account, cluster ARN (and related keys) |
| Credential delivery | Projected web identity token + STS AssumeRoleWithWebIdentity | Agent + Credential Provider on the node hands temporary creds into the Pod |
Both still key off a Kubernetes ServiceAccount name. Both still need a least-privilege IAM permission policy on the role. The difference is how the trust and delivery path are declared.
§III — Worked Pod Identity shape
Confirm the agent is present (add-on eks-pod-identity-agent or equivalent DaemonSet in kube-system):
kubectl -n kube-system get ds eks-pod-identity-agent
# or: kubectl -n kube-system get pods -l app.kubernetes.io/name=eks-pod-identity-agent
Create (or reuse) an IAM role whose trust policy allows Pod Identity. Sketch:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "pods.eks.amazonaws.com" },
"Action": ["sts:AssumeRole", "sts:TagSession"],
"Condition": {
"StringEquals": {
"aws:SourceAccount": "111122223333"
},
"ArnEquals": {
"aws:SourceArn": "arn:aws:eks:us-east-1:111122223333:cluster/prod"
}
}
}
]
}
Tighten further with association-scoped condition keys from current AWS docs when you want one role shared carefully across associations. Attach a narrow permission policy (for example S3 GetObject on one prefix).
Associate:
aws eks create-pod-identity-association \
--cluster-name prod \
--namespace payments \
--service-account ledger-reader \
--role-arn arn:aws:iam::111122223333:role/payments-ledger-reader
The ServiceAccount ledger-reader itself has no IRSA annotation. Pods that use serviceAccountName: ledger-reader receive credentials through the agent path once the association is Active.
List and describe:
aws eks list-pod-identity-associations --cluster-name prod
aws eks describe-pod-identity-association \
--cluster-name prod \
--association-id a-xxxxxxxxxxxx
§IV — IRSA contrast (do not unlearn)
IRSA still works and still appears in many clusters. Reminder of the annotation path:
apiVersion: v1
kind: ServiceAccount
metadata:
name: ledger-reader
namespace: payments
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/payments-ledger-reader-irsa
Trust for IRSA uses the cluster OIDC provider as Federated principal and matches system:serviceaccount:payments:ledger-reader. That is a different trust document from Pod Identity. Do not paste a pods.eks.amazonaws.com trust onto an IRSA-only role and expect the annotation path to succeed.
§V — Ops discriminators
- Missing agent: association exists, Pod has no AWS creds. Check DaemonSet Ready on every node pool that runs the workload.
- Wrong SA name or namespace: association is exact-match. Typos fail closed.
- Dual binding confusion: a SA with both an IRSA annotation and a Pod Identity association can surprise operators. Prefer one path per SA during migration.
- Node role sprawl: if the instance profile still allows broad S3/ECR, Pod Identity does not erase that blast radius. Shrink the node role separately.
- Not NetworkPolicy: east-west filters (09-09) do not grant IAM. Not PSA: admit-time pod shape (09-12) does not grant IAM.
§VI — Migration sketch
- Inventory IRSA annotations (Dev companion censuses associations and remaining annotations).
- Install Pod Identity Agent on the cluster.
- Create roles (or clone) with
pods.eks.amazonaws.comtrust. - Create associations for high-value SAs first.
- Remove
eks.amazonaws.com/role-arnonly after Pods prove temporary creds via the new path. - Keep the OIDC provider until no IRSA consumers remain (other add-ons may still need it).
§VII — Closing
Pod Identity moves the IAM bind from an annotation on the ServiceAccount to an association owned by the cluster control plane, with a shared EKS service principal in the trust policy. IRSA remains valid. Pick one bind style per ServiceAccount, keep the IAM permission policy narrow, and verify the agent before you debug the application.
Related
- EKS IRSA (09-06)
- Dev: association census
- Cert: CKS SA hardening