<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DevOps With Zack]]></title><description><![CDATA[Kia ora! I'm Arshad Zackeriya. As an AWS Hero, I love diving deep into the world of AWS, DevOps, Kubernetes and Platform Engineering. I used to write Blogs in h]]></description><link>https://blog.awsfanboy.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1741589979016/2d869d7f-e7ae-4992-b131-48c95b90dd59.png</url><title>DevOps With Zack</title><link>https://blog.awsfanboy.com</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 20:56:26 GMT</lastBuildDate><atom:link href="https://blog.awsfanboy.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Boosting Kubernetes Platform Engineering with EKS Capabilities - Chapter 1]]></title><description><![CDATA[Welcome to the first chapter of this series. In this blog post, we are going to explore,

What is kro (Kube Resource Orchestrator)

What is ACK (AWS Controllers for Kubernetes)

EKS capabilities advan]]></description><link>https://blog.awsfanboy.com/boosting-kubernetes-platform-engineering-with-eks-capabilities-chapter-1</link><guid isPermaLink="true">https://blog.awsfanboy.com/boosting-kubernetes-platform-engineering-with-eks-capabilities-chapter-1</guid><category><![CDATA[AWS]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[EKS]]></category><category><![CDATA[KRO]]></category><category><![CDATA[ack]]></category><dc:creator><![CDATA[Arshad Zackeriya]]></dc:creator><pubDate>Mon, 19 Jan 2026 10:03:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767508935306/f466ee9b-9dc6-4f43-bf7f-d4d111ad739d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to the first chapter of this series. In this blog post, we are going to explore,</p>
<ul>
<li><p>What is kro (Kube Resource Orchestrator)</p>
</li>
<li><p>What is ACK (AWS Controllers for Kubernetes)</p>
</li>
<li><p>EKS capabilities advantages in building a platform for developers</p>
</li>
<li><p>How the workload for platform engineers can be reduced (operational efficiency)</p>
</li>
</ul>
<p>The primary focus of this chapter is to cover kro and ACK capabilities and how to deploy an application using ACK and Kubernetes APIs abstracted with kro. In the demo, I'll use a single account setup, but don't worry, later in this series, I will cover multi-account setups and more.</p>
<p>Okay, let me try to simplify kro and ACK. Imagine your Kubernetes cluster is a high-end restaurant kitchen.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768718472941/f9058e51-1d1f-4ffa-9fa4-594a65480825.png" alt="" style="display:block;margin:0 auto" />

<p><strong>The Developers are the Hungry Customers</strong> They don't want to know how the stove works or where you bought the carrots. They just want a meal (an app with a database) delivered to their table, fast.</p>
<p><strong>ACK is the Fully Stocked Pantry</strong> ACK connects your kitchen to the massive warehouse of ingredients (AWS Resources).</p>
<p><strong>kro is the Head Chef’s Recipe Card (The Menu)</strong> kro is the tool the Head Chef (Platform Engineer) uses to combine those raw ingredients into a finished dish. Abstracting API’s in the Kubernetes cluster and creates single unit, reusable API’s for the developers.</p>
<hr />
<h2><strong>What are kro and ACK?</strong></h2>
<h3><strong>ACK (AWS Controllers for Kubernetes)</strong></h3>
<p>ACK allows you to define AWS resources directly as Kubernetes objects. Instead of logging into the AWS Console or running Terraform, you apply a YAML file for an <code>S3Bucket</code> or <code>Table</code> (DynamoDB), and the ACK controller provisions it for you.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767509166196/dcea1319-0a9a-4289-992b-9a1a5ead593c.png" alt="IMG: ACK" style="display:block;margin:0 auto" />

<ul>
<li><p>User creates the Kubernetes manifest files for Kubernetes objects with default api objects, also AWS Resources using ACK.</p>
</li>
<li><p>Not using any IaC tools like <code>Cloudformation</code> or <code>terraform</code> to create AWS resources. Using a Kuberntes manifest file we can create the AWS resources.</p>
</li>
<li><p>In order to do that, we need to give the right IAM permission for the ACK. Don’t worry, we will be revisiting this part with more details later.</p>
</li>
</ul>
<p>Take a look in the below manifest which creates a lambda execution role using ACK. When we deploy the manifest, IAM role will be created in the AWS Account. So simply put, using a Kubernetes manifest we can create AWS Resources without any IaC tools. While this is a simplified example, I will expand into the advanced details later in this post.</p>
<pre><code class="language-yaml">apiVersion: iam.services.k8s.aws/v1alpha1
kind: Role
metadata:
  name: lambda-execution-role
  namespace: default
spec:
  name: lambda-execution-role
  assumeRolePolicyDocument: |
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
  policies:
    - arn:aws:iam::&lt;accountif&gt;:policy/lambda-execution-policy
  tags:
    - key: App
      value: hello-world-lambda
    - key: Environment
      value: test
</code></pre>
<p>Think back to the high-end restaurant kitchen analogy I mentioned earlier, here is how it applies,</p>
<ul>
<li><p>Need a <strong>DynamoDB Table</strong>? ACK brings you a crate of raw potatoes.</p>
</li>
<li><p>Need an <strong>S3 Bucket</strong>? ACK brings you a bag of flour.</p>
</li>
</ul>
<blockquote>
<p><strong>The Problem:</strong> You can't serve a customer a raw potato and a bag of flour. It’s too messy, complicated, and they don't know how to cook it. That is where kro comes into the picture.</p>
</blockquote>
<h3><strong>kro (Kube Resource Orchestrator)</strong></h3>
<p>If ACK provides the ingredients, kro provides the recipe. kro is an open-source project that lets you create custom, single unit, reusable API’s. It acts as the abstracter, bundling multiple resources (like a Deployment, Service, and DynamoDB Table [via ACK API’s]) into a high-level API. kro uses CEL (Common Expression Language), the same language used by Kubernetes webhooks, for logical operations.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767509353236/e18328e8-30de-4c68-a1d1-8cd1bc6354b5.png" alt="" style="display:block;margin:0 auto" />

<ul>
<li><p>kro is an abstracter, it manages groups of resources as a single, reusable custom API.</p>
</li>
<li><p>Building on that same high-end restaurant kitchen analogy,</p>
<ul>
<li><p>You define a recipe called "The Microservice Special".</p>
</li>
<li><p>The recipe says: "Take 1 bag of flour (S3 via ACK), 2 potatoes (DynamoDB via ACK), and cook them with a side of Compute (Deployment)."</p>
</li>
<li><p>The Magic: Now, you provide the customer (Developer) a Menu. They don't order "flour and potatoes"; they just point to "The Microservice Special."</p>
</li>
</ul>
</li>
</ul>
<p><strong>Resource Graph Definition (RGD) Cluster-scoped</strong> :<br />A Resource Graph Definition is a reusable template that defines: A developer-facing schema (API) A resource graph with dependencies. Here's a simplified example showing the core structure:</p>
<pre><code class="language-yaml">apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: webappstack.kro.run
spec:
  schema:
    apiVersion: v1alpha1
    kind: WebAppStack
    spec:
      name: string
      team: string 
      image: string | default="nginx"
      replicas: integer | default=2
      bucket:
        enabled: boolean | default=false
        name: string | default=""
        region: string | default="us-west-2"
    status:
      deploymentStatus: ${deployment.status.conditions}
      bucketStatus: ${s3-bucket.status.ackResourceMetadata.arn}

  resources:
  - id: deployment
    template:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: ${schema.spec.name}
      spec:
        replicas: ${schema.spec.replicas}
        selector:
          matchLabels:
            app: ${schema.spec.name}
        template:
          spec:
            containers:
              - name: ${schema.spec.name}
                image: ${schema.spec.image}              
# ACK S3 Bucket Resource
  - id: s3-bucket
    includeWhen:
      - ${schema.spec.bucket.enabled}
    template:
      apiVersion: s3.services.k8s.aws/v1alpha1
      kind: Bucket
      metadata:
        name: ${schema.spec.bucket.name}
        namespace: ${schema.metadata.namespace}
        labels:
          team: ${schema.spec.team}
      spec:
        name: ${schema.spec.bucket.name}
        createBucketConfiguration:
          locationConstraint: ${schema.spec.bucket.region}
</code></pre>
<p><strong>Highlighted Components:</strong></p>
<ul>
<li><p><strong>Abstraction Layer</strong>: The schema is the developer interface; they don't see Kubernetes complexity</p>
</li>
<li><p><strong>Dependency Resolution</strong>: The platform resolves references</p>
</li>
<li><p><strong>Conditional Resources</strong>: The <code>includeWhen</code> directive shows conditional resource creation based on developer input. As per to the above example, if developer needs an s3bucket simply can set this value to true.</p>
</li>
<li><p><strong>Status Aggregation</strong>: The status section exposes runtime information back to developers</p>
</li>
</ul>
<p><strong>Resource Group Instance (Namespace-scoped):</strong></p>
<p>Developers create instances using the RGD schema. They focus on the desired state, leaving the implementation to the controllers. Developers simply need to apply the Resource Group Instance YAML.</p>
<pre><code class="language-yaml">apiVersion: kro.run/v1alpha1
kind: WebAppStack
metadata:
  name: blog-api
  namespace: production
spec:
  name: blog-api
  team: backend-team
  image: my-registry.io/blog-api:v1.2.3
  replicas: 3
  containerPort: 8080
  ingress:
    enabled: true
  # Request the ACK Bucket
  bucket:
    enabled: true
    name: blog-api-assets-prod-001
</code></pre>
<p><strong>Platform Engineering Benefits:</strong></p>
<ul>
<li><p><strong>Self-Service</strong>: Developers provision infrastructure without platform team tickets</p>
</li>
<li><p><strong>Consistency</strong>: All web apps follow the same patterns and best practices</p>
</li>
<li><p><strong>Governance</strong>: Platform team controls what can be created via the RGD</p>
</li>
<li><p><strong>Abstraction</strong>: Developers think in application terms (replicas, image), not Kubernetes resources</p>
</li>
</ul>
<p>If the RGD is the recipe; the instance is the customer order.</p>
<p>If you came this far you might be thinking “where is EKS Capabilities, all I read is kro and ACK”. Don’t worry, we are getting in to that soon. However, before we jump into the EKS Capabilities, let’s ensure we’re all on the same page regarding the basics of how these two tools function independently.</p>
<hr />
<h2>The Old Way: Self-Managing ACK and kro</h2>
<p>Before EKS Capabilities, platform engineers installed and managed ACK and the kro themselves.</p>
<p><strong>Additional operational burden:</strong></p>
<ul>
<li><p>Running ACK, kro pods</p>
</li>
<li><p>Managing ACK, kro upgrades and compatibility</p>
</li>
</ul>
<p><strong>The Reality:</strong></p>
<p>Platform engineers spent significant time on:</p>
<ul>
<li><p>Controller installation and configuration</p>
</li>
<li><p>Security patches and vulnerability management</p>
</li>
<li><p>Capacity planning for controller workloads</p>
</li>
<li><p>Debugging controller reconciliation loops</p>
</li>
</ul>
<p>This operational overhead diverts resources such as time, from developing new tools and enhancing the platform</p>
<blockquote>
<p>You can watch this episode from The Zacs’ Show, where we share how to install ACK and kro controllers to an EKS cluster: <a href="https://www.youtube.com/watch?v=Fex-xxKTMC4">https://www.youtube.com/watch?v=Fex-xxKTMC4</a></p>
</blockquote>
<h2>The New Way: EKS Capabilities</h2>
<p>EKS Capabilities provides fully managed ACK and KRO, running in AWS-owned infrastructure outside your cluster. It also includes Argo CD for GitOps (to be covered in upcoming posts).</p>
<p><strong>What Changed:</strong></p>
<ol>
<li><p><strong>No In-Cluster Controllers</strong>: ACK and kro run in AWS-managed infrastructure</p>
</li>
<li><p><strong>GitOps Ready</strong>: ArgoCD is included as a managed capability (keep an eye out in future chapters for a detailed explanation)</p>
</li>
<li><p><strong>Automatic Lifecycle Management</strong>: AWS handles scaling, patching, and upgrades</p>
</li>
</ol>
<p>But now:</p>
<ul>
<li><p>No controller pods in your cluster</p>
</li>
<li><p>No manual upgrades or patches</p>
</li>
<li><p>Focus on building platform abstractions</p>
</li>
</ul>
<p><strong>Platform Engineering Benefits:</strong></p>
<ul>
<li><p>No controller management</p>
</li>
<li><p>More time on developer experience</p>
</li>
<li><p>AWS managed security patches</p>
</li>
<li><p>AWS handles availability and scaling</p>
</li>
</ul>
<p>This shift allows platform engineers to concentrate on creating abstractions that empower developers to help themselves, instead of managing the underlying infrastructure tools.</p>
<p>Let's get hands-on.</p>
<hr />
<div>
<div>💡</div>
<div>You can download the code I used for this demo from this repo <a target="_self" rel="noopener noreferrer nofollow" class="text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer" href="https://github.com/awsfanboy/the-eks-platform-shift/tree/main/chapter-1" style="pointer-events:none">https://github.com/awsfanboy/the-eks-platform-shift/tree/main/chapter-1</a>. I will also be using the same repo for this entire series, so for this demo, use the chapter1 folder. If you find any issues or have improvements, please feel free to raise a PR. Thank you for your feedback and contribution.</div>
</div>

<h2>Demo Time: Building a Full-Stack Application with EKS Capabilities</h2>
<p>We'll deploy a full-stack application that demonstrates (kro and ACK) EKS Capabilities in action. The platform team defines a Resource Graph Definition (RGD) that abstracts infrastructure complexity, and the development team provisions their application with a single Kubernetes manifest.</p>
<p><strong>The Scenario:</strong></p>
<ul>
<li><p>Platform team: Creates a reusable Resource Graph Definition (RGD) that sets up a complete application stack.</p>
</li>
<li><p>Development team: Uses the RGD to deploy their application.</p>
</li>
<li><p>Result: A functioning application with AWS resources, Kubernetes workloads, and networking all from one simple manifest.</p>
</li>
</ul>
<p>When you apply the Resource Group Instance, you'll see:</p>
<ol>
<li><p><strong>Automatic Resource Creation</strong>: KRO manages the creation of multiple resources in the correct order.</p>
</li>
<li><p><strong>Dependency Resolution</strong>: Resources that rely on others (like an IAM Role referencing an IAM Policy) are created in sequence.</p>
</li>
<li><p><strong>Status Propagation</strong>: The RGD status fields are automatically filled as resources become available.</p>
</li>
<li><p><strong>Conditional Resources</strong>: The Ingress resource is created only when <code>ingress.enabled: true</code>.</p>
</li>
<li><p><strong>End-to-End Integration</strong>: Your application pods automatically receive AWS credentials through Pod Identity and can access DynamoDB.</p>
</li>
<li><p><strong>Readiness</strong>: Using <code>readyWhen:</code> ensures a resource is fully ready before deployment begins.</p>
</li>
</ol>
<h3>Resources created in this Demo</h3>
<blockquote>
<p>PS: You might notice Feijoa mentioned in this demo. Feijoa is a fruit available in New Zealand 🇳🇿 and it's my favorite fruit too 😛</p>
</blockquote>
<p>Here's the complete inventory of resources that will be provisioned:</p>
<h4><strong>KRO Resources</strong></h4>
<ul>
<li><p><strong>ResourceGraphDefinition</strong> <code>feijoaappstack.kro.run</code> - The platform abstraction template</p>
<ul>
<li>We are going to create a single API called <code>feijoaappstack</code>.</li>
</ul>
</li>
</ul>
<h4><strong>ACK Resources</strong></h4>
<ul>
<li><p><strong>IAM Policy</strong> <code>iam.services.k8s.aws/v1alpha1</code>- Grants DynamoDB permissions</p>
</li>
<li><p><strong>IAM Role</strong> <code>iam.services.k8s.aws/v1alpha1</code> - Assumable by EKS pods</p>
</li>
<li><p><strong>PodIdentityAssociation</strong> <code>eks.services.k8s.aws/v1alpha1</code>- Links IAM role to Kubernetes ServiceAccount</p>
</li>
<li><p><strong>DynamoDB Table</strong> <code>dynamodb.services.k8s.aws/v1alpha1</code> - Application data store</p>
</li>
</ul>
<h4><strong>Kubernetes Native Resources</strong></h4>
<ul>
<li><p><strong>ServiceAccount</strong> - Used by pods for Pod Identity</p>
</li>
<li><p><strong>Deployment</strong> - Application workload with configurable replicas</p>
</li>
<li><p><strong>Service</strong> - ClusterIP service exposing the deployment</p>
</li>
<li><p><strong>PodDisruptionBudget</strong> - Ensures availability during disruptions</p>
</li>
<li><p><strong>IngressClass</strong> - Defines ALB ingress controller (created once)</p>
</li>
<li><p><strong>Ingress</strong> - Application Load Balancer (conditional, only if ingress.enabled: true)</p>
</li>
</ul>
<h2>Demo Steps:</h2>
<h3>Step 1: EKS Cluster</h3>
<ul>
<li>I used an EKS cluster with Auto Mode enabled, therefore, I don’t have to setup the core add-ons and other dependencies. If you are new to EKS Auto Mode check this : <a href="https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode">https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode</a></li>
</ul>
<h3>Step 2: Enable EKS Capabilities</h3>
<ul>
<li><p>There are several ways to do this. In this demo, I used the AWS Console to enable it.</p>
</li>
<li><p>Go to the EKS Console and click on capabilities, and you will see this page.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768363225233/cdd65443-349c-438e-8f22-83361798e903.png" alt="" style="display:block;margin:0 auto" />

<ul>
<li><p>Select the kro and ACK capabilities, then create a Capability role here. For ACK, this role will create the AWS resources for you.</p>
</li>
<li><p>Follow the "Create admin role" instructions to create the capabilities for both ACK and kro.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768363944064/a4bc1271-e3a0-47ed-aceb-be5cf611e478.png" alt="" style="display:block;margin:0 auto" /></li>
</ul>
<blockquote>
<p>In this demo, we will deploy everything in the default namespace. However, in this series, I will also share how to deploy with namespace-specific permissions using <code>IAMRoleSelector</code>. This means we can assign specific IAM roles for each namespace, allowing developers with access to that namespace to deploy resources using a designated IAM role.</p>
</blockquote>
<ul>
<li><p>Having completed these steps, you will see the capabilities are active now.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768364576367/b44c51ce-e771-4bd7-ad82-e692cc374463.png" alt="" style="display:block;margin:0 auto" /></li>
</ul>
<p>Now, if you check the available APIs in the EKS cluster, you will see over 200 available APIs.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768519069271/b9860f9e-567e-4974-9049-aed459f55bab.png" alt="" />

<p>You can see the <code>resourcegraphdefinitions</code> API from kro.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768519175625/a7eecc77-a689-4a43-8735-7c74fe1f8f18.png" alt="" style="display:block;margin:0 auto" />

<p>Also, you can see APIs available for AWS resources from ACK.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768519118860/4b9d1cef-d660-4c39-89b3-95b9bbebc70f.png" alt="" style="display:block;margin:0 auto" />

<p>There are more ;) ,</p>
<h3>Step 4: Configure RBAC for KRO</h3>
<p><strong>Important note:</strong> The kro capability can manage <code>ResourceGraphDefinitions</code> and their instances by default using the <code>AmazonEKSKROPolicy</code>, but it requires additional permissions to handle Kubernetes resources like ACK resources. These permissions need to be granted through access entry policies or Kubernetes RBAC.</p>
<p>For this demo, I am creating a <code>ClusterRoleBinding</code> with the cluster-admin <code>ClusterRole</code>. Ensure that <code>/KRO</code> is in <strong>uppercase</strong>, as the name is case-sensitive. Reference: <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-example">Kubernetes RoleBinding Example</a>. However, in a production environment, you can set more granular access. I will cover a real production setup in future posts in this series, so stay tuned.</p>
<pre><code class="language-yaml">apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kro-cluster-admin
subjects:
- kind: User # Replace the name: with with your IAM role
  name: arn:aws:sts::&lt;account_id&gt;:assumed-role/AmazonEKSCapabilityKRORole/KRO
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
</code></pre>
<h3>Step 5: Create Resource Graph Definition (Platform Team)</h3>
<p>Platform team creates the RGD template that defines the application stack abstraction.</p>
<pre><code class="language-bash"># Apply the Resource Graph Definition
kubectl apply -f chapter-1/platform-team/feijoa-rgd.yaml

# Verify RGD was created
kubectl get resourcegraphdefinition feijoaappstack.kro.run

# Check RGD details
kubectl describe resourcegraphdefinition feijoaappstack.kro.run
</code></pre>
<p>If your RGD is ready, you will see the Status as active same as the below screenshot.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768518547596/1ebfca45-5708-4ef2-b479-65a4c9590caa.png" alt="" style="display:block;margin:0 auto" />

<h3>Step 6: Create Resource Group Instance (Application Team)</h3>
<p>The application team uses the RGD to deploy their application with a single manifest.</p>
<pre><code class="language-bash"># Apply the Resource Group Instance
kubectl apply -f chapter-1/dev-team/feijoa-instance.yml

# Watch resources being created
kubectl get feijoaappstack store -w

# Check instance status
kubectl get feijoaappstack store
</code></pre>
<p>Our Store instance is ready</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768801850140/2dfe6c90-687e-486c-9480-2f272dd962d5.png" alt="" style="display:block;margin:0 auto" />

<h3>Step 7: Verify Resource Creation and Dependencies</h3>
<p>Confirm that all resources were created correctly and that dependencies are resolved.</p>
<p>We can list some of the resources created using <code>kubectl get all, serviceaccount, ingress, pdb, table, roles.iam.services.k8s.aws, policies.iam.services.k8s.aws</code>. The output will look something like this. Now, we can see that all the resources have been successfully created.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768519552846/59c92ffe-0755-4bbe-97ba-e972f1e350c5.png" alt="" style="display:block;margin:0 auto" />

<p>You can use the following commands to check and play around.</p>
<pre><code class="language-bash"># Check RGD status fields are populated
kubectl get feijoaappstack store -o jsonpath='{.status}'

# Verify IAM role ARN is in PodIdentityAssociation
kubectl get podidentityassociation store -o jsonpath='{.spec.roleARN}'

# Verify DynamoDB table ARN in deployment env vars
kubectl get deployment store -o jsonpath='{.spec.template.spec.containers[0].env}'

# Verify ACK resources
kubectl get policy,role
kubectl get table
kubectl get podidentityassociation

# Verify Kubernetes resources
kubectl get deployment,service,serviceaccount
kubectl get ingress
kubectl get poddisruptionbudget

# Check all resources eg:
kubectl get all,serviceaccount,ingress,pdb,table,roles.iam.services.k8s.aws,policies.iam.services.k8s.aws
</code></pre>
<h3>Step 8: Test the Application</h3>
<p>Verify that the application is accessible and can interact with DynamoDB.</p>
<pre><code class="language-bash"># Get Ingress URL
kubectl get ingress store-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
</code></pre>
<p>Now that you have the ALB URL, you can access the app. Try adding some Feijoa fruits to the bucket.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768688782148/3a1ca195-3caf-45ae-80c3-d4e5f8a53897.png" alt="" style="display:block;margin:0 auto" />

<p>We can see that DynamoDB is updating, which means everything is working end-to-end.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768688816330/7ccb8a13-d07c-440d-b6b1-9895f0a48a4d.png" alt="" style="display:block;margin:0 auto" />

<h3>Step 9: Cleanup</h3>
<p>To clean up the demo environment, make sure to delete the cluster resources first before disabling the EKS capabilities.</p>
<pre><code class="language-bash"># Delete the instance (cascades to all resources)
kubectl delete feijoaappstack store

# Verify resources are deleted
kubectl get all,serviceaccount,ingress,pdb -l app=store

# Delete RGD (if needed)
kubectl delete resourcegraphdefinition feijoaappstack.kro.run
</code></pre>
<p>Once the resources are deleted, we can follow <a href="https://docs.aws.amazon.com/eks/latest/userguide/working-with-capabilities.html#_delete_a_capability">these steps</a> to disable the EKS Capabilities from the EKS cluster.</p>
<hr />
<h2>Summary</h2>
<p>In this demo, we have successfully:</p>
<ul>
<li><p>Enabled EKS Capabilities (ACK, kro)</p>
</li>
<li><p>Configured IAM roles and access entries</p>
</li>
<li><p>Set up RBAC for kro capability role</p>
</li>
<li><p>Created a Resource Graph Definition (platform abstraction)</p>
</li>
<li><p>Used the API we created to deploy a complete application stack</p>
</li>
<li><p>Verified all resources and dependencies</p>
</li>
<li><p>Tested the application end-to-end</p>
</li>
</ul>
<h2><strong>Key Takeaway</strong></h2>
<p>From a single developer-friendly manifest, we managed 10 resources across AWS and Kubernetes, showing the strength of platform engineering with EKS Capabilities. With kro, we easily can create reusable single API units to set up multiple instances in a standard way for developers. Also, using ACK we can create the AWS resources and dependencies for the Kubernetes resources.</p>
<h2>References:</h2>
<ul>
<li><p><a href="https://docs.aws.amazon.com/eks/latest/userguide/capabilities.html">https://docs.aws.amazon.com/eks/latest/userguide/capabilities.html</a></p>
</li>
<li><p><a href="https://kro.run/">https://kro.run/</a></p>
</li>
<li><p><a href="https://aws-controllers-k8s.github.io/docs/intro/">https://aws-controllers-k8s.github.io/docs/intro/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Cross Account Access with EKS Pod Identity]]></title><description><![CDATA[Before we dive into Cross Account Pod Identity support, let's review some history. In the early days of EKS, if a pod needed to access another AWS resource, it had to use the Node IAM role. For example, if an app running in a pod needed to access an ...]]></description><link>https://blog.awsfanboy.com/cross-account-access-with-eks-pod-identity</link><guid isPermaLink="true">https://blog.awsfanboy.com/cross-account-access-with-eks-pod-identity</guid><category><![CDATA[EKS]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[IAM]]></category><dc:creator><![CDATA[Arshad Zackeriya]]></dc:creator><pubDate>Sun, 29 Jun 2025 20:21:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751228350825/28a47e14-1379-454d-99ce-3e1f4f543b10.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Before we dive into Cross Account Pod Identity support, let's review some history. In the early days of EKS, if a pod needed to access another AWS resource, it had to use the Node IAM role. For example, if an app running in a pod needed to access an S3 bucket, the Node IAM role had to have the right IAM policy to access that specific S3 bucket.</p>
<p>However, this raised security concerns. Many of us, including myself, used <code>KIAM</code> and <code>kube2iam</code> to solve this problem by allowing granular access for Kubernetes applications. But managing these proxies wasn't easy either.</p>
<p>Then AWS IRSA (IAM roles for service accounts) simplified this complexity. However, for larger organizations with multiple AWS accounts, this also became a challenge because managing OIDC providers isn't easy. Before we dive in to EKS Pod Identity here is a simple understanding of KIAM, kube2iam and AWS IRSA.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Tool/Method</strong></td><td><strong>Approach</strong></td><td><strong>Key Characteristics</strong></td></tr>
</thead>
<tbody>
<tr>
<td>KIAM</td><td>Proxy-based</td><td>Intercepts metadata API calls. Requires a central server and node agents.</td></tr>
<tr>
<td>kube2iam</td><td>Proxy-based</td><td>Similar to KIAM, intercepts metadata API calls.</td></tr>
<tr>
<td>AWS IRSA</td><td>OIDC Federation</td><td>Native AWS integration with EKS. Associates IAM roles directly with service accounts.</td></tr>
</tbody>
</table>
</div><h3 id="heading-eks-pod-identity-launch">EKS Pod Identity Launch</h3>
<p>In 2023, AWS announced the launch of EKS Pod Identity, which simplifies the configuration of Kubernetes applications to obtain AWS IAM permissions. Unlike AWS IRSA, with EKS Pod Identity, we don't need the OIDC Provider dependency. You just need to install the EKS Pod Identity add-on and create the Pod Identity association. The diagram below explains the simple setup of EKS Pod Identity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751173253180/8d87e34b-83ed-46d8-8c72-6c874bd4c7b4.png" alt class="image--center mx-auto" /></p>
<p>However, there were still some limitations with cross-account setup. There wasn't a direct way to do it, so we had to use some workarounds. We appreciate that AWS is great at listening to their customers.</p>
<p><a target="_blank" href="https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-streamlines-cross-account-access/">Finally, AWS announced improvements for EKS Pod Identity cross-account access.</a></p>
<h3 id="heading-internals-of-cross-account-eks-pod-identity">Internals of Cross-Account EKS Pod Identity</h3>
<p>Let's break down how this works. In this scenario, Pods running in the EKS cluster within the Developer account can easily access resources in the Infrastructure account. This is done by having the IAM role in the Developer account assume a role in the Infrastructure account. It's as simple as that!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751173573042/65856c7e-b265-407e-9814-f04c70747b89.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-lets-explore-two-scenarios-in-this-demo">Let's explore two scenarios in this demo:</h3>
<ul>
<li><p>As a Platform Engineer, Set up the external-dns add-on to create DNS records in Route53 across accounts.</p>
</li>
<li><p>As a developer, learn how to access parameter store values from a different account.</p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites:</h3>
<ul>
<li><p>EKS Auto Mode Cluster with built-in EKS Pod Identity add-on. If you want to learn how to set up a EKS Auto Mode cluster, check my previous blog <a target="_blank" href="https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode">here</a>.</p>
</li>
<li><p>Two AWS Accounts</p>
</li>
<li><p>Domain name configured in Route53</p>
</li>
<li><p>Optional: ACM Certificate</p>
</li>
</ul>
<h2 id="heading-scenario-1-external-dns-cross-account-access">Scenario 1: External DNS Cross-Account Access</h2>
<p>We have an EKS Cluster running in the Developer Account, and the External DNS pods use the EKS Pod Identity to create DNS records in the Infrastructure Account.We already have a domain set up in Route53 within the Infrastructure Account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751162606231/c93c79dd-0a3d-4e3b-a073-18b4d52dd666.png" alt class="image--center mx-auto" /></p>
<p>Now let's create the IAM role and policies.</p>
<h3 id="heading-infrastructure-account-iam"><strong>Infrastructure Account IAM</strong></h3>
<p>Include the IAM role ARN that we will create later in the developer account. Also, include the <code>sts:ExternalId</code> for more granular access..</p>
<p>Format for the <code>sts:ExternalId</code></p>
<p><code>{AWS_Region}/{AWS_AccountNumber}/{EKS_Cluster_Name}/{NameSpace}/{ServiceAccountName}</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751162982108/95c7181d-5cbf-47e5-88d8-5276afba0a49.png" alt class="image--center mx-auto" /></p>
<p>For demonstration purposes, we are granting <code>AmazonRoute53FullAccess</code> access.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751163471636/daefdf51-dc03-4121-bb1d-2c09800a8c9b.png" alt class="image--center mx-auto" /></p>
<p>Create the IAM Role</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751163412920/4dd9d1eb-8a3a-4747-b784-f3b4f9efaccc.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-developer-account-iam">Developer Account IAM</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751161835657/833d950d-4ed5-4c7e-877e-51e18f9fba88.png" alt class="image--center mx-auto" /></p>
<p>Skip adding permissions and create the IAM Role.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751161927910/d64a2dfd-e5d6-49c3-8d22-0187ef59961f.png" alt class="image--center mx-auto" /></p>
<p>Go to the created IAM role and create an inline policy. Include the IAM role ARN that we previously created in the Infrastructure Account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751163642238/cd6ba7f8-102b-4878-93e3-d1e579a7744e.png" alt class="image--center mx-auto" /></p>
<p>Create the policy</p>
<p>Install and configure the External DNS Addon in the EKS Cluster within the Developer Account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751163767798/4b2176e3-a892-4507-8730-0a746a84c1c1.png" alt class="image--center mx-auto" /></p>
<p>Select External DNS</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751163788535/6657bc2c-5442-444a-8203-52cf94189f95.png" alt class="image--center mx-auto" /></p>
<p>Select the EKS Pod Identity and choose the IAM Role we created. The External DNS add-on will be ready in a few minutes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751232643560/5c519a0c-0583-4635-af91-2e385695655a.png" alt class="image--center mx-auto" /></p>
<p>We needed to make one final configuration: edit the EKS Pod Identity Association for the External DNS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751166490622/d23de8da-1311-473f-8833-a126c2c27822.png" alt class="image--center mx-auto" /></p>
<p>Add the IAM role from the Infrastructure Account in the <strong>Target IAM Role</strong> section.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751166616663/fd386207-149f-4154-99e4-fe8e1c7082a6.png" alt class="image--center mx-auto" /></p>
<p>Now it's time to deploy a test application to check the External DNS Cross Account setup.</p>
<p>Here is the sample Kubernetes manifest we used for the application deployment:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">doggo-app</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">doggo-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">doggo-app</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">doggo-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">doggo-app</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">awsfanboy/doggo-app:latest</span>
        <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">doggo-app-service</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">NodePort</span>
  <span class="hljs-attr">ports:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">targetPort:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">doggo-app</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">IngressClass</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">eks-auto-alb</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">controller:</span> <span class="hljs-string">eks.amazonaws.com/alb</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">doggo-app-ingress</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">alb.ingress.kubernetes.io/target-type:</span> <span class="hljs-string">ip</span>
    <span class="hljs-attr">alb.ingress.kubernetes.io/scheme:</span> <span class="hljs-string">internet-facing</span>
    <span class="hljs-attr">alb.ingress.kubernetes.io/certificate-arn:</span> <span class="hljs-string">arn:aws:acm:ap-southeast-2:xxxxxxxxxx:certificate/{ACM}</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">ingressClassName:</span> <span class="hljs-string">eks-auto-alb</span>
  <span class="hljs-attr">rules:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">host:</span> <span class="hljs-string">app.infra.awsfanboy.com</span>
    <span class="hljs-attr">http:</span>
      <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/</span>
        <span class="hljs-attr">pathType:</span> <span class="hljs-string">Prefix</span>
        <span class="hljs-attr">backend:</span>
          <span class="hljs-attr">service:</span>
            <span class="hljs-attr">name:</span> <span class="hljs-string">doggo-app-service</span>
            <span class="hljs-attr">port:</span>
              <span class="hljs-attr">number:</span> <span class="hljs-number">80</span>
</code></pre>
<p>Deploy the application</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751166757692/07288b90-3825-495f-b2bf-88e016958834.png" alt class="image--center mx-auto" /></p>
<p>List the ingress object</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751166800398/8f4e5ce4-7a0f-4257-bbd2-db71c694ec91.png" alt class="image--center mx-auto" /></p>
<p>Logs of the <code>external-dns</code> pod show that the DNS records were successfully created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751166984061/64b0c99c-c93b-4c6e-9a94-3ac43721a5a9.png" alt class="image--center mx-auto" /></p>
<p>Route53 records were created successfully.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751167084255/461a63a4-4746-4835-8d68-baa0eae6346f.png" alt class="image--right mx-auto mr-0" /></p>
<p>App is running successfully</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751167151037/e080da98-557d-4671-8a06-a0775ee5d7e1.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-scenario-2-app-deployed-on-eks-cluster-with-ssm-parameter-access-from-infrastructure-account">Scenario 2: App deployed on EKS Cluster with SSM Parameter access from Infrastructure account</h2>
<p>We already have a parameter in SSM within the Infrastructure Account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751169075680/d099f4c3-d365-45ff-84e8-eb648ed7b727.png" alt class="image--center mx-auto" /></p>
<p>Now, let's create the IAM role and policies.</p>
<h3 id="heading-infrastructure-account-iam-1"><strong>Infrastructure Account IAM</strong></h3>
<p>We are following the same steps we used for the External DNS add-on. Below are the screenshots for the Trust Relationship policy, IAM Policy of the the IAM Role to access the SSM Parameter store.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751169224932/57015da7-69f3-45c6-b01b-23927410ef8c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751169284831/4ce91145-8485-4acc-820f-bca72d276756.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-developer-account-iam-1"><strong>Developer Account IAM</strong></h3>
<p>We are following the same steps we used for the External DNS add-on. Below are the screenshots for the Trust Relationship policy, IAM Policy of the the IAM Role to assume the IAM role in the Infrastructure Account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751169357007/7c6ddaaf-2660-4f88-be83-fe18a441938c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751169414107/c1b94b36-4158-40d0-99b9-3d1d6e79cff3.png" alt class="image--center mx-auto" /></p>
<p>Create the EKS Pod Identity Association. Later, we are deploying a sample app into the default namespace with a <code>Service Account</code> named <code>awsfanboy-sa</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751169969127/3bbb8d34-ac73-4e36-a46b-17e77aefda70.png" alt class="image--center mx-auto" /></p>
<p>Sample Kubernetes Manifest we used for deploying the sample application</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceAccount</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">awsfanboy-sa</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">awsfanboy</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">awsfanboy</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">awsfanboy</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">awsfanboy</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">serviceAccountName:</span> <span class="hljs-string">awsfanboy-sa</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">awscli</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">amazon/aws-cli:latest</span>
        <span class="hljs-attr">command:</span> [<span class="hljs-string">"sleep"</span>]
        <span class="hljs-attr">args:</span> [<span class="hljs-string">"infinity"</span>]
</code></pre>
<p>Let's deploy the sample app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751170059789/e48d5237-b99a-4b65-afb7-2ba9d204f970.png" alt class="image--center mx-auto" /></p>
<p>Now let's access the pod and run some commands to check the cross-account access.</p>
<ul>
<li><p><code>aws sts get-caller-identity</code>: This shows that the identity is using the IAM role created in the Infrastructure Account.</p>
</li>
<li><ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751170365174/2df5e26f-70b7-465d-b467-8742573704ea.png" alt class="image--center mx-auto" /></li>
</ul>
</li>
<li><p><code>aws ssm get-parameter --name &lt;parameter_name&gt;</code> The pod can access the parameter from the SSM Parameter Store in the Infrastructure Account.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751170572543/c7596092-0bad-44ed-9e21-db6e1c43025b.png" alt class="image--center mx-auto" /></li>
</ul>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>We can clearly see that this new enhanced and streamlined way of EKS Pod Identity for cross-account access can be configured within a few seconds, reducing the workload for the EKS platform team and developers.</p>
<h2 id="heading-reference">Reference</h2>
<ul>
<li><p><a target="_blank" href="https://aws.amazon.com/blogs/aws/amazon-eks-pod-identity-simplifies-iam-permissions-for-applications-on-amazon-eks-clusters/">https://aws.amazon.com/blogs/aws/amazon-eks-pod-identity-simplifies-iam-permissions-for-applications-on-amazon-eks-clusters/</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/about-aws/whats-new/2023/11/amazon-eks-pod-identity/">https://aws.amazon.com/about-aws/whats-new/2023/11/amazon-eks-pod-identity/</a></p>
</li>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html">https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-streamlines-cross-account-access/">https://aws.amazon.com/blogs/containers/amazon-eks-pod-identity-streamlines-cross-account-access/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[AWS CodePipeline now includes native support for deploying to Amazon EKS.]]></title><description><![CDATA[Previously, when using AWS CodePipeline for Amazon EKS deployment, you had to create a CodeBuild project to install kubectl or helm and manage the deployment. Now, with this recent update, AWS CodePipeline natively supports EKS deployment. The best p...]]></description><link>https://blog.awsfanboy.com/aws-codepipeline-now-includes-native-support-for-deploying-to-amazon-eks</link><guid isPermaLink="true">https://blog.awsfanboy.com/aws-codepipeline-now-includes-native-support-for-deploying-to-amazon-eks</guid><category><![CDATA[AWS]]></category><category><![CDATA[EKS]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[CodePipeline]]></category><category><![CDATA[aws-codebuild]]></category><category><![CDATA[cicd]]></category><dc:creator><![CDATA[Arshad Zackeriya]]></dc:creator><pubDate>Sun, 09 Mar 2025 08:27:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741483197975/9c02a000-3b96-48bb-8e5a-672c9eadd1a3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Previously, when using AWS CodePipeline for Amazon EKS deployment, you had to create a CodeBuild project to install <code>kubectl</code> or <code>helm</code> and manage the deployment. Now, with this recent update, AWS CodePipeline natively supports EKS deployment. The best part is that it supports both public and private cluster endpoints. For EKS clusters with private endpoints, you don't need to make any network changes; providing the necessary permissions to the CodePipeline service role is sufficient.</p>
<p>Official announcement: <a target="_blank" href="https://aws.amazon.com/about-aws/whats-new/2025/02/aws-codepipeline-native-amazon-eks-deployment-support/">https://aws.amazon.com/about-aws/whats-new/2025/02/aws-codepipeline-native-amazon-eks-deployment-support/</a></p>
<blockquote>
<p>In this article, I will cover not only the Deploy stage but also discuss an end-to-end pipeline. Feel free to skip the Build section if you are already familiar with those services.</p>
</blockquote>
<h2 id="heading-lets-begin-with-setting-up-the-eks-cluster">Let's begin with setting up the EKS Cluster</h2>
<p>We need an EKS Cluster to test this out. Also, a note on; if the API server endpoint access is private, ensure the private subnets should be able to access internet via a NAT Gateway. Therefore, make sure the Route tables are configured correctly.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741383998825/77e76320-2cd9-4e47-9c81-88c6a4955f9d.png" alt class="image--center mx-auto" /></p>
<p>In this Demo, my EKS Cluster API server endpoint access is private, I have created an inbound rule in the <code>Cluster security group</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741384198990/1362fdce-f119-402a-9582-e93c37f45af6.png" alt class="image--center mx-auto" /></p>
<p>You can follow my <a target="_blank" href="https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode">blog</a> to create an EKS cluster with Auto Mode within few minutes.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode">https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode</a></div>
<p> </p>
<h2 id="heading-creating-the-ecr-repository">Creating the ECR Repository</h2>
<p>For this demo, we need a container registry to store the container image that we will build during the build stage. Later, we will deploy this image to the EKS cluster. You can use the following simple Terraform script to create the registry or create it directly through the console.</p>
<pre><code class="lang-json">terraform {
  required_providers {
    aws = {
      source  = <span class="hljs-attr">"hashicorp/aws"</span>
      version = <span class="hljs-attr">"~&gt; 5.0"</span>
    }
  }
}

resource <span class="hljs-string">"aws_ecr_repository"</span> <span class="hljs-string">"doggo_app_ecr"</span> {
  name                 = <span class="hljs-attr">"doggo-app-ecr"</span>
  image_tag_mutability = <span class="hljs-attr">"MUTABLE"</span>
  image_scanning_configuration {
    scan_on_push = true
  }
}

output <span class="hljs-string">"ecr_repository_uri"</span> {
  value       = aws_ecr_repository.doggo_app_ecr.repository_url
  description = <span class="hljs-attr">"The URI of the ECR repository"</span>
}
</code></pre>
<h2 id="heading-aws-codepipeline-creation-and-configuration">AWS CodePipeline Creation and Configuration</h2>
<p>Create a Pipeline from the scratch, follow the steps as per to my screenshots.</p>
<p>Creation option, select <code>Build custom pipeline</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741427675076/496a5c70-4477-4d64-9ee7-b9cdfaf6ed30.png" alt class="image--center mx-auto" /></p>
<p>For this demo, under the <code>Service role</code>, create a <code>New service role</code>. Since we are using an EKS cluster with a private API server endpoint, we will need to update this roles policy later to allow access to read the EKS cluster's private subnets. You can proceed for now; we will handle this step later.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741427602142/f72a01ed-7824-4186-b165-d3e43bdc8cf9.png" alt class="image--center mx-auto" /></p>
<p>A quick note before we start the source stage: I have created this repository <a target="_blank" href="https://github.com/awsfanboy/doggo-container-app">https://github.com/awsfanboy/doggo-container-app</a> with all the resources needed for this demo. Make sure to update the <code>values.yaml</code> file with the image URL.</p>
<blockquote>
<p>This Helm chart is ready for an EKS Auto Mode cluster. If your cluster is not enabled for EKS Auto Mode, you will need to install add-ons like the <strong>ALB Ingress Controller</strong>.</p>
</blockquote>
<pre><code class="lang-yaml"><span class="hljs-attr">image:</span>
  <span class="hljs-attr">repository:</span> { <span class="hljs-string">Replace</span> <span class="hljs-string">with</span> <span class="hljs-string">your</span> <span class="hljs-string">ECR</span> <span class="hljs-string">repository</span> <span class="hljs-string">URL</span>}
 <span class="hljs-comment"># example,  repository: 11111111111.dkr.ecr.ap-southeast-2.amazonaws.com/doggo-ap-ecr</span>
  <span class="hljs-attr">tag:</span> <span class="hljs-string">latest</span>
  <span class="hljs-attr">pullPolicy:</span> <span class="hljs-string">Always</span>
</code></pre>
<p>Set up the source stage by connecting your GitHub repository or any other repository you plan to use.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741645980065/043e3955-eaea-4e6a-988d-98631baece65.png" alt class="image--center mx-auto" /></p>
<p>In the build stage, choose <code>Other build providers</code> and select <code>AWS ECR</code>. Then, select the ECR repository name from the dropdown menu.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741646073091/2974992e-683c-4983-bdc6-15afa5862ecf.png" alt class="image--center mx-auto" /></p>
<p>Skip the Test stage for this demo and proceed to the deploy stage.</p>
<p>So far, what we've done isn't new, and I'm sure you're familiar with the previous steps. However, I wanted to explain each step for this demo.</p>
<p>Deploy stage, let’s check these fields now.</p>
<ul>
<li><p>Select the deploy provider as <code>Amazon EKS</code>.</p>
</li>
<li><p>Since we have already created an EKS cluster in the same region, you can select the cluster from the dropdown.</p>
</li>
<li><p>For <code>Deployment configuration type</code>, choose either <code>helm</code> or <code>kubectl</code>. In this demo, we are using a Helm chart to deploy.</p>
</li>
<li><p>Provide the <code>release name</code> and Helm chart location.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741429197942/4aacd365-1288-435a-9674-4b71203da80d.png" alt class="image--center mx-auto" /></p>
<p>Thats all for this and create the pipeline.</p>
<blockquote>
<p>The pipeline will fail because there are more steps to follow later, but don't worry about it. Once we complete the setup, we can rerun the pipeline.</p>
</blockquote>
<h2 id="heading-cluster-access-from-codepipeline">Cluster access from CodePipeline</h2>
<p>This is really an important part, let’s breakdown the changes that we are going to do.</p>
<ul>
<li>Since we are using an EKS cluster with a private API server endpoint, we need to update the existing CodePipeline role to grant permissions for <code>ec2:CreateNetworkInterface</code>, <code>ec2:CreateNetworkInterfacePermission</code>, <code>ec2:DeleteNetworkInterface</code>, and some <code>ec2:Describe</code> permissions.</li>
</ul>
<blockquote>
<p><strong><em><mark>If your cluster endpoint is public, you can skip this step.</mark></em></strong></p>
</blockquote>
<ul>
<li>The CodePipeline service role needs to authenticate and authorize access permissions through <code>EKS access entries</code>. Additionally, the CodePipeline service role should have the <code>eks:DescribeCluster</code> permission to access the cluster.</li>
</ul>
<h3 id="heading-update-the-codepipeline-service-role">Update the CodePipeline service role</h3>
<p>Find your CodePipeline service role from the IAM roles and edit the existing role.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741646590224/00db01d2-b4e6-4930-b5c9-98871b0d44fa.png" alt class="image--center mx-auto" /></p>
<p>Update the existing policy by adding the following policy document. Make sure to replace the Subnet ARNs and EKS Cluster ARN.</p>
<p>You can find the subnets in the Networking tab of the EKS cluster console.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741478175633/514f50ea-31f8-4771-8876-96322b2d4eaf.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-json">
            <span class="hljs-string">"Sid"</span>: <span class="hljs-string">"EksClusterPolicy"</span>,
            <span class="hljs-string">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-string">"Action"</span>: <span class="hljs-string">"eks:DescribeCluster"</span>,
            <span class="hljs-string">"Resource"</span>: <span class="hljs-string">"arn:aws:eks:ap-southeast-2:111111111111:cluster/awsfanboy-dev"</span>
        },
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"EksVpcClusterPolicy"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"ec2:DescribeDhcpOptions"</span>,
                <span class="hljs-string">"ec2:DescribeNetworkInterfaces"</span>,
                <span class="hljs-string">"ec2:DescribeRouteTables"</span>,
                <span class="hljs-string">"ec2:DescribeSubnets"</span>,
                <span class="hljs-string">"ec2:DescribeSecurityGroups"</span>,
                <span class="hljs-string">"ec2:DescribeVpcs"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: [
                <span class="hljs-string">"*"</span>
            ]
        },
        {
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"ec2:CreateNetworkInterface"</span>,
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>,
            <span class="hljs-attr">"Condition"</span>: {
                <span class="hljs-attr">"StringEqualsIfExists"</span>: {
                     <span class="hljs-attr">"ec2:Subnet"</span>: [
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0c147f0e842c43726"</span>,
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-068e82a6b56cc1742"</span>,
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0b12ab5e6baf6028d"</span>
                    ]
                }
            }
        },
        {
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"ec2:CreateNetworkInterfacePermission"</span>,
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>,
            <span class="hljs-attr">"Condition"</span>: {
                <span class="hljs-attr">"ArnEquals"</span>: {
                    <span class="hljs-attr">"ec2:Subnet"</span>: [
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0c147f0e842c43726"</span>,
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-068e82a6b56cc1742"</span>,
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0b12ab5e6baf6028d"</span>
                    ]
                }
            }
        },
        {
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"ec2:DeleteNetworkInterface"</span>,
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>,
            <span class="hljs-attr">"Condition"</span>: {
                <span class="hljs-attr">"StringEqualsIfExists"</span>: {
                     <span class="hljs-attr">"ec2:Subnet"</span>: [
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0c147f0e842c43726"</span>,
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-068e82a6b56cc1742"</span>,
                        <span class="hljs-string">"arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0b12ab5e6baf6028d"</span>
                    ]
                }
            }
        }
</code></pre>
<h3 id="heading-update-the-codepipeline-service-role-to-access-ecr">Update the CodePipeline service role to access ECR.</h3>
<p>Add the <code>AmazonEC2ContainerRegistryPowerUser</code> AWS managed policy to the CodePipeline service role so that AWS ECR build provider can push the container image to the ECR.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741646297004/e9bdfebc-4bf8-4f4e-8261-42a2ad3ff969.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-eks-cluster-authentication-and-authorization">EKS Cluster Authentication and Authorization</h3>
<p>We can create this easily via EKS access entries in the EKS Cluster console</p>
<p>Create access entry</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741478519607/4e19b7a0-e091-4454-bbc4-bd129c2e6f2b.png" alt class="image--center mx-auto" /></p>
<p>Search for your CodePipeline service role and select it, then keep the type as <code>Standard</code> and click Next.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741478567847/a78145a3-096c-4823-9470-5054e13d493e.png" alt class="image--center mx-auto" /></p>
<p>Now we need to attach the policy to authorize permissions for the CodePipeline service role. For this demo, I am using <code>AmazonEKSClusterAdminPolicy</code>. Keep the Access scope as Cluster, then click <code>Add policy</code>, followed by clicking Next to create the access entry.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741478642749/36ba36c3-f647-46de-92da-00969a9d65ae.png" alt class="image--center mx-auto" /></p>
<p>That’s it now we can go ahead and rerun the pipeline.</p>
<p>Run the Pipeline</p>
<p>Navigate to the pipeline and click release pipeline.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741479359266/d92cadfb-d264-4a57-9880-37a9752663da.png" alt class="image--center mx-auto" /></p>
<p>If all good, you will see your pipeline executed successfully.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741646410426/393f3ccf-e6cd-4481-b898-225636a19a77.png" alt class="image--center mx-auto" /></p>
<p>You can expand the deploy action and check the logs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741480052756/6a61332d-e14e-4cce-8086-9e57bf3981cd.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-verify-the-deployment">Verify the deployment</h3>
<p>Since this is a private cluster, I am using the CloudShell with a VPC environment , where which is created in the same VPC where I have deployed the EKS cluster. So I can run <code>kubectl</code> and <code>helm</code> commands. We don’t have to install <code>kubectl</code> but <code>helm</code>. Once done update the <code>kubeconfig</code></p>
<pre><code class="lang-bash">~ $ aws eks update-kubeconfig --name awsfanboy-dev
</code></pre>
<p>Install helm:</p>
<pre><code class="lang-bash">curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 &gt; get_helm.sh
chmod 700 get_helm.sh
./get_helm.sh
</code></pre>
<p>List the Helm release and get the ingress URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741480308067/bc07e42b-17f7-4b0c-bec4-5b7645d64ab1.png" alt class="image--center mx-auto" /></p>
<p>You can also list the Ingress URL from the EKS console by navigating to the Resources tab and selecting the Ingress object.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741480396678/5df5301e-671e-4019-9f68-f4788649a632.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741480452443/087b050e-b190-458d-a267-01e1e2a82086.png" alt class="image--center mx-auto" /></p>
<p>Copy the Load Balancer URL and access it from your browser. You should see that our Doggo App Version 1 is running.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741480515691/bf88c7e4-744b-402f-9a6c-4034bb966851.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-eager-to-learn-and-curious-about-the-deployment-action-for-a-private-cluster-i-looked-at-the-cloudtrail-logs-and-found-the-trail-logs-where-useridentity-from-codepipeline-appeared-for-networkinterface-events">Eager to learn and curious about the deployment action for a private cluster, I looked at the CloudTrail logs and found the trail logs where <code>userIdentity</code> from CodePipeline appeared for <code>NetworkInterface</code> events.</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741480766513/73bfd451-a066-4bbb-8205-4d794bf98174.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Previously, for EKS deployment, we had to use CodeBuild with a lot of configurations to manage Kubernetes deployments. But now, with native CodePipeline support, we can easily and directly deploy to EKS clusters. My favourite part is that we can deploy to an EKS cluster with a private endpoint without any customizations, except for the IAM policy, which is really awesome.</p>
<p>If you have any questions or comments, please let me know.</p>
]]></content:encoded></item><item><title><![CDATA[Simplify IP Utilization Monitoring in Subnets with Serverless Automation]]></title><description><![CDATA[I want to begin with saying that Amazon Q developer and AWS Infrastructure Composer helped me to design this solution in a matter of minutes.
Amazon Q: https://aws.amazon.com/q/ AWS Infrastructure Composer: https://aws.amazon.com/infrastructure-compo...]]></description><link>https://blog.awsfanboy.com/simplify-ip-utilization-monitoring-in-subnets-with-serverless-automation</link><guid isPermaLink="true">https://blog.awsfanboy.com/simplify-ip-utilization-monitoring-in-subnets-with-serverless-automation</guid><category><![CDATA[AWS]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[aws-cdk]]></category><category><![CDATA[aws sam]]></category><category><![CDATA[vpc]]></category><category><![CDATA[subnetting]]></category><category><![CDATA[monitoring]]></category><category><![CDATA[#CloudWatch]]></category><dc:creator><![CDATA[Arshad Zackeriya]]></dc:creator><pubDate>Fri, 07 Mar 2025 20:53:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927464445/b41e289a-5853-4d1f-9ada-cf78b24ea269.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I want to begin with saying that Amazon Q developer and AWS Infrastructure Composer helped me to design this solution in a matter of minutes.</p>
<p>Amazon Q: <a target="_blank" href="https://aws.amazon.com/q/">https://aws.amazon.com/q/</a> AWS Infrastructure Composer: <a target="_blank" href="https://aws.amazon.com/infrastructure-composer/">https://aws.amazon.com/infrastructure-composer/</a></p>
<h2 id="heading-problem">Problem:</h2>
<p>Let's discuss the problem I'm attempting to tackle. IP exhaustion, which occurs when given subnets run out of IPs, is a problem that may arise if you are using Amazon EKS and your workload is growing.</p>
<p>Unless you have <a target="_blank" href="https://docs.aws.amazon.com/vpc/latest/ipam/what-it-is-ipam.html">IPAM</a>, AWS CloudWatch metrics do not support them at the time I am writing this blog. Monitoring your available IP addresses in subnets without the use of IPAM is what I'm attempting to accomplish here.</p>
<h2 id="heading-solution">Solution:</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926377526/2c777f53-1d59-424c-9b4a-e6ca9292230b.png" alt class="image--center mx-auto" /></p>
<p>AWS Services involved in this solution:</p>
<ul>
<li><p>AWS Lambda</p>
</li>
<li><p>Event Bridge Scheduler</p>
</li>
<li><p>Amazon CloudWatch Metrics</p>
</li>
<li><p>Amazon CloudWatch Alarm</p>
</li>
<li><p>Amazon SNS</p>
</li>
</ul>
<h3 id="heading-lambda-function">Lambda Function</h3>
<p>I was able to create this in a matter of minutes with the help of Amazon Q Developer, however, I obviously needed to make a few little adjustments. This is very beneficial if you understand the basics and what you are doing. Instead of configuring AWS services blindly, I recommend everyone to better understand AWS services.</p>
<h3 id="heading-full-python-script-here">Full Python Script here:</h3>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> boto3
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> botocore.exceptions <span class="hljs-keyword">import</span> ClientError

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lambda_handler</span>(<span class="hljs-params">event, context</span>):</span>
    vpc_id = os.environ[<span class="hljs-string">'VPC_ID'</span>]
    subnet_ids = os.environ[<span class="hljs-string">'SUBNET_IDS'</span>].split(<span class="hljs-string">','</span>)
    namespace = os.environ[<span class="hljs-string">'NAMESPACE'</span>]

    ec2 = boto3.client(<span class="hljs-string">'ec2'</span>)
    cloudwatch = boto3.client(<span class="hljs-string">'cloudwatch'</span>)

    <span class="hljs-keyword">try</span>:
        response = ec2.describe_subnets(
            Filters=[
                {<span class="hljs-string">'Name'</span>: <span class="hljs-string">'vpc-id'</span>, <span class="hljs-string">'Values'</span>: [vpc_id]},
                {<span class="hljs-string">'Name'</span>: <span class="hljs-string">'subnet-id'</span>, <span class="hljs-string">'Values'</span>: subnet_ids}
            ]
        )

        <span class="hljs-keyword">for</span> subnet <span class="hljs-keyword">in</span> response[<span class="hljs-string">'Subnets'</span>]:
            subnet_id = subnet[<span class="hljs-string">'SubnetId'</span>]
            available_ip_count = subnet[<span class="hljs-string">'AvailableIpAddressCount'</span>]
            cidr_block = subnet[<span class="hljs-string">'CidrBlock'</span>]
            total_ip_count = <span class="hljs-number">2</span> ** (<span class="hljs-number">32</span> - int(cidr_block.split(<span class="hljs-string">'/'</span>)[<span class="hljs-number">1</span>])) - <span class="hljs-number">5</span>  <span class="hljs-comment"># Subtract 5 for reserved IPs</span>

            subnet_name = subnet_id  <span class="hljs-comment"># Default to subnet ID if no name tag</span>
            <span class="hljs-keyword">for</span> tag <span class="hljs-keyword">in</span> subnet.get(<span class="hljs-string">'Tags'</span>, []):
                <span class="hljs-keyword">if</span> tag[<span class="hljs-string">'Key'</span>] == <span class="hljs-string">'Name'</span>:
                    subnet_name = tag[<span class="hljs-string">'Value'</span>]
                    <span class="hljs-keyword">break</span>

            utilization_percentage = ((total_ip_count - available_ip_count) / total_ip_count) * <span class="hljs-number">100</span>

            <span class="hljs-comment"># Send metrics to CloudWatch</span>
            cloudwatch.put_metric_data(
                Namespace=namespace,
                MetricData=[
                    {
                        <span class="hljs-string">'MetricName'</span>: <span class="hljs-string">'AvailableIPAddresses'</span>,
                        <span class="hljs-string">'Dimensions'</span>: [
                            {<span class="hljs-string">'Name'</span>: <span class="hljs-string">'SubnetName'</span>, <span class="hljs-string">'Value'</span>: subnet_name},
                            {<span class="hljs-string">'Name'</span>: <span class="hljs-string">'SubnetId'</span>, <span class="hljs-string">'Value'</span>: subnet_id}
                        ],
                        <span class="hljs-string">'Value'</span>: available_ip_count,
                        <span class="hljs-string">'Unit'</span>: <span class="hljs-string">'Count'</span>
                    },
                    {
                        <span class="hljs-string">'MetricName'</span>: <span class="hljs-string">'IPUtilizationPercentage'</span>,
                        <span class="hljs-string">'Dimensions'</span>: [
                            {<span class="hljs-string">'Name'</span>: <span class="hljs-string">'SubnetName'</span>, <span class="hljs-string">'Value'</span>: subnet_name},
                            {<span class="hljs-string">'Name'</span>: <span class="hljs-string">'SubnetId'</span>, <span class="hljs-string">'Value'</span>: subnet_id}
                        ],
                        <span class="hljs-string">'Value'</span>: utilization_percentage,
                        <span class="hljs-string">'Unit'</span>: <span class="hljs-string">'Percent'</span>
                    }
                ]
            )

            print(<span class="hljs-string">f"Metrics sent for Subnet: <span class="hljs-subst">{subnet_name}</span> (ID: <span class="hljs-subst">{subnet_id}</span>)"</span>)

    <span class="hljs-keyword">except</span> ClientError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"An error occurred: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">500</span>,
            <span class="hljs-string">'body'</span>: str(e)
        }

    <span class="hljs-keyword">return</span> {
        <span class="hljs-string">'statusCode'</span>: <span class="hljs-number">200</span>,
        <span class="hljs-string">'body'</span>: <span class="hljs-string">'Subnet monitoring completed'</span>
    }
</code></pre>
<h3 id="heading-get-ip-address-utilization">Get IP address utilization:</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926410281/115af832-8336-4e3a-b08a-25dc93e6d569.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-send-metrics-to-cloudwatch">Send metrics to CloudWatch:</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926429262/aaa159eb-d32f-4051-a420-69934af18d27.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-use-aws-infrastructure-composer-to-design-the-infrastructure">Use AWS Infrastructure Composer to design the infrastructure.</h2>
<p>This further enables you design your infrastructure visually, generate Infrastructure as Code and deploy it using AWS SAM (AWS Serverless Application Model) <a target="_blank" href="https://aws.amazon.com/serverless/sam/">https://aws.amazon.com/serverless/sam/</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926728136/90449649-c623-49fa-8400-2fd6e1d42d8b.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-how-to-deploy">How to Deploy</h2>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>AWS CLI installed and configured with appropriate permissions</p>
</li>
<li><p>AWS Toolkit for Visual Studio Code installed and configured</p>
</li>
<li><p>AWS SAM CLI installed</p>
</li>
</ul>
<h3 id="heading-deployment-steps">Deployment Steps</h3>
<h4 id="heading-repository-for-entire-code-and-instructions-on-how-to-deploy-httpsgithubcomawsfanboyaws-subnet-ip-address-utilization-monitorhttpsgithubcomawsfanboyaws-subnet-ip-address-utilization-monitor">Repository for entire code and instructions on how to deploy: <a target="_blank" href="https://github.com/awsfanboy/aws-subnet-ip-address-utilization-monitor">https://github.com/awsfanboy/aws-subnet-ip-address-utilization-monitor</a></h4>
<ul>
<li><p>Modify the <code>template.yaml</code> file to adjust default parameter values or add/remove resources as needed. eg: VPC ID, Subnet Name, Subnet ID, CloudWatch Metric Namespace.</p>
</li>
<li><p>(Optional) Update the <code>lambda_</code><a target="_blank" href="http://function.py"><code>function.py</code></a> file in the src directory.</p>
</li>
<li><p>Build the SAM application: <code>sam build</code></p>
</li>
<li><p>Deploy the SAM application: <code>sam deploy --guided</code></p>
</li>
<li><p>This will start an interactive deployment process. You'll be prompted to provide values for the parameters defined in the template. You can accept the default values or provide your own.</p>
</li>
<li><p>During the deployment, you'll be asked to confirm the creation of IAM roles and the changes to be applied. Review and confirm these.</p>
</li>
<li><p>SAM will output the ARNs of the created Lambda function and SNS topic once the deployment is complete.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926758774/7196628b-33fe-4674-a49e-1b49617e1f70.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-parameters">Parameters</h3>
<pre><code class="lang-apache">    <span class="hljs-attribute">VpcId</span>: The ID of the VPC to monitor
    <span class="hljs-attribute">SubnetIds</span>: Comma-separated list of subnet IDs to monitor
    <span class="hljs-attribute">SubnetName1</span>: Name of the first subnet
    <span class="hljs-attribute">SubnetName2</span>: Name of the second subnet
    <span class="hljs-attribute">CWMetericNamespace</span>: The CloudWatch metric namespace
    <span class="hljs-attribute">AlertEmail</span>: Email address to receive alerts
</code></pre>
<h3 id="heading-resources-created">Resources Created</h3>
<ul>
<li><p>Lambda function for monitoring subnets</p>
</li>
<li><p>EventBridge rule to trigger the Lambda function every minute</p>
</li>
<li><p>SNS topic for sending alerts</p>
</li>
<li><p>CloudWatch alarms for each monitored subnet</p>
</li>
</ul>
<h3 id="heading-customization">Customization</h3>
<ul>
<li><p>To monitor more than two subnets, duplicate the <code>SubnetUtilizationAlarm</code> resource in the template and adjust the <code>SubnetIds</code> parameter.</p>
</li>
<li><p>Modify the Lambda function code in <code>src/lambda_</code><a target="_blank" href="http://function.py"><code>function.py</code></a> to implement your specific monitoring logic.</p>
</li>
<li><p>Adjust the alarm thresholds and evaluation periods in the <code>SubnetUtilizationAlarm</code> resources as needed.</p>
</li>
</ul>
<h3 id="heading-cleanup">Cleanup</h3>
<ul>
<li><p>To remove all resources created by this stack: <code>sam delete</code></p>
</li>
<li><p>Follow the prompts to confirm the deletion of resources.</p>
</li>
</ul>
<h2 id="heading-demo">Demo</h2>
<p>I have an Amazon EKS cluster running a deployment with 6 replicas. Worker nodes are running on 2 Subnets. IP address utilization is looking good.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926805652/94c0ee4b-00ae-4565-a987-c0a9cb439ff1.png" alt class="image--center mx-auto" /></p>
<p>The alarm state is OK.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926857053/f88a9b9c-02ae-44be-b3ce-cb580620f0c6.png" alt class="image--center mx-auto" /></p>
<p>Okay! let's increase the number of replicas from <code>6</code> to <code>600</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926885209/a873024f-fb95-48c5-b17f-76dfc0d40694.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736926902905/69bc6b5d-b49d-4ea0-be06-050dc696f22f.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927031848/c5be081d-9244-46ad-8154-453b062176de.png" alt class="image--center mx-auto" /></p>
<p>Let's check metrics from the CloudWatch and ooops! now we can see that IP utilization is high.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927049056/1f89864e-747f-4cd1-a01e-ac8596f389d9.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927069588/07e5d03c-9fa8-4259-837a-2a31e4928278.png" alt class="image--center mx-auto" /></p>
<p>Now, let's check the Alarms in the CloudWatch. Now the state changed from <code>OK</code> to <code>ALARM</code> state.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927093800/e77e8ea9-ff9e-4d70-b2d1-84d555698de5.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927126619/74bc8843-687e-4be9-9a3b-cb85df8d89b7.png" alt class="image--center mx-auto" /></p>
<p>Let's check my emails</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927145898/6455160c-95dd-4a5e-bbc4-304913c932f7.png" alt class="image--center mx-auto" /></p>
<p>I can see there are 2 emails in my inbox.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927194740/802a556f-6888-4afb-b3f0-19cf0c3ed760.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927221546/6a4368ea-f5e8-41ad-b04e-d2c124ea8057.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-cost">Cost</h1>
<p>I calculated the cost using <a target="_blank" href="http://calculator.aws">calculator.aws</a>, and it appears to be not bad though.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927236461/9edde7c4-8286-4dc8-91b6-da923f4be150.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736927258083/083e94a8-be20-485f-a3a8-87943a6b9111.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-what-next">What Next?</h1>
<p>These notifications can be sent to Slack, PagerDuty, and other platforms.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>I hope my automation will help someone who doesn't want to use IPAM to monitor IP address utilization in subnets, and I truly wish we could access these metrics straight from CloudWatch.</p>
<p>If you have any suggestions for improvement or if you would like to use anything you currently have in a different way, please feel free to share.</p>
]]></content:encoded></item><item><title><![CDATA[Let's Explore Amazon EKS Auto Mode]]></title><description><![CDATA[💡
Note: I hope you already have a good understanding of Amazon EKS and Kubernetes. If not, you might find some parts difficult to follow. However, feel free to ask any questions, and I will be happy to help.


During AWS re:Invent 2024, one of my fa...]]></description><link>https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode</link><guid isPermaLink="true">https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[AWS]]></category><category><![CDATA[EKS]]></category><dc:creator><![CDATA[Arshad Zackeriya]]></dc:creator><pubDate>Mon, 13 Jan 2025 09:59:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650635446/42ef0c00-c7ec-4328-9700-4af6290ea3bb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Note: I hope you already have a good understanding of Amazon EKS and Kubernetes. If not, you might find some parts difficult to follow. However, feel free to ask any questions, and I will be happy to help.</div>
</div>

<p>During AWS re:Invent 2024, one of my favorite announcements was Amazon EKS Auto Mode. As a huge AWSFanBoy, I was very impressed and decided to try out.</p>
<p>In this blog post, I explain what AWS offers in EKS, the challenges of managing an EKS cluster, and what is Amazon EKS Auto Mode is. I aim to keep it simple for easy understanding. Don't worry, I've included a link to a demo that <a class="user-mention" href="https://hashnode.com/@zachjonesnoel">Jones Zachariah Noel N</a> and I did on our podcast <a target="_blank" href="https://www.youtube.com/@thezacsshowtalkingaws/streams"><strong>The Zacs' Show Talking AWS</strong></a><strong>.</strong> I've also added a video showing a cluster upgrade with EKS Auto Mode.</p>
<p>Before we dive into EKS Auto Mode, let's quickly review the previous EKS offerings.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">⚠</div>
<div data-node-type="callout-text">These offerings are still valid. EKS Auto Mode is a new feature, and customers can choose to enable or disable it.</div>
</div>

<h3 id="heading-eks-cluster-architecture-and-responsibility-model-without-eks-auto-mode">EKS Cluster Architecture and Responsibility Model without EKS Auto Mode</h3>
<blockquote>
<p>Disclaimer: I have included some screenshots from the official re:Invent slide deck, which is publicly available here - <a target="_blank" href="https://aws.amazon.com/events/events-content/?awsf.filter-series=*all&amp;awsf.filter-year=*all&amp;awsf.filter-session-type=*all&amp;awsf.filter-level=*all&amp;awsf.filter-topic=*all&amp;awsf.filter-area-of-interest=*all&amp;awsf.filter-industry=*all&amp;awsf.filter-role=*all&amp;awsf.filter-language=*all&amp;sc_channel=sm&amp;sc_campaign=Support&amp;sc_publisher=YOUTUBE&amp;sc_country=global&amp;sc_geo=GLOBAL&amp;sc_outcome=AWS%20Support&amp;sc_content=Support&amp;trk=Support&amp;linkId=682479436&amp;cards.sort-by=item.additionalFields.sortDate&amp;cards.sort-order=desc&amp;cards.q=KUB204-NEW&amp;cards.q_operator=AND">Link</a></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651580411/fd42d52a-6b0f-49bd-ace0-983cbd1fc42e.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651597022/5ff8862f-86bd-4613-9a69-e33354f5f9fb.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>AWS manages the control plane, etcd instances.</p>
</li>
<li><p>Customers have to manage add-ons, worker nodes, and other services like load balancers for ingress.</p>
</li>
<li><p>Without the core add-ons, we cannot run any workloads in a newly created EKS cluster.</p>
</li>
<li><p>Customers are responsible for node patching, node scaling, add-on updates and more.</p>
</li>
</ul>
<h3 id="heading-some-of-the-challenges-with-managing-an-amazon-eks-cluster">Some of the challenges with managing an Amazon EKS cluster</h3>
<p>Deploying an EKS cluster is not enough to run your workloads; you need at least these core add-ons: <code>Kubeproxy</code>, <code>CoreDNS</code>, and <code>VPC CNI</code> to have an application-ready cluster. Keeping these add-ons up to date and managing them also requires time and effort from engineers.</p>
<p>Managing compute resources is one of the biggest challenges. The most commonly used add-ons for auto-scaling nodes are Cluster Autoscaler or Karpenter. However, managing another add-on requires additional effort and time.</p>
<p>When you have multiple EKS clusters for internal developer teams or customers, upgrading them can be challenging. We need to ensure that core add-ons are updated along with the clusters, check for API deprecations, and more.</p>
<p>After upgrading the clusters, we need to gradually update the nodes and remove the old ones. Additionally, when AWS releases a new AMI for the nodes, we must ensure the clusters use nodes with the latest AMI. This is important for applying security patches as well.</p>
<h1 id="heading-what-is-amazon-eks-auto-mode">What is Amazon EKS Auto Mode</h1>
<p>Now we can create application-ready EKS clusters with essential Kubernetes capabilities. AWS handles all the complex aspects of managing your Kubernetes setup - including computing, storage, and networking.</p>
<h3 id="heading-amazon-eks-cluster-architecture-and-responsibility-model-for-an-eks-cluster-with-eks-auto-mode">Amazon EKS Cluster Architecture and Responsibility Model for an EKS Cluster with EKS Auto Mode</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650582424/cbe086fb-d953-4b8b-80b7-64007eeaee04.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736670615526/358a992f-7f0b-4c22-873c-7dcd180f5f12.png" alt class="image--center mx-auto" /></p>
<p>With EKS Auto Mode enabled, AWS now manages the Cluster EC2 instances and Cluster Capabilities. Compared to the previous image, it's clear that customers have fewer responsibilities. Let me explain the Cluster EC2 instances and Cluster Capabilities.</p>
<h2 id="heading-cluster-ec2-instances">Cluster EC2 instances</h2>
<p>In EKS Auto Mode Node autoscaling is managed by Karpenter. Karpenter is excellent for node scaling. It is not only auto-scales nodes but also finds the right and optimized node for your workload, helping to save costs. We will discuss Karpenter separately in another article. If you are not familiar with Karpenter, you can read more about it <a target="_blank" href="https://karpenter.sh/">here</a>. All the instances launched by EKS Auto Mode run <a target="_blank" href="https://aws.amazon.com/bottlerocket/?amazon-bottlerocket-whats-new.sort-by=item.additionalFields.postDateTime&amp;amazon-bottlerocket-whats-new.sort-order=desc">Bottlerocket</a> OS.</p>
<h2 id="heading-cluster-capabilities">Cluster Capabilities</h2>
<h3 id="heading-networking">Networking</h3>
<p>There are three essential add-ons under Networking that are managed by AWS.</p>
<ul>
<li><p>Kubeproxy</p>
</li>
<li><p>CoreDNS</p>
</li>
<li><p>VPC CNI</p>
</li>
</ul>
<p>So we no longer need to manage these add-ons. These add-ons run as systemd services on the nodes, so you won't see any of these pods running on the cluster. You'll see more in the demo later.</p>
<p>Listing pods in all namespaces, I don't see any of the pods running.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736757841026/d065a35a-5888-4119-a1d9-4e2311ba0d5d.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-load-balancing">Load balancing</h3>
<p>AWS manages the LB controller. For your ingress resource, you can use an ALB, and for services, you can use a NLB. You don’t have to install these controllers since AWS is managing them for you; you just need to annotate your service/ingress.</p>
<h3 id="heading-storage">Storage</h3>
<p>With EKS Auto Mode, the EBS CSI driver comes included, allowing us to set up EBS storage for pods that need persistent volumes.</p>
<hr />
<h1 id="heading-try-it-out-yourself-in-just-a-few-minutes">Try it out yourself in just a few minutes.</h1>
<p>I am using eksctl command to deploy an EKS cluster, which is really easy since it will deploy the dependancies for you cluster such as networking resource as VPC, Subnets etc.</p>
<ol>
<li><p>Install eksctl - <a target="_blank" href="https://eksctl.io/installation/">https://eksctl.io/installation/</a></p>
</li>
<li><p>Deploy an EKS cluster with EKS Auto Mode</p>
<ul>
<li><code>eksctl create cluster --name=hogwarts --enable-auto-mode --version=1.30</code></li>
</ul>
</li>
<li><p>Once cluster is created successfully, run the following commands</p>
<ul>
<li><p>List all the pods in all namespaces - <code>kubectl get pods -A</code></p>
</li>
<li><p>List all the nodes - <code>kubectl get nodes</code></p>
</li>
<li><p>You will not see any resources</p>
</li>
</ul>
</li>
<li><p>Now lets check the manifest file that you are going to deploy in the <code>manifest.yaml</code> file</p>
<ul>
<li><p>This manifest file will deploy</p>
<ul>
<li><p>Deployment</p>
</li>
<li><p>Pod Disruption Budget</p>
</li>
<li><p>Service</p>
</li>
<li><p>IngressClass</p>
</li>
<li><p>Ingress</p>
<pre><code class="lang-yaml">  <span class="hljs-string">---</span>
  <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">zacs-show-deployment</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">zacs</span>
  <span class="hljs-attr">spec:</span>
    <span class="hljs-attr">replicas:</span> <span class="hljs-number">36</span>
    <span class="hljs-attr">selector:</span>
      <span class="hljs-attr">matchLabels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">zacs</span>
    <span class="hljs-attr">template:</span>
      <span class="hljs-attr">metadata:</span>
        <span class="hljs-attr">labels:</span>
          <span class="hljs-attr">app:</span> <span class="hljs-string">zacs</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">zacs</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">awsfanboy/doggo-app</span>
          <span class="hljs-attr">ports:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
  <span class="hljs-string">---</span>
  <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">policy/v1</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">PodDisruptionBudget</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">zacs-pdb</span>
  <span class="hljs-attr">spec:</span>
    <span class="hljs-attr">minAvailable:</span> <span class="hljs-number">80</span><span class="hljs-string">%</span>
    <span class="hljs-attr">selector:</span>
      <span class="hljs-attr">matchLabels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">zacs</span>
  <span class="hljs-string">---</span>
  <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">zacs-show-service</span>
  <span class="hljs-attr">spec:</span>
    <span class="hljs-attr">selector:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">zacs</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
        <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
        <span class="hljs-attr">targetPort:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">NodePort</span>
  <span class="hljs-string">---</span>
  <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">IngressClass</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">eks-auto-alb</span>
  <span class="hljs-attr">spec:</span>
    <span class="hljs-attr">controller:</span> <span class="hljs-string">eks.amazonaws.com/alb</span>
  <span class="hljs-string">---</span>
  <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">networking.k8s.io/v1</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
  <span class="hljs-attr">metadata:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">zacs-ingress</span>
    <span class="hljs-attr">annotations:</span>
      <span class="hljs-attr">alb.ingress.kubernetes.io/target-type:</span> <span class="hljs-string">ip</span>
      <span class="hljs-attr">alb.ingress.kubernetes.io/scheme:</span> <span class="hljs-string">internet-facing</span>
  <span class="hljs-attr">spec:</span>
    <span class="hljs-attr">ingressClassName:</span> <span class="hljs-string">eks-auto-alb</span>
    <span class="hljs-attr">rules:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">http:</span>
          <span class="hljs-attr">paths:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">path:</span> <span class="hljs-string">/</span>
              <span class="hljs-attr">pathType:</span> <span class="hljs-string">Prefix</span>
              <span class="hljs-attr">backend:</span>
                <span class="hljs-attr">service:</span>
                  <span class="hljs-attr">name:</span> <span class="hljs-string">zacs-show-service</span>
                  <span class="hljs-attr">port:</span>
                    <span class="hljs-attr">number:</span> <span class="hljs-number">80</span>
</code></pre>
</li>
</ul>
</li>
</ul>
</li>
<li><p>Deploy it by running <code>kubectl apply -f manifest.yaml</code>.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736648708433/e99e9107-132f-4a47-a1b3-8e1f06c0fa6a.png" alt class="image--center mx-auto" /></li>
</ul>
</li>
<li><p>Let's list all the resources by running <code>kubectl get all</code>.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736648790963/416e37dc-5ea0-48b6-ae5b-57f5703abd33.png" alt class="image--center mx-auto" /></li>
</ul>
</li>
<li><p>Check the <code>nodes</code> and <code>nodeclaims</code> - <code>kubectl get nodes</code> , <code>kubectl get nodeclaims</code></p>
<ul>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736649401829/dcee4dbb-497b-44b9-bbc2-38d75d97b2cf.png" alt class="image--center mx-auto" /></p>
<p>  Karpenter launched a new node for my workload.</p>
</li>
</ul>
</li>
<li><p>AWS manages the compute, as I mentioned earlier. You can find the API resources by running the command <code>kubectl api-resources | grep karpenter</code>.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736649581684/20164301-e7ba-48f9-b0c0-ccbc8ceae716.png" alt class="image--center mx-auto" /></li>
</ul>
</li>
<li><p>Get the ingress URL by running <code>kubectl get ingress</code>.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736648857852/bbd2c6f8-37af-4029-adca-185898947eff.png" alt class="image--center mx-auto" /></li>
</ul>
</li>
<li><p>Allow some time for the DNS propagation and the creation of the ALB, then access the ALB URL in your browser.</p>
<ul>
<li><p>Voila! You can see my doggo app is running.</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736759213366/0344ea48-d74a-4b6f-b5e7-cd521f7fa33a.gif" alt class="image--center mx-auto" /></p>
</li>
</ul>
</li>
<li><p>See, that's it. We didn't install any add-ons, and within a few minutes, you have a cluster ready to run your application.</p>
</li>
<li><p>To cleanup run <code>eksctl delete cluster --name=hogwarts</code></p>
</li>
</ol>
<h2 id="heading-cluster-upgrades">Cluster Upgrades</h2>
<p>I have included a demo where I show an EKS cluster with Kubernetes version 1.30 and upgrade it to version 1.31.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/B-ejRnpBi-U">https://youtu.be/B-ejRnpBi-U</a></div>
<p> </p>
<h3 id="heading-here-is-the-podcast-where-jones-and-myself-did-a-deep-dive-into-amazon-eks-auto-mode-on-our-show-the-zacs-show-talking-awshttpswwwyoutubecomthezacsshowtalkingawsstreams">Here is the podcast where Jones and myself did a deep dive into Amazon EKS Auto Mode on our show, "<a target="_blank" href="https://www.youtube.com/@thezacsshowtalkingaws/streams"><strong>The Zacs' Show Talking AWS</strong></a>"</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=qxPP6zb_3mM&amp;ab_channel=TheZacs%27ShowTalkingAWS">https://www.youtube.com/watch?v=qxPP6zb_3mM&amp;ab_channel=TheZacs%27ShowTalkingAWS</a></div>
<p> </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Amazon EKS Auto Mode reduces operational tasks like managing core add-ons, patching nodes, dynamic scaling, and cluster upgrades. You can create a cluster anytime, and if you want to run tests, EKS Auto Mode helps by setting up an application-ready cluster in just a few minutes. Currently, only the main core add-ons are included by default, but I really wish, more add-ons will be available in the future. However, you can still install and configure other add-ons on an EKS Auto Mode cluster.</p>
<h2 id="heading-whats-next">Whats next ?</h2>
<p>I will publish another article on how to migrate an existing EKS cluster to an EKS Auto Mode cluster.</p>
<h2 id="heading-useful-links">Useful links</h2>
<ul>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=a_aDPo9oTMo">https://www.youtube.com/watch?v=a_aDPo9oTMo</a></p>
</li>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html">https://docs.aws.amazon.com/eks/latest/userguide/automode.html</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/eks/auto-mode/">https://aws.amazon.com/eks/auto-mode/</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>