Intermediate
Introduction
If you’ve configured IAM Roles for Service Accounts (IRSA) on Amazon EKS, you know the pain: creating OIDC providers, crafting trust policies with specific issuer URLs, and debugging sts:AssumeRoleWithWebIdentity failures at 2 AM. Every cluster needs its own OIDC provider, every role needs a cluster-specific trust policy, and scaling this across dozens of clusters becomes a maintenance nightmare.
EKS Pod Identity, launched at re:Invent 2023, eliminates all of that. No OIDC provider setup, no per-cluster trust policies, and a straightforward association model that maps Kubernetes service accounts to IAM roles directly through the EKS API. This guide is for intermediate DevOps engineers and developers who are already running workloads on EKS with IRSA and want to migrate — or for those setting up new clusters and want the simpler path from day one.
Prerequisites
- An existing Amazon EKS cluster running Kubernetes 1.24 or later
- AWS CLI v2 installed and configured (v2.15.3+ recommended for full Pod Identity support)
kubectlconfigured to communicate with your clustereksctl0.173.0 or later (optional but simplifies setup)- IAM permissions to create roles, policies, and EKS pod identity associations
How EKS Pod Identity Works Under the Hood
EKS Pod Identity introduces a new component: the Amazon EKS Pod Identity Agent, which runs as a DaemonSet on your nodes. When a pod needs AWS credentials, the SDK calls the container credentials endpoint (http://169.254.170.23/v1/credentials) instead of the OIDC token exchange used by IRSA.
The agent intercepts this request, communicates with the EKS Auth API (eks-auth:AssumeRoleForPodIdentity), and returns temporary credentials scoped to the IAM role you associated with that pod’s service account. The key difference: the trust relationship is managed by EKS itself, not by an OIDC trust policy on the IAM role. This means one IAM role can be reused across multiple clusters without modifying its trust policy.
The credentials chain in AWS SDKs automatically picks up Pod Identity credentials — no code changes needed if you’re using a recent SDK version.
Step-by-Step: Setting Up EKS Pod Identity
Step 1: Install the EKS Pod Identity Agent Add-on
aws eks create-addon \
--cluster-name my-cluster \
--addon-name eks-pod-identity-agent \
--addon-version v1.3.5-eksbuild.2
# Verify the agent pods are running
kubectl get pods -n kube-system -l app.kubernetes.io/name=eks-pod-identity-agent
Expected output:
NAME READY STATUS RESTARTS AGE
eks-pod-identity-agent-gk2d4 1/1 Running 0 45s
eks-pod-identity-agent-rv7wz 1/1 Running 0 45s
Step 2: Create an IAM Role with the Pod Identity Trust Policy
Create a file named trust-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}
# Create the IAM role
aws iam create-role \
--role-name my-app-pod-role \
--assume-role-policy-document file://trust-policy.json
# Attach your application policy
aws iam attach-role-policy \
--role-name my-app-pod-role \
--policy-arn arn:aws:iam::policy/my-app-s3-read-policy
Notice the trust policy principal is pods.eks.amazonaws.com — not a cluster-specific OIDC URL. This is the fundamental simplification.
Step 3: Create a Kubernetes Service Account
kubectl create serviceaccount my-app-sa -n default
Unlike IRSA, you do not need the eks.amazonaws.com/role-arn annotation on the service account.
Step 4: Create the Pod Identity Association
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--namespace default \
--service-account my-app-sa \
--role-arn arn:aws:iam::role/my-app-pod-role
Expected output:
{
"association": {
"clusterName": "my-cluster",
"namespace": "default",
"serviceAccount": "my-app-sa",
"roleArn": "arn:aws:iam::role/my-app-pod-role",
"associationId": "a-xxxxxxxxxxxx"
}
}
Step 5: Deploy and Verify
# Deploy a test pod
kubectl run aws-test --image=amazon/aws-cli:latest \
--serviceaccount=my-app-sa \
--restart=Never \
--command -- sleep 3600
# Verify credentials
kubectl exec aws-test -- aws sts get-caller-identity
You should see the assumed role ARN matching my-app-pod-role in the output.
IRSA vs EKS Pod Identity: Detailed Comparison
| Feature | IRSA | EKS Pod Identity |
|---|---|---|
| OIDC Provider Required | Yes (one per cluster) | No |
| Trust Policy Scope | Cluster-specific OIDC URL | Generic pods.eks.amazonaws.com |
| Role Reuse Across Clusters | Requires trust policy update per cluster | Same role, no changes needed |
| Service Account Annotation | Required (eks.amazonaws.com/role-arn) |
Not required |
| Credential Delivery | Projected service account token + STS | EKS Pod Identity Agent DaemonSet |
| Session Tags | Not supported natively | Automatic (cluster name, namespace, service account) |
| IAM Roles Per Cluster Limit | Limited by OIDC trust policy size (4 conditions) | No trust policy scaling issue |
| Cross-Account Access | Supported (complex trust policy) | Supported (simpler trust policy) |
| Minimum EKS Version | 1.13+ | 1.24+ |
| Fargate Support | Yes | No (nodes required for DaemonSet) |
Migration Path: From IRSA to Pod Identity
You can run IRSA and Pod Identity side by side. There’s no need for a big-bang migration. Here’s a practical approach:
- Install the Pod Identity Agent on your existing cluster (Step 1 above).
- Update the IAM role trust policy to accept both IRSA and Pod Identity by adding the
pods.eks.amazonaws.comprincipal alongside the existing OIDC statement. - Create the Pod Identity association for the same service account and role.
- Restart pods — the SDK credential chain will prefer Pod Identity credentials when both are available.
- Verify using
aws sts get-caller-identityfrom inside the pod. The session name will includeeks-my-cluster-...for Pod Identity vs a token-based name for IRSA. - Clean up: Remove the OIDC trust policy statement and the service account annotation once all workloads are verified.
# Dual trust policy during migration (trust-policy-migration.json)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": ["sts:AssumeRole", "sts:TagSession"]
},
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:default:my-app-sa"
}
}
}
]
}
aws iam update-assume-role-policy \
--role-name my-app-pod-role \
--policy-document file://trust-policy-migration.json
Common Mistakes
- Forgetting
sts:TagSessionin the trust policy. Pod Identity requires bothsts:AssumeRoleandsts:TagSession. Omitting the latter causesAccessDeniederrors that are not immediately obvious from the error message. - Using an outdated AWS SDK. Pod Identity relies on the container credential provider in the SDK credential chain. AWS SDK for Go v1.54.0+, Boto3 1.28.57+, and AWS SDK for Java 2.21.1+ support it. Older versions silently fall back to instance metadata credentials (the node role), which is a security risk you may not notice until an audit.
- Expecting Pod Identity to work on Fargate. The Pod Identity Agent runs as a DaemonSet, which requires EC2 nodes or Karpenter-managed nodes. Fargate profiles must continue using IRSA.
- Mismatched namespace or service account name. The association is an exact match on
namespace+service-account. A typo means the pod silently gets no credentials — check withaws eks list-pod-identity-associations --cluster-name my-cluster. - Not restarting pods after creating the association. Existing running pods do not pick up new associations. You must restart them to trigger the credential injection.
Security and Cost Considerations
Security improvements over IRSA:
- Automatic session tags: Pod Identity injects session tags for
eks-cluster-arn,eks-cluster-name,kubernetes-namespace,kubernetes-service-account, andkubernetes-pod-name. You can use these in IAM policy conditions withaws:PrincipalTagfor attribute-based access control (ABAC) — something IRSA cannot do natively. - Reduced trust policy surface: No OIDC issuer URLs to manage means fewer opportunities for trust policy misconfiguration.
- Centralized visibility: All associations are queryable via the EKS API, unlike IRSA where you’d need to scan every IAM role trust policy and every service account annotation.
Cost: EKS Pod Identity has no additional cost. The Pod Identity Agent add-on is free. You pay only for the standard EKS cluster pricing ($0.10/hour) and the compute resources for the DaemonSet pods, which are minimal (~30MB memory per node).
Conclusion
EKS Pod Identity is the clear successor to IRSA for EC2-based EKS workloads. It removes the OIDC complexity, enables role reuse across clusters without trust policy changes, and adds session tagging for ABAC policies out of the box. The migration is non-disruptive since both mechanisms coexist.
- Pod Identity eliminates OIDC provider setup — one trust policy works across all clusters.
- Migration is incremental — run IRSA and Pod Identity side by side, validate, then clean up.
- Automatic session tags enable ABAC policies that weren’t possible with IRSA.
- Fargate workloads still require IRSA — Pod Identity needs the DaemonSet agent on EC2 nodes.
- Always include
sts:TagSessionin the trust policy and ensure your AWS SDKs are up to date.
Found this helpful? Share it with your team. For more practical AWS and DevOps guides, visit riseofcloud.com.
Let’s keep learning consistently at a medium pace.