# AWS CodePipeline now includes native support for deploying to Amazon EKS.

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 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.

Official announcement: [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/)

> 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.

## Let's begin with setting up the EKS Cluster

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741383998825/77e76320-2cd9-4e47-9c81-88c6a4955f9d.png align="center")

In this Demo, my EKS Cluster API server endpoint access is private, I have created an inbound rule in the `Cluster security group`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741384198990/1362fdce-f119-402a-9582-e93c37f45af6.png align="center")

You can follow my [blog](https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode) to create an EKS cluster with Auto Mode within few minutes.

%[https://blog.awsfanboy.com/lets-explore-amazon-eks-auto-mode] 

## Creating the ECR Repository

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.

```json
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

resource "aws_ecr_repository" "doggo_app_ecr" {
  name                 = "doggo-app-ecr"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration {
    scan_on_push = true
  }
}

output "ecr_repository_uri" {
  value       = aws_ecr_repository.doggo_app_ecr.repository_url
  description = "The URI of the ECR repository"
}
```

## AWS CodePipeline Creation and Configuration

Create a Pipeline from the scratch, follow the steps as per to my screenshots.

Creation option, select `Build custom pipeline`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741427675076/496a5c70-4477-4d64-9ee7-b9cdfaf6ed30.png align="center")

For this demo, under the `Service role`, create a `New service role`. 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741427602142/f72a01ed-7824-4186-b165-d3e43bdc8cf9.png align="center")

A quick note before we start the source stage: I have created this repository [https://github.com/awsfanboy/doggo-container-app](https://github.com/awsfanboy/doggo-container-app) with all the resources needed for this demo. Make sure to update the `values.yaml` file with the image URL.

> 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 **ALB Ingress Controller**.

```yaml
image:
  repository: { Replace with your ECR repository URL}
 # example,  repository: 11111111111.dkr.ecr.ap-southeast-2.amazonaws.com/doggo-ap-ecr
  tag: latest
  pullPolicy: Always
```

Set up the source stage by connecting your GitHub repository or any other repository you plan to use.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741645980065/043e3955-eaea-4e6a-988d-98631baece65.png align="center")

In the build stage, choose `Other build providers` and select `AWS ECR`. Then, select the ECR repository name from the dropdown menu.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741646073091/2974992e-683c-4983-bdc6-15afa5862ecf.png align="center")

Skip the Test stage for this demo and proceed to the deploy stage.

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.

Deploy stage, let’s check these fields now.

* Select the deploy provider as `Amazon EKS`.
    
* Since we have already created an EKS cluster in the same region, you can select the cluster from the dropdown.
    
* For `Deployment configuration type`, choose either `helm` or `kubectl`. In this demo, we are using a Helm chart to deploy.
    
* Provide the `release name` and Helm chart location.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741429197942/4aacd365-1288-435a-9674-4b71203da80d.png align="center")

Thats all for this and create the pipeline.

> 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.

## Cluster access from CodePipeline

This is really an important part, let’s breakdown the changes that we are going to do.

* 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 `ec2:CreateNetworkInterface`, `ec2:CreateNetworkInterfacePermission`, `ec2:DeleteNetworkInterface`, and some `ec2:Describe` permissions.
    

> ***<mark>If your cluster endpoint is public, you can skip this step.</mark>***

* The CodePipeline service role needs to authenticate and authorize access permissions through `EKS access entries`. Additionally, the CodePipeline service role should have the `eks:DescribeCluster` permission to access the cluster.
    

### Update the CodePipeline service role

Find your CodePipeline service role from the IAM roles and edit the existing role.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741646590224/00db01d2-b4e6-4930-b5c9-98871b0d44fa.png align="center")

Update the existing policy by adding the following policy document. Make sure to replace the Subnet ARNs and EKS Cluster ARN.

You can find the subnets in the Networking tab of the EKS cluster console.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741478175633/514f50ea-31f8-4771-8876-96322b2d4eaf.png align="center")

```json

            "Sid": "EksClusterPolicy",
            "Effect": "Allow",
            "Action": "eks:DescribeCluster",
            "Resource": "arn:aws:eks:ap-southeast-2:111111111111:cluster/awsfanboy-dev"
        },
        {
            "Sid": "EksVpcClusterPolicy",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeDhcpOptions",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeRouteTables",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeVpcs"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "ec2:CreateNetworkInterface",
            "Resource": "*",
            "Condition": {
                "StringEqualsIfExists": {
                     "ec2:Subnet": [
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0c147f0e842c43726",
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-068e82a6b56cc1742",
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0b12ab5e6baf6028d"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ec2:CreateNetworkInterfacePermission",
            "Resource": "*",
            "Condition": {
                "ArnEquals": {
                    "ec2:Subnet": [
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0c147f0e842c43726",
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-068e82a6b56cc1742",
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0b12ab5e6baf6028d"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": "ec2:DeleteNetworkInterface",
            "Resource": "*",
            "Condition": {
                "StringEqualsIfExists": {
                     "ec2:Subnet": [
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0c147f0e842c43726",
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-068e82a6b56cc1742",
                        "arn:aws:ec2:ap-southeast-2:111111111111:subnet/subnet-0b12ab5e6baf6028d"
                    ]
                }
            }
        }
```

### Update the CodePipeline service role to access ECR.

Add the `AmazonEC2ContainerRegistryPowerUser` AWS managed policy to the CodePipeline service role so that AWS ECR build provider can push the container image to the ECR.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741646297004/e9bdfebc-4bf8-4f4e-8261-42a2ad3ff969.png align="center")

### EKS Cluster Authentication and Authorization

We can create this easily via EKS access entries in the EKS Cluster console

Create access entry

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741478519607/4e19b7a0-e091-4454-bbc4-bd129c2e6f2b.png align="center")

Search for your CodePipeline service role and select it, then keep the type as `Standard` and click Next.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741478567847/a78145a3-096c-4823-9470-5054e13d493e.png align="center")

Now we need to attach the policy to authorize permissions for the CodePipeline service role. For this demo, I am using `AmazonEKSClusterAdminPolicy`. Keep the Access scope as Cluster, then click `Add policy`, followed by clicking Next to create the access entry.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741478642749/36ba36c3-f647-46de-92da-00969a9d65ae.png align="center")

That’s it now we can go ahead and rerun the pipeline.

Run the Pipeline

Navigate to the pipeline and click release pipeline.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741479359266/d92cadfb-d264-4a57-9880-37a9752663da.png align="center")

If all good, you will see your pipeline executed successfully.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741646410426/393f3ccf-e6cd-4481-b898-225636a19a77.png align="center")

You can expand the deploy action and check the logs.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741480052756/6a61332d-e14e-4cce-8086-9e57bf3981cd.png align="center")

### Verify the deployment

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 `kubectl` and `helm` commands. We don’t have to install `kubectl` but `helm`. Once done update the `kubeconfig`

```bash
~ $ aws eks update-kubeconfig --name awsfanboy-dev
```

Install helm:

```bash
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 > get_helm.sh
chmod 700 get_helm.sh
./get_helm.sh
```

List the Helm release and get the ingress URL.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741480308067/bc07e42b-17f7-4b0c-bec4-5b7645d64ab1.png align="center")

You can also list the Ingress URL from the EKS console by navigating to the Resources tab and selecting the Ingress object.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741480396678/5df5301e-671e-4019-9f68-f4788649a632.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741480452443/087b050e-b190-458d-a267-01e1e2a82086.png align="center")

Copy the Load Balancer URL and access it from your browser. You should see that our Doggo App Version 1 is running.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741480515691/bf88c7e4-744b-402f-9a6c-4034bb966851.png align="center")

### 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1741480766513/73bfd451-a066-4bbb-8205-4d794bf98174.png align="center")

## Conclusion

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.

If you have any questions or comments, please let me know.
