How to Use Amazon EKS Pod Identity for Simplified IAM Access — Replace IRSA with Zero OIDC Configuration

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)
  • kubectl configured to communicate with your cluster
  • eksctl 0.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:

  1. Install the Pod Identity Agent on your existing cluster (Step 1 above).
  2. Update the IAM role trust policy to accept both IRSA and Pod Identity by adding the pods.eks.amazonaws.com principal alongside the existing OIDC statement.
  3. Create the Pod Identity association for the same service account and role.
  4. Restart pods — the SDK credential chain will prefer Pod Identity credentials when both are available.
  5. Verify using aws sts get-caller-identity from inside the pod. The session name will include eks-my-cluster-... for Pod Identity vs a token-based name for IRSA.
  6. 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:TagSession in the trust policy. Pod Identity requires both sts:AssumeRole and sts:TagSession. Omitting the latter causes AccessDenied errors 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 with aws 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, and kubernetes-pod-name. You can use these in IAM policy conditions with aws:PrincipalTag for 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:TagSession in 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.

Leave a Comment

Your email address will not be published. Required fields are marked *