Skip to main content

IAM Roles for Service Accounts IRSA Lab

· 4 min read

Provision an OpenID Connect (OIDC) identity provider, map an AWS IAM Role to a Kubernetes Service Account, and verify access using a test Pod.

Prerequisites​

  • An active Amazon EKS cluster.

  • kubectl, eksctl, and aws CLI tools configured and authenticated.


Step 1: Associate an OIDC Provider​

EKS clusters have an OIDC issuer URL. You must associate an IAM OIDC provider with this cluster so IAM can validate the tokens generated by Kubernetes.

Bash

# Replace and with your cluster details
export CLUSTER_NAME="eks-cluster"
export AWS_REGION="us-east-1"

eksctl utils associate-iam-oidc-provider \
--cluster $CLUSTER_NAME \
--region $AWS_REGION \
--approve

``

# Replace and with your cluster details
export CLUSTER_NAME="eks-cluster"
export AWS_REGION="us-east-1"

eksctl utils associate-iam-oidc-provider \
--cluster $CLUSTER_NAME \
--region $AWS_REGION \
--approve

Step 2: Create an AWS IAM Policy​

Create a policy that defines the exact AWS permissions the Pod requires. In this lab, we will grant read-only access to Amazon S3.

Bash

cat < s3-readonly-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": &#91;
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
&#93;,
"Resource": "*"
}
]
}
EOF

# Create the policy and capture the ARN
POLICY_ARN=$(aws iam create-policy \
--policy-name EKSS3ReadOnlyPolicy \
--policy-document file://s3-readonly-policy.json \
--query 'Policy.Arn' \
--output text)

echo $POLICY_ARN

``

cat < s3-readonly-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": &#91;
"s3:ListAllMyBuckets",
"s3:GetBucketLocation"
&#93;,
"Resource": "*"
}
]
}
EOF

# Create the policy and capture the ARN
POLICY_ARN=$(aws iam create-policy \
--policy-name EKSS3ReadOnlyPolicy \
--policy-document file://s3-readonly-policy.json \
--query &#39;Policy.Arn&#39; \
--output text)

echo $POLICY_ARN

Step 3: Create the IAM Role and Kubernetes Service Account​

This step creates an IAM role, attaches the policy from Step 2, and creates a Kubernetes Service Account annotated with the IAM Role ARN. It establishes the trust relationship.

Bash

eksctl create iamserviceaccount \
--name s3-reader-sa \
--namespace default \
--cluster $CLUSTER_NAME \
--region $AWS_REGION \
--attach-policy-arn $POLICY_ARN \
--approve \
--override-existing-serviceaccounts

``

eksctl create iamserviceaccount \
--name s3-reader-sa \
--namespace default \
--cluster $CLUSTER_NAME \
--region $AWS_REGION \
--attach-policy-arn $POLICY_ARN \
--approve \
--override-existing-serviceaccounts

Step 4: Deploy a Test Pod​

Deploy an AWS CLI Pod configured to use the newly created Service Account (s3-reader-sa).

Bash

cat < test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: aws-cli-tester
namespace: default
spec:
serviceAccountName: s3-reader-sa
containers:
- name: aws-cli
image: amazon/aws-cli:latest
command: &#91;"sleep", "3600"&#93;
restartPolicy: Never
EOF

kubectl apply -f test-pod.yaml

``

cat < test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: aws-cli-tester
namespace: default
spec:
serviceAccountName: s3-reader-sa
containers:
- name: aws-cli
image: amazon/aws-cli:latest
command: &#91;"sleep", "3600"&#93;
restartPolicy: Never
EOF

kubectl apply -f test-pod.yaml

Wait for the Pod to be in the Running state:

Bash

kubectl get pods aws-cli-tester -w

``

kubectl get pods aws-cli-tester -w

Step 5: Verify IRSA Configuration​

Exec into the Pod and verify that the AWS CLI is automatically picking up the injected web identity token.

1: Check the environment variables: Kubernetes automatically injects AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE when the Service Account is annotated.

Bash

kubectl exec -it aws-cli-tester -- env | grep AWS

``

kubectl exec -it aws-cli-tester -- env | grep AWS

2: Verify Identity: Check which IAM role the Pod is assuming.

Bash

kubectl exec -it aws-cli-tester -- aws sts get-caller-identity

``

kubectl exec -it aws-cli-tester -- aws sts get-caller-identity

Expected Output: The Arn should reflect the IAM role created in Step 3, not the worker node's IAM role.

3: Test S3 Access:

Bash

kubectl exec -it aws-cli-tester -- aws s3 ls

``

kubectl exec -it aws-cli-tester -- aws s3 ls

Expected Output: A list of S3 buckets in your account.

4: Test Unauthorized Access (Validation): Attempt to create an S3 bucket to ensure the least privilege policy is enforced.

Bash

kubectl exec -it aws-cli-tester -- aws s3 mb s3://my-irsa-test-bucket-12345

``

kubectl exec -it aws-cli-tester -- aws s3 mb s3://my-irsa-test-bucket-12345

Expected Output: AccessDenied error.

Step 6: Teardown​

Remove the resources to avoid any ongoing configuration drift or clutter.

Bash

kubectl delete -f test-pod.yaml

eksctl delete iamserviceaccount \
--name s3-reader-sa \
--namespace default \
--cluster $CLUSTER_NAME \
--region $AWS_REGION

aws iam delete-policy --policy-arn $POLICY_ARN
rm s3-readonly-policy.json test-pod.yaml

``

kubectl delete -f test-pod.yaml

eksctl delete iamserviceaccount \
--name s3-reader-sa \
--namespace default \
--cluster $CLUSTER_NAME \
--region $AWS_REGION

aws iam delete-policy --policy-arn $POLICY_ARN
rm s3-readonly-policy.json test-pod.yaml