Table of Contents
Introduction
Every Kubernetes workload on Amazon EKS eventually needs to talk to AWS services—pulling files from S3, fetching secrets from Secrets Manager, publishing to SNS, or writing to DynamoDB. The question is not if your pods need access, but how to grant that access without hardcoding long-lived keys or passing credentials around by hand.
AWS offers two modern, AWS-native options:
- IRSA (IAM Roles for Service Accounts) — the long-standing approach using OpenID Connect (OIDC) to map a Kubernetes service account to an IAM role.
- EKS Pod Identity — a newer approach that uses an AWS-managed controller and agent to handle identity for your pods with less setup.
This post does three things:
- Walk through step-by-step setup for IRSA and Pod Identity.
- Explain how each method works under the hood, including token creation and role assumption.
- Give you a clear comparison plus a practical migration path.
How pods obtain AWS access on EKS
Kubernetes doesn’t know about IAM. The bridge is identity federation. Here’s the standard pattern:
- A pod runs under a service account.
- That service account is tied to an IAM role (directly via IRSA or via an association with Pod Identity).
- The pod requests temporary credentials from AWS STS.
- With those short-lived credentials, the pod calls AWS APIs.
The goal is to grant least-privilege permissions per workload while avoiding long-lived secrets. Both IRSA and Pod Identity do this, but they differ in setup and mechanics.
Method 1 — Set up EKS IRSA (IAM Roles for Service Accounts)
IRSA uses your cluster’s OIDC issuer and a trust relationship in IAM to let a pod assume a role via its service account. Below are two ways to set it up: a manual path (fine-grained control) or a one-liner with eksctl (fast and convenient). Pick one approach—no need to do both.
Option A — Manual IRSA setup (fine-grained)
Step A1 — Confirm your cluster’s OIDC provider
# Get the issuer URL
aws eks describe-cluster \
--name my-cluster \
--query "cluster.identity.oidc.issuer" \
--output text
If the OIDC provider is missing, create it once per cluster (some tools create it for you automatically):
# Example: create an OIDC provider (only if missing)
# Replace <issuer> and <thumbprint> appropriately
aws iam create-open-id-connect-provider \
--url https://oidc.eks.<region>.amazonaws.com/id/<cluster-oidc-id> \
--client-id-list sts.amazonaws.com \
--thumbprint-list <thumbprint>
Tip: With eksctl, you can also run:
eksctl utils associate-iam-oidc-provider \
--cluster my-cluster \
--approve
That command wires up the OIDC provider for you.
Step A2 — Create a least-privilege IAM policy
Example: read-only access to a specific S3 bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadOnlyToBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
]
}
Step A3 — Create an IAM role with a trust policy for your cluster
Trust policy example (replace account, audience, and issuer):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "IRSAOIDCTrust",
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::<account-id>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<cluster-oidc-id>" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<region>.amazonaws.com/id/<cluster-oidc-id>:aud": "sts.amazonaws.com",
"oidc.eks.<region>.amazonaws.com/id/<cluster-oidc-id>:sub": "system:serviceaccount:default:s3-access-sa"
}
}
}
]
}

Attach the S3 read-only policy to this role.
Step A4 — Create a Kubernetes ServiceAccount annotated with the role
apiVersion: v1
kind: ServiceAccount
metadata:
name: s3-access-sa
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account-id>:role/S3AccessRoleStep A5 — Run a pod that uses the service account
apiVersion: v1
kind: Pod
metadata:
name: s3-tester
namespace: default
spec:
serviceAccountName: s3-access-sa
containers:
- name: alpine
image: public.ecr.aws/docker/library/alpine:3.20
command: ["sh", "-c", "apk add --no-cache aws-cli && aws s3 ls s3://my-bucket && sleep 3600"]
Check the logs and confirm the bucket listing appears.
Option B — IRSA via eksctl (one command)
If you prefer a faster route, eksctl can create the IAM role, attach the policy, and create or reuse the Kubernetes service account with the correct annotation. You can often skip the manual trust policy and service account YAML because eksctl wires those for you.
eksctl create iamserviceaccount \
--name <service-account-name> \
--namespace <namespace> \
--cluster <cluster-name> \
--attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
--approve \
--override-existing-serviceaccountsWhat this does:
- Creates (or updates) the Kubernetes ServiceAccount.
- Creates an IAM role and attaches the specified policy.
- Adds the role annotation to the service account.
- If needed, you can also run
eksctl utils associate-iam-oidc-provider --cluster <cluster-name> --approvebefore the command to set up the OIDC provider.
In short, eksctl create iamserviceaccount automates the role, annotation, and trust wiring that you would otherwise craft by hand.
How IRSA actually works
With IRSA, the pod uses a projected service account token signed by the cluster’s OIDC issuer. The AWS SDK (or the AWS CLI you installed inside the container) follows a Web Identity flow:

Key points:
- The trust policy ties the role to specific service account identity (
sub) and audience (aud). - STS returns short-lived credentials, which the pod uses for AWS API calls.
- No static IAM keys live in your manifests or pods.
Method 2 — Set up EKS Pod Identity
EKS Pod Identity aims to simplify per-pod IAM by managing the heavy lifting inside the cluster. You still need IAM roles and policies, but the mapping from service account to role is handled through an association API and an in-cluster agent.
Note: by default, EKS does not install the Pod Identity components. You add them as an EKS add-on. When the add-on is enabled, EKS deploys a Pod Identity Agent (typically as a DaemonSet) on every node.

That node-local agent serves credential requests for matching pods and talks to AWS STS and AWS APIs to fetch short-lived credentials. This is how pods get credentials without you wiring up an OIDC provider or adding role annotations.
Step B1 — Turn on Pod Identity for your cluster
You can enable it in the EKS console or via CLI (the exact command may vary by region and tooling). Once enabled, create an association:
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--namespace default \
--service-account-name app-sa \
--role-arn arn:aws:iam::<account-id>:role/PodIdentityRole
Step B2 — Create a least-privilege policy, then the role
Reuse the S3 read-only policy above, or tailor it to your app. Attach it to a new IAM role (e.g., PodIdentityRole).
Step B3 — Deploy your workload
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
spec:
serviceAccountName: app-sa
containers:
- name: app
image: public.ecr.aws/docker/library/alpine:3.20
command: ["sh", "-c", "apk add --no-cache aws-cli && aws s3 ls s3://my-bucket && sleep 3600"]Verify access
As before, check logs to confirm API calls succeed.
How Pod Identity works
With Pod Identity, an AWS-managed agent running in your cluster handles token exchange on behalf of the pod. The association you created tells AWS which role a given service account can use.

Key points:
- You don’t create or manage an OIDC provider or service account annotations for role ARNs.
- The association is the source of truth.
- The agent fetches temporary credentials from STS and exposes them to pods that match an approved service account/namespace pair.
7) IRSA vs Pod Identity — when to use which?
Feature | EKS IRSA | EKS Pod Identity |
|---|---|---|
Initial setup | OIDC provider + trust policy + SA annotation | AWS-managed association + agent |
OIDC management | Required | Not required |
Mapping granularity | Per service account (via annotation and trust policy claims) | Per service account (via association) |
Multi-cluster scale | Repeated OIDC + roles per cluster | Associations scale cleanly across clusters |
Day-2 upkeep | More manual wiring | Simpler ongoing ops |
Maturity | Long-used in production | Newer, rapidly adopted |
Good fit | Existing clusters, fine-grained trust conditions | New clusters, teams seeking lighter setup |
Quick guidance
- If you already run IRSA successfully and have compliance rules tied to OIDC claims, staying with IRSA is perfectly valid.
- If you’re starting fresh or want fewer moving parts, Pod Identity is attractive and easier to roll out at scale.
8) Migration path — moving from IRSA to Pod Identity
Many teams will run both methods for a while, shifting workloads gradually.
Step 1: Inventory current IRSA usage
List service accounts, roles, and policies. Note any trust conditions tied to sub or aud.
Step 2: Create matching IAM roles for Pod Identity
Reuse existing policies. Keep role names close to current ones for clarity.
Step 3: Create associations
Bind each service account to the matching role:
aws eks create-pod-identity-association \
--cluster-name my-cluster \
--namespace team-a \
--service-account-name writer \
--role-arn arn:aws:iam::<account-id>:role/team-a-writerStep 4: Update workloads to the new service accounts (if needed)
Some teams keep the same names; others create parallel SAs (e.g., writer-pi) to test side-by-side.
Step 5: Validate and cut over
Watch application logs and CloudTrail events to confirm correct role usage. When satisfied, you can remove IRSA annotations and, later, retire the OIDC provider if nothing else depends on it.
IaC tip: Model both IRSA and Pod Identity in Terraform or CDK during the transition, then delete IRSA blocks once all pods move.
Conclusion
Both EKS IRSA and EKS Pod Identity solve the same core challenge: giving pods short-lived AWS credentials tied to an IAM role, without shipping secrets inside your containers. IRSA is familiar and feature-rich; Pod Identity trims setup and day-to-day friction by moving identity plumbing into an AWS-managed component.
Takeaways
- For new clusters or teams that want less wiring, Pod Identity is a strong default.
- For existing clusters with OIDC trust policies dialed in, IRSA remains a fine approach.
- No matter which you pick, keep policies tight, test early, and watch CloudTrail.
Question for you: Which method are you using today—IRSA, Pod Identity, or both? What tripped you up during setup? Share your story so others can learn from it.