Containerized workloads running on Amazon EKS frequently need to interact with other AWS services. A pod running a web application might need to read secrets from AWS Secrets Manager. A monitoring agent might push metrics to CloudWatch. An autoscaler like Karpenter needs permissions to launch and terminate EC2 instances. In each case, the pod needs AWS credentials to authenticate its API calls.
The simplest (and least secure) approach is to embed long-lived AWS access keys in the container as environment variables or as mounted secrets. This gets the job done but creates a security risk because: (i) the credentials never expire, (ii) are shared across all pods that use that secret, and (iii) are difficult to rotate without redeploying workloads.
To solve this, AWS introduced IAM Roles for Service Accounts (IRSA) in 2019, which eliminated long-lived credentials by leveraging OpenID Connect (OIDC) federation. The mechanism works as follows. EKS exposes an OIDC provider URL for the cluster, you register that URL as an identity provider in IAM, then configure IAM role trust policies to accept tokens issued by that specific provider for a specific namespace and service account. You also annotate the Kubernetes service account with the IAM role ARN. When a pod starts with that service account, a token is injected into the pod, and the AWS SDK exchanges it for short-lived IAM credentials via STS.
IRSA works, but it introduced operational complexity. Each EKS cluster requires its own OIDC provider registered in IAM. Each IAM role’s trust policy must reference the specific OIDC provider URL and encode the namespace and service account name as conditions. For organizations managing dozens of clusters, this means dozens of OIDC providers and IAM role trust policies that embed cluster-specific URLs. Sharing a single IAM role across multiple clusters requires updating the trust policy with each cluster’s OIDC URL, and IAM trust policies have a hard character limit (4096 characters) that constrains scalability. Additionally, the Kubernetes service account must be annotated with the IAM role ARN, coupling the Kubernetes resource to a specific AWS configuration.
In November 2023, AWS launched EKS Pod Identity as a simpler alternative. Pod Identity removes the need for OIDC providers entirely. Instead, it uses an EKS-managed agent (deployed as a DaemonSet addon on the cluster) that intercepts credential requests from pods and exchanges them for temporary IAM credentials. The trust relationship is configured once with a generic trust policy that allows pods.eks.amazonaws.com to assume the role, and the mapping between IAM role, namespace, and service account is managed through the EKS API using aws_eks_pod_identity_association resources. Hence, this eliminates OIDC providers, cluster-specific trust policy conditions, and service account annotations.
In this article, I’ll explain how EKS Pod Identity works, walk through the Terraform configuration to set it up, and demonstrate the complete flow using the AWS Load Balancer Controller as a real-world example in this EKS cluster.
Solution Overview
This article uses the AWS Load Balancer Controller deployment from the previous articles as the working example. The Pod Identity configuration spans two layers of the multi-configuration Terraform architecture:
Infrastructure configuration (infrastructure/): Installs the Pod Identity Agent addon, creates the IAM role with a Pod Identity trust policy, attaches permissions via an inline policy, and creates the Pod Identity association that maps the role to a specific namespace and service account.
Platform configuration (platform/): Installs the workload (via Helm) that creates the Kubernetes service account matching the association. When the pod starts, the Pod Identity Agent intercepts its credential request and provides temporary IAM credentials.

The implementation follows five steps:
1. Install the Pod Identity Agent addon on the EKS cluster (a DaemonSet that runs on every node).
2. Create the IAM role with a trust policy that allows pods.eks.amazonaws.com to assume it.
3. Attach permissions to the role via an inline policy granting the specific AWS API actions the workload needs.
4. Create the Pod Identity association that links the IAM role to a specific Kubernetes namespace and service account.
5. Deploy the workload with a matching service account via Helm, completing the link from Step 4.
You can find the complete implementation in my GitHub repository: kunduso-org/aws-eks-terraform (branch: platform-components). The code includes Terraform configurations, GitHub Actions CI/CD, and security scanning with Checkov.
Prerequisites
This use case builds on the first article in this series, Provision a Secure Amazon EKS Cluster using Terraform and GitHub Actions. Please go through that before starting here.
Implementation
Step 1: Install the Pod Identity Agent addon
The Pod Identity Agent is an EKS-managed DaemonSet that runs on every node in the cluster. It intercepts credential requests from pods (via the container credentials endpoint) and exchanges them for temporary IAM credentials from STS. Without this addon, Pod Identity associations have no effect.
The addon is installed using the aws_eks_addon resource in the infrastructure folder configuration.

The agent runs in the kube-system namespace and listens on a local endpoint that the AWS SDKs automatically discover. When a pod makes an AWS API call, the SDK contacts this endpoint. The agent then checks whether a Pod Identity association exists for that pod’s service account and, if so, assumes the mapped IAM role and returns temporary credentials.
Step 2: Create the IAM role with a Pod Identity trust policy
Unlike IRSA, which references a cluster-specific OIDC provider URL, Pod Identity uses a single, universal trust policy. The principal is pods.eks.amazonaws.com, and the actions are sts:AssumeRole and sts:TagSession. This same trust policy works across all clusters without modification.

The sts:TagSession action is required because Pod Identity automatically tags the assumed role session with metadata (cluster name, namespace, service account name). These session tags can be used in IAM policy conditions for attribute-based access control (ABAC), allowing a single IAM role to grant different permissions based on which cluster or namespace is making the request.
Step 3: Attach permissions to the role
The IAM role needs the actual permissions the workload requires. For the AWS Load Balancer Controller, this means creating, modifying, and deleting ALBs, NLBs, target groups, listeners, and security groups. I stored the policy as a separate JSON file (lb-controller-iam-policy.json) referenced by an aws_iam_role_policy resource.

This step is identical whether you use IRSA or Pod Identity. The permissions themselves don’t change; only the trust mechanism differs.
Step 4: Create the Pod Identity association
The aws_eks_pod_identity_association resource is the core of Pod Identity. It tells EKS which IAM role to assign to pods running under a specific service account in a specific namespace. Four properties define the mapping: cluster_name, namespace, service_account, and the role_arn to assume.

When a pod starts in the specified namespace with the specified service account, the Pod Identity Agent intercepts its credential request, finds this association, assumes the mapped role, and injects temporary credentials. The pod’s AWS SDK picks them up automatically without any manual configuration on the pod or service account.
Step 5: Deploy the workload with a matching service account
The final piece is the Kubernetes service account itself, created by the Helm chart during workload installation. The service account name (aws-load-balancer-controller) must exactly match the service_account field in the Pod Identity association from Step 4.

With Pod Identity, the service account does not need the eks.amazonaws.com/role-arn annotation required by IRSA. The association in Step 4 is what drives the credential injection, not an annotation on the service account.
Deployment
The infrastructure workflow (terraform.yml) deploys the Pod Identity Agent addon and IAM resources (Steps 1-4). The platform workflow (deploy-platform.yml) installs the Helm chart that creates the service account (Step 5). This is the same two-workflow pattern covered in Article 2.
Validation
To verify that Pod Identity is working, I described the Load Balancer Controller pod and inspected its environment variables:
kubectl describe pod -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller | grep -A 20 "Environment"

The output confirms Pod Identity is active. AWS_CONTAINER_CREDENTIALS_FULL_URI points to the Pod Identity Agent’s local endpoint (http://169.254.170.23/v1/credentials) and AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE references the injected token at /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token. The Mounts section also shows an eks-pod-identity-token projected volume. These tell the AWS SDK to fetch credentials from the Pod Identity Agent rather than using static keys or IRSA’s token exchange.
Conclusion
In this article, I explained how EKS Pod Identity simplifies AWS credential management for Kubernetes pods compared to IRSA. The configuration involves five steps in Terraform: installing the Pod Identity Agent addon, creating an IAM role with a universal trust policy, attaching workload-specific permissions, creating a Pod Identity association that maps the role to a namespace and service account, and deploying the workload with a matching service account. The working example used the AWS Load Balancer Controller to demonstrate the complete flow from IAM role creation to credential injection.
In the next article, I’ll build on this by integrating AWS Secrets Manager with the EKS cluster using the CSI Secrets Store Driver, where Pod Identity provides the authentication mechanism for the secrets driver to read from Secrets Manager.
If you have any questions or suggestions, feel free to comment or get in touch.
One thought on “Configure EKS Pod Identity for Secure AWS Access using Terraform”