Module 15 ยท Lesson 15.2
IAM: IRSA & Pod Identity
Your pods have never needed to authenticate to anything outside the cluster so far. The moment one needs to call a real AWS API โ read from S3, write to DynamoDB, publish to SQS โ you need a way to hand it real AWS credentials, without baking an access key into a Secret (don't do that; Module 5 already covered why Secrets are encoded, not encrypted, and a long-lived AWS key sitting in one is a real liability).
IRSA: federating a ServiceAccount to an IAM role
IRSA (IAM Roles for Service Accounts) was the first real solution: EKS runs an OIDC provider, you register it with IAM, and you create an IAM role whose trust policy says "any pod using ServiceAccount X in namespace Y may assume this role." You annotate the ServiceAccount with the role's ARN:
apiVersion: v1
kind: ServiceAccount
metadata:
name: payments-api
namespace: payments
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/payments-api-role
A webhook injects AWS SDK environment variables and a projected token into any pod using that ServiceAccount; the SDK exchanges the token for temporary credentials via STS, with no long-lived keys anywhere.
Pod Identity: the same outcome, less OIDC plumbing
EKS Pod Identity, AWS's newer mechanism, keeps the same shape โ ServiceAccount maps to IAM role โ but replaces the OIDC trust-policy dance with a direct, EKS-managed association via an agent running on each node. No per-cluster OIDC trust relationship to maintain, easier to reuse the same IAM role across multiple clusters, and as of now it's what AWS recommends for new applications on supported node types. IRSA still matters: it's required for AWS Fargate pods (Pod Identity's node agent needs an EC2 node to run on), and it's fine to leave running if you already have it working and tied to compliance controls around OIDC claims.
Your kind cluster vs. a real EKS cluster
| Your kind cluster | Real EKS | |
|---|---|---|
| Networking | Calico overlay โ pod IPs from an arbitrary virtual range (192.168.0.0/16), tunneled between nodes | VPC CNI โ pod IPs from your real VPC subnet via ENIs, routable like any EC2 instance |
| Ingress / LB | Traefik, Service type ClusterIP โ no real external address | AWS Load Balancer Controller provisions a real NLB/ALB from Service/Ingress annotations |
| Storage | local-path-provisioner โ a directory on the node's own disk | EBS CSI driver โ a real, network-attached EBS volume (or EFS CSI for shared access) |
| Identity for pods | No cloud IAM โ nothing to authenticate to | IRSA or Pod Identity โ a ServiceAccount maps to a real IAM role |
What's real here, what isn't
Your local cluster has no AWS account behind it, so you can't actually
assume a role or hit STS. What is real and testable: the ServiceAccount
object, its annotation, and a Deployment correctly referencing that
ServiceAccount via serviceAccountName โ the exact plumbing a real EKS
setup needs to be correct before IAM even enters the picture. That's what
this lesson's lab grades: the Kubernetes-side wiring, not AWS's half of the
handshake.
The infrastructure analogy
This is the Kubernetes-native evolution of instance profiles: instead of every EC2 instance in an Auto Scaling Group sharing one IAM role (too broad, hard to scope down), each pod gets exactly the permissions its own workload needs โ the same principle of least privilege, applied at a much finer grain than EC2 ever offered.
๐งช Lab: lab-31-irsa-pod-identity
Preview onlyGoal
Fix a Deployment that was supposed to run under an IRSA-enabled ServiceAccount, but doesn't โ the single most common way this setup breaks in practice: the role-arn annotation is correct, but nothing actually uses the ServiceAccount it's on.
Tasks
- Apply the starting manifests:
kubectl apply -f manifests/ - Look at the ServiceAccount's annotation:
kubectl get serviceaccount payments-api -n lab-31-irsa-pod-identity -o yamlโ note theeks.amazonaws.com/role-arnannotation. This part is correct. - Look at what ServiceAccount the pod is actually running under:
kubectl get pod -n lab-31-irsa-pod-identity -o jsonpath='{.items[0].spec.serviceAccountName}' - On a real EKS cluster, a pod running as
defaulthas no IRSA role at all โ it would fail the moment it tried to call any AWS API. Fixmanifests/app.yaml: setserviceAccountNametopayments-api. - Re-apply:
kubectl apply -f manifests/app.yaml - Confirm: re-run step 3 โ it should now print
payments-api.
Check
Run the check once the Deployment's pods run under the payments-api
ServiceAccount.
This lab runs against a real local Kubernetes cluster with an automated grader โ clone the repo and run make start to do it for real.
๐ Quiz
1. What's the right way for a pod on EKS to get AWS API credentials for S3/DynamoDB/etc.?
2. As of now, what does AWS recommend for new applications on supported node types?
3. You're deploying to AWS Fargate and want pod-level IAM roles. Which mechanism must you use?scenario
4. On your local kind cluster, you apply a ServiceAccount with a correct-looking `eks.amazonaws.com/role-arn` annotation and a Deployment referencing it. What actually happens when a pod tries to call AWS?scenario
5. Why is per-pod IAM (IRSA/Pod Identity) better than one shared IAM role on every node's instance profile?
Progress isn't saved in this preview โ run the course locally to track completion and grade labs for real.