๐Ÿ“– Read-only preview โ€” lessons, diagrams, and quizzes only. The hands-on labs, live grading, and progress tracking require running the real course locally against a Kubernetes cluster.Run it locally โ†’

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 clusterReal EKS
NetworkingCalico overlay โ€” pod IPs from an arbitrary virtual range (192.168.0.0/16), tunneled between nodesVPC CNI โ€” pod IPs from your real VPC subnet via ENIs, routable like any EC2 instance
Ingress / LBTraefik, Service type ClusterIP โ€” no real external addressAWS Load Balancer Controller provisions a real NLB/ALB from Service/Ingress annotations
Storagelocal-path-provisioner โ€” a directory on the node's own diskEBS CSI driver โ€” a real, network-attached EBS volume (or EFS CSI for shared access)
Identity for podsNo cloud IAM โ€” nothing to authenticate toIRSA or Pod Identity โ€” a ServiceAccount maps to a real IAM role
Same Kubernetes API the whole way down โ€” Deployments, Services, NetworkPolicies all mean the same thing. What changes is purely what's implementing them underneath.

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 only

Goal

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

  1. Apply the starting manifests: kubectl apply -f manifests/
  2. Look at the ServiceAccount's annotation: kubectl get serviceaccount payments-api -n lab-31-irsa-pod-identity -o yaml โ€” note the eks.amazonaws.com/role-arn annotation. This part is correct.
  3. 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}'
  4. On a real EKS cluster, a pod running as default has no IRSA role at all โ€” it would fail the moment it tried to call any AWS API. Fix manifests/app.yaml: set serviceAccountName to payments-api.
  5. Re-apply: kubectl apply -f manifests/app.yaml
  6. 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.