Claude-skill-registry eks-irsa
IAM Roles for Service Accounts (IRSA) for EKS pod-level AWS permissions. Use when configuring pod IAM access, setting up AWS service integrations, implementing least-privilege security, troubleshooting OIDC trust relationships, or deploying AWS controllers.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/eks-irsa" ~/.claude/skills/majiayu000-claude-skill-registry-eks-irsa && rm -rf "$T"
skills/data/eks-irsa/SKILL.mdEKS IAM Roles for Service Accounts (IRSA)
Overview
Comprehensive guide for implementing IAM Roles for Service Accounts (IRSA) in Amazon EKS. IRSA enables fine-grained IAM permissions at the pod level using OpenID Connect (OIDC) federation, eliminating the need for node-level IAM credentials and enabling least-privilege security.
Keywords: IRSA, IAM Roles for Service Accounts, EKS security, OIDC provider, pod IAM permissions, service account annotations, least privilege, AWS integration, trust policy, cross-account access
Status: Production-ready (2025 best practices)
When to Use This Skill
- Setting up pod-level AWS permissions in EKS
- Configuring AWS service integrations (S3, DynamoDB, Secrets Manager, SQS, SNS)
- Installing EKS add-ons (AWS Load Balancer Controller, EBS CSI Driver, External DNS)
- Implementing least-privilege security architecture
- Troubleshooting OIDC trust relationship issues
- Cross-account IAM role assumption from EKS
- Migrating from node IAM roles to pod-level permissions
- Blue/green cluster upgrades with IRSA
What is IRSA?
IAM Roles for Service Accounts (IRSA) allows Kubernetes workloads to assume IAM roles securely without relying on node-level credentials.
How IRSA Works
1. Pod starts with annotated ServiceAccount 2. EKS mutating webhook injects AWS_WEB_IDENTITY_TOKEN_FILE 3. AWS SDK reads JWT token from injected file 4. SDK calls STS::AssumeRoleWithWebIdentity 5. OIDC provider validates token against trust policy 6. Temporary credentials issued (automatically rotated) 7. Pod uses scoped IAM permissions
Key Benefits
Security:
- Pod-level permissions (not node-level)
- Prevents privilege escalation
- Automatic credential rotation
- No long-lived credentials
Compliance:
- Least-privilege principle
- Audit trail via CloudTrail
- Compliance requirements met
- Fine-grained access control
Operational:
- Each app gets own IAM role
- Modify permissions without node changes
- Better resource isolation
- Supports multi-tenancy
Quick Start
Prerequisites
- OIDC Provider Enabled (automatic with terraform-aws-modules/eks)
- IAM Role Created with trust policy for OIDC
- ServiceAccount Annotated with role ARN
- Pod Configured to use ServiceAccount
30-Second Setup (Terraform)
# 1. Enable IRSA in EKS module (automatic OIDC setup) module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 20.0" cluster_name = "production" enable_irsa = true # Creates OIDC provider automatically } # 2. Create IAM role for S3 access module "s3_access_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" version = "~> 5.0" role_name = "my-app-s3-access" role_policy_arns = { s3_read = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" } oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["production:my-app-sa"] } } } # 3. Create Kubernetes ServiceAccount resource "kubernetes_service_account" "my_app" { metadata { name = "my-app-sa" namespace = "production" annotations = { "eks.amazonaws.com/role-arn" = module.s3_access_irsa.iam_role_arn } } } # 4. Use ServiceAccount in pod resource "kubernetes_deployment" "my_app" { spec { template { spec { service_account_name = "my-app-sa" # ✅ IRSA enabled! containers { name = "app" image = "my-app:latest" # AWS SDK automatically uses IRSA credentials } } } } }
Verify IRSA Setup
# Check OIDC provider exists aws iam list-open-id-connect-providers # Verify IAM role trust policy aws iam get-role --role-name my-app-s3-access # Test from pod kubectl exec -it my-pod -- env | grep AWS # Should show: # AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token # AWS_ROLE_ARN=arn:aws:iam::123456789012:role/my-app-s3-access # Test AWS access kubectl exec -it my-pod -- aws s3 ls
Common IRSA Patterns
1. AWS Load Balancer Controller
What it needs: Create/manage ALBs and NLBs
module "lb_controller_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "aws-load-balancer-controller" attach_load_balancer_controller_policy = true # Pre-built policy! oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:aws-load-balancer-controller"] } } }
2. EBS CSI Driver
What it needs: Create/attach/delete EBS volumes
module "ebs_csi_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "ebs-csi-controller" attach_ebs_csi_policy = true # Pre-built policy! oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] } } }
3. External DNS
What it needs: Manage Route53 records
module "external_dns_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "external-dns" attach_external_dns_policy = true # Pre-built policy! external_dns_hosted_zone_arns = ["arn:aws:route53:::hostedzone/Z123456789"] oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:external-dns"] } } }
4. Cluster Autoscaler
What it needs: Modify Auto Scaling Groups
module "cluster_autoscaler_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "cluster-autoscaler" attach_cluster_autoscaler_policy = true # Pre-built policy! cluster_autoscaler_cluster_names = [module.eks.cluster_name] oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:cluster-autoscaler"] } } }
5. Karpenter (Recommended Autoscaler)
What it needs: Provision EC2 instances, manage instance profiles
module "karpenter" { source = "terraform-aws-modules/eks/aws//modules/karpenter" cluster_name = module.eks.cluster_name irsa_oidc_provider_arn = module.eks.oidc_provider_arn # Includes pre-configured IRSA role! }
6. External Secrets Operator
What it needs: Read secrets from AWS Secrets Manager
module "external_secrets_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "external-secrets" attach_external_secrets_policy = true # Pre-built policy! oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:external-secrets"] } } }
7. Custom Application (S3 + DynamoDB)
module "app_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "my-app" role_policy_arns = { s3 = aws_iam_policy.app_s3_policy.arn dynamodb = aws_iam_policy.app_dynamodb_policy.arn } oidc_providers = { main = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["production:my-app-sa"] } } } # Custom policies with least privilege resource "aws_iam_policy" "app_s3_policy" { name = "my-app-s3-access" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:GetObject", "s3:PutObject" ] Resource = "arn:aws:s3:::my-bucket/my-app/*" } ] }) }
Application Code Examples
Python (Boto3)
import boto3 # AWS SDK automatically detects IRSA credentials # No configuration needed! s3 = boto3.client('s3') response = s3.list_buckets() print(response['Buckets']) # The SDK: # 1. Reads AWS_WEB_IDENTITY_TOKEN_FILE env var # 2. Reads AWS_ROLE_ARN env var # 3. Calls STS::AssumeRoleWithWebIdentity # 4. Uses temporary credentials automatically
Node.js (AWS SDK v3)
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3"; // AWS SDK automatically detects IRSA credentials const s3Client = new S3Client({ region: "us-east-1" }); const response = await s3Client.send(new ListBucketsCommand({})); console.log(response.Buckets);
Go (AWS SDK v2)
import ( "context" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) func main() { // AWS SDK automatically detects IRSA credentials cfg, _ := config.LoadDefaultConfig(context.TODO()) client := s3.NewFromConfig(cfg) resp, _ := client.ListBuckets(context.TODO(), &s3.ListBucketsInput{}) fmt.Println(resp.Buckets) }
Detailed Documentation
For in-depth guides on specific IRSA topics:
-
OIDC Setup: references/oidc-setup.md
- OIDC provider configuration
- Trust relationship anatomy
- Thumbprint calculation
- Blue/green cluster upgrades
-
Role Creation: references/role-creation.md
- IAM role patterns for common services
- Custom policy examples
- Session tags for ABAC
- Cross-account access
-
Pod Configuration: references/pod-configuration.md
- ServiceAccount annotations
- Pod specifications
- Environment variables
- Troubleshooting
Security Best Practices
1. Use Dedicated Service Accounts
# ❌ BAD: Sharing service account apiVersion: v1 kind: ServiceAccount metadata: name: shared-sa # Used by multiple apps annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123:role/shared-role # ✅ GOOD: One service account per app apiVersion: v1 kind: ServiceAccount metadata: name: payment-service-sa annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123:role/payment-service --- apiVersion: v1 kind: ServiceAccount metadata: name: email-service-sa annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123:role/email-service
2. Restrict IMDS Access
# Prevent pods from accessing node IAM credentials module "eks" { source = "terraform-aws-modules/eks/aws" eks_managed_node_groups = { main = { # Require IMDSv2 (prevents container escape to node credentials) metadata_options = { http_endpoint = "enabled" http_tokens = "required" # IMDSv2 only http_put_response_hop_limit = 1 } } } }
3. Least Privilege Policies
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::my-bucket/my-app/*", "Condition": { "StringEquals": { "aws:PrincipalAccount": "123456789012" } } } ] }
4. Regular Auditing
# Find all IRSA roles aws iam list-roles --query 'Roles[?contains(AssumeRolePolicyDocument.Statement[0].Principal.Federated, `oidc-provider`)]' # Check CloudTrail for AssumeRoleWithWebIdentity calls aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \ --max-results 50
Troubleshooting Quick Reference
| Issue | Cause | Fix |
|---|---|---|
| Missing IAM permissions | Check role policy allows action |
| Trust policy mismatch | Verify OIDC provider ARN matches cluster |
| Wrong namespace/SA in trust | Check condition |
| Pod can't assume role | ServiceAccount not annotated | Add annotation |
| Using node credentials | Pod not using ServiceAccount | Set in pod spec |
| OIDC provider not found | IRSA not enabled | Set in EKS module |
eksctl Quick Setup
# Create cluster with OIDC enabled eksctl create cluster \ --name production \ --region us-east-1 \ --with-oidc # Create IRSA role + ServiceAccount in one command eksctl create iamserviceaccount \ --name my-app-sa \ --namespace production \ --cluster production \ --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \ --approve # Verify kubectl get sa my-app-sa -n production -o yaml
Blue/Green Cluster Upgrades
Problem: IRSA trust policies include cluster OIDC endpoint
Solution: Update trust policies during upgrade
# Support both blue and green clusters temporarily resource "aws_iam_role" "app" { assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Federated = [ module.eks_blue.oidc_provider_arn, # Old cluster module.eks_green.oidc_provider_arn # New cluster ] } Action = "sts:AssumeRoleWithWebIdentity" Condition = { StringEquals = { "${module.eks_blue.oidc_provider}:sub" = "system:serviceaccount:prod:app-sa" "${module.eks_green.oidc_provider}:sub" = "system:serviceaccount:prod:app-sa" } } } ] }) }
EKS Pod Identity (New Alternative)
Note: EKS Pod Identity is the new simplified alternative to IRSA (GA 2024).
Differences:
- No OIDC provider needed
- Simpler trust policies
- Managed by AWS entirely
- Recommended for new deployments
IRSA vs Pod Identity:
- IRSA: Proven, mature, widely used (2019+)
- Pod Identity: Simpler, newer, AWS-managed (2024+)
- Both work, Pod Identity is easier for new clusters
Migration Path: Keep IRSA for existing clusters, use Pod Identity for new ones.
Version: 2025 Best Practices Terraform Module:
terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks
Status: Production-ready
Last Updated: November 2025