{"id":49022,"date":"2025-04-08T02:32:39","date_gmt":"2025-04-08T02:32:39","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=49022"},"modified":"2025-04-08T02:32:39","modified_gmt":"2025-04-08T02:32:39","slug":"aws-tutorial-how-to-give-aws-permissions-to-pods-in-eks","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/aws-tutorial-how-to-give-aws-permissions-to-pods-in-eks\/","title":{"rendered":"AWS Tutorial: How to Give AWS Permissions to Pods in EKS?"},"content":{"rendered":"\n<p>Here&#8217;s a <strong>comprehensive tutorial<\/strong> on how to <strong>assign AWS permissions to Pods running in Amazon EKS<\/strong>, covering:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <strong>problem<\/strong> it solves<\/li>\n\n\n\n<li><strong>All available options<\/strong><\/li>\n\n\n\n<li><strong>Pros and cons<\/strong> of each method<\/li>\n\n\n\n<li>A <strong>comparison table<\/strong><\/li>\n\n\n\n<li><strong>Implementation guide<\/strong> for the recommended ones (IRSA and Pod Identity)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde9 Problem Statement<\/h2>\n\n\n\n<p>In Kubernetes (EKS), your <strong>Pods<\/strong> often need to access AWS resources like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>S3 buckets<\/strong> to read\/write files<\/li>\n\n\n\n<li><strong>DynamoDB<\/strong> to store session data<\/li>\n\n\n\n<li><strong>VPC Lattice<\/strong> for service networking<\/li>\n\n\n\n<li><strong>CloudWatch<\/strong> for logging<\/li>\n<\/ul>\n\n\n\n<p>But <strong>how do we securely give AWS IAM permissions<\/strong> to a Pod?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0d The Core Challenge<\/h2>\n\n\n\n<p>Unlike EC2 instances, <strong>Pods don\u2019t have IAM roles<\/strong> by default. So, we need a secure way to assign IAM permissions to Pods.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Available Options<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Option No.<\/th><th>Method<\/th><th>Recommended<\/th><th>Fine-Grained<\/th><th>Secure<\/th><th>Requires OIDC<\/th><th>Notes<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td>IAM Roles for Service Accounts (IRSA)<\/td><td>\u2705 Yes<\/td><td>\u2705 Yes<\/td><td>\u2705 Yes<\/td><td>\u2705 Yes<\/td><td>Widely used<\/td><\/tr><tr><td>2<\/td><td>EKS Pod Identity (Agent-based)<\/td><td>\u2705 Yes<\/td><td>\u2705 Yes<\/td><td>\u2705 Yes<\/td><td>\u274c No<\/td><td>Newer method<\/td><\/tr><tr><td>3<\/td><td>EC2 Instance IAM Role<\/td><td>\u274c No<\/td><td>\u274c No<\/td><td>\u274c No<\/td><td>\u274c No<\/td><td>All Pods share role<\/td><\/tr><tr><td>4<\/td><td>AWS Credentials in Secrets\/Env Vars<\/td><td>\u274c No<\/td><td>\u2705 Yes<\/td><td>\u274c No<\/td><td>\u274c No<\/td><td>Insecure<\/td><\/tr><tr><td>5<\/td><td>Custom Identity Proxy\/Sidecar Relay<\/td><td>\u26a0\ufe0f Maybe<\/td><td>\u2705 Yes<\/td><td>\u2705 Yes<\/td><td>\u274c No<\/td><td>Complex, flexible<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0e Option 1: IAM Roles for Service Accounts (IRSA)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd27 How it works:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You associate an <strong>IAM Role<\/strong> with a <strong>Kubernetes Service Account<\/strong>.<\/li>\n\n\n\n<li>EKS uses <strong>OIDC (OpenID Connect)<\/strong> to let your Pod assume that IAM role.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee0 Prerequisites:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>EKS Cluster with OIDC provider enabled<\/li>\n\n\n\n<li>IAM Role with trust relationship<\/li>\n\n\n\n<li>Annotated Kubernetes ServiceAccount<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Pros:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Secure &amp; fine-grained<\/li>\n\n\n\n<li>Widely supported in production<\/li>\n\n\n\n<li>Works with <code>eksctl<\/code>, Terraform, AWS CLI<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u274c Cons:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Requires OIDC setup<\/li>\n\n\n\n<li>Slightly more steps<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcd8 IRSA Setup Guide:<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Enable OIDC for your cluster<\/strong> (only once): <code>eksctl utils associate-iam-oidc-provider --cluster &lt;cluster-name> --approve<\/code><\/li>\n\n\n\n<li><strong>Create IAM policy<\/strong> (example for S3 access): <code>aws iam create-policy \\ --policy-name MyAppS3Access \\ --policy-document file:\/\/s3-policy.json<\/code><\/li>\n\n\n\n<li><strong>Create IAM role for ServiceAccount<\/strong>: <code>eksctl create iamserviceaccount \\ --cluster &lt;cluster-name> \\ --namespace &lt;namespace> \\ --name &lt;sa-name> \\ --attach-policy-arn arn:aws:iam::&lt;account-id>:policy\/MyAppS3Access \\ --approve<\/code><\/li>\n\n\n\n<li><strong>Use the ServiceAccount in your deployment<\/strong>: <code>serviceAccountName: &lt;sa-name><\/code><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0e Option 2: EKS Pod Identity<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd27 How it works:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Amazon EKS runs a <strong>Pod Identity Agent<\/strong> on each node.<\/li>\n\n\n\n<li>Your Pod\u2019s <strong>ServiceAccount is annotated<\/strong> with an IAM role.<\/li>\n\n\n\n<li>The agent helps the Pod assume that IAM role \u2014 <strong>no OIDC needed<\/strong>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Pros:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No need to set up OIDC<\/li>\n\n\n\n<li>Easier for beginners<\/li>\n\n\n\n<li>Native integration, evolving fast<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u274c Cons:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Requires Pod Identity Agent (minor extra setup)<\/li>\n\n\n\n<li>Newer, evolving feature<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcd8 Pod Identity Setup Guide:<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Install Pod Identity Agent<\/strong>: <code>eksctl enable pod-identity --cluster &lt;cluster-name><\/code><\/li>\n\n\n\n<li><strong>Create IAM policy<\/strong>: <code>aws iam create-policy \\ --policy-name MyAppS3Access \\ --policy-document file:\/\/s3-policy.json<\/code><\/li>\n\n\n\n<li><strong>Create IAM role and bind to service account<\/strong>: <code>eksctl create iamidentitymapping \\ --cluster &lt;cluster-name> \\ --service-name &lt;sa-name> \\ --namespace &lt;namespace> \\ --arn arn:aws:iam::&lt;account-id>:role\/MyAppS3Access<\/code><\/li>\n\n\n\n<li><strong>Annotate your ServiceAccount<\/strong>: <code>kubectl annotate serviceaccount &lt;sa-name> \\ eks.amazonaws.com\/role-arn=arn:aws:iam::&lt;account-id>:role\/MyAppS3Access \\ -n &lt;namespace><\/code><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0e Option 3: EC2 IAM Role for Node<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee0 How it works:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Attach the IAM role directly to EC2 instance\/node.<\/li>\n\n\n\n<li>All Pods on that node share the same permissions.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u274c Why it\u2019s bad:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Not secure for multi-tenant or microservice workloads.<\/li>\n\n\n\n<li>No fine-grained control \u2014 violates least privilege.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0e Option 4: Hardcoded AWS Credentials<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee0 How it works:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Manually inject <code>AWS_ACCESS_KEY_ID<\/code> and <code>AWS_SECRET_ACCESS_KEY<\/code> into the pod.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u274c Why it\u2019s risky:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Credentials can leak<\/li>\n\n\n\n<li>Hard to rotate and audit<\/li>\n\n\n\n<li>Breaks cloud-native best practices<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd0e Option 5: Custom Sidecar Token Relay<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udee0 How it works:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You run a <strong>sidecar container<\/strong> (e.g., Vault Agent, identity proxy) alongside your app.<\/li>\n\n\n\n<li>The sidecar securely handles IAM calls or credential fetching.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 When useful:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Complex enterprise setups with custom identity management<\/li>\n\n\n\n<li>Using Vault for dynamic credential delivery<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcca Final Comparison Table<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Feature \/ Method<\/th><th>IRSA<\/th><th>Pod Identity<\/th><th>EC2 Role<\/th><th>Env Vars<\/th><th>Sidecar Proxy<\/th><\/tr><\/thead><tbody><tr><td>Secure<\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u274c<\/td><td>\u2705<\/td><\/tr><tr><td>Granular per-Pod IAM Roles<\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u2705<\/td><td>\u2705<\/td><\/tr><tr><td>OIDC Required<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u274c<\/td><td>\u274c<\/td><td>\u274c<\/td><\/tr><tr><td>Easy to Set Up<\/td><td>\u26a0\ufe0f Medium<\/td><td>\u2705 Easy<\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u274c Complex<\/td><\/tr><tr><td>Production-ready<\/td><td>\u2705<\/td><td>\u2705 (new)<\/td><td>\u274c<\/td><td>\u274c<\/td><td>\u26a0\ufe0f Yes if done right<\/td><\/tr><tr><td>AWS-recommended<\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u274c<\/td><td>\u274c<\/td><td>\u274c<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\ude80 Conclusion: What Should You Use?<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>If you&#8217;re&#8230;<\/th><th>Use this method<\/th><\/tr><\/thead><tbody><tr><td>Starting fresh in 2024+<\/td><td>\u2705 Pod Identity<\/td><\/tr><tr><td>Existing setup using IRSA<\/td><td>\u2705 Stick with IRSA<\/td><\/tr><tr><td>Insecure or legacy app<\/td><td>\u274c Avoid Env\/EC2 roles<\/td><\/tr><tr><td>Complex identity control needed<\/td><td>\u26a0\ufe0f Sidecar\/Proxy + Vault<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s a comprehensive tutorial on how to assign AWS permissions to Pods running in Amazon EKS, covering: \ud83e\udde9 Problem Statement In Kubernetes (EKS), your Pods often need to access AWS&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[2],"tags":[],"class_list":["post-49022","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49022","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=49022"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49022\/revisions"}],"predecessor-version":[{"id":49023,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/49022\/revisions\/49023"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=49022"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=49022"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=49022"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}