DEV Community

Cover image for How to Deploy AWS EKS With Open-Source Terraform Modules
Mattia Vercelli
Mattia Vercelli

Posted on

How to Deploy AWS EKS With Open-Source Terraform Modules

Deploying Amazon EKS by hand means composing AWS, IAM, Kubernetes, Helm, networking, and autoscaling resources yourself. This guide takes a more practical route: start from two runnable examples in the Das Meta open-source EKS module, inspect the code, validate the Terraform plan, then deploy only into an approved sandbox.

The architecture we are going to deploy

The repository contains two paths that share an existing VPC but serve different learning goals:

  • The basic path creates an EKS cluster and connects it to an existing VPC and its subnets.
  • The advanced path keeps critical components on a small on-demand system node group and enables Karpenter to provision workload capacity from NodePool rules.
  • The advanced path also demonstrates NGINX ingress, ExternalDNS, and sample Helm workloads.

Figure 1. Conceptual EKS topology for the advanced path: stable system nodes host critical components, while Karpenter provisions workload nodes as demand changes.<br>
Figure 1. Conceptual EKS topology for the advanced path: stable system nodes host critical components, while Karpenter provisions workload nodes as demand changes.

Prerequisites

Before you run either example, make sure you have:

  • Opened the open-source EKS module at v2.29.0.
  • Terraform compatible with the release. Version v2.29.0 declares Terraform ~> 1.3 and AWS provider >= 3.31, < 6.0.0.
  • AWS CLI, kubectl, and AWS credentials for an approved non-production account.
  • An existing VPC tagged Name = "default" and reviewed subnets. Both published examples use this lookup.
  • Permission to create and change EKS, IAM, EC2, security groups, and related resources. The advanced path also uses Helm and Kubernetes providers.
  • A cost owner. EKS, worker nodes, load balancers, NAT, storage, and data transfer can generate charges.

Step 1: Clone the exact release

Clone the same release referenced by this guide. Do not start from main, because examples and module defaults can change after publication.

git clone --branch v2.29.0 --depth 1 https://github.com/dasmeta/terraform-aws-eks.git
cd terraform-aws-eks
Enter fullscreen mode Exit fullscreen mode

Expected result: you are in the repository root, where each example can refer to the module with source = "../..".

git describe --tags --exact-match
Enter fullscreen mode Exit fullscreen mode

Expected output: v2.29.0. If Git does not print that tag, stop and check the checkout before continuing.

Step 2: Choose and inspect an example

Start with basic if you want the smallest possible module call. Choose eks-with-karpenter if you want to review system capacity, dynamic workload nodes, ingress, DNS, and Helm configuration.

Inspect these files first:

examples/basic/0-setup.tf
examples/basic/1-example.tf
examples/eks-with-karpenter/0-setup.tf
examples/eks-with-karpenter/1-example.tf
examples/eks-with-karpenter/http-echo.yaml
examples/eks-with-karpenter/http-echo-on-demand.yaml
Enter fullscreen mode Exit fullscreen mode

In both examples, inspect the provider region, the VPC lookup, selected subnets, and cluster name before running Terraform. The current examples use eu-central-1 and discover a VPC tagged Name = "default".

Technical validation required: confirm that the AWS account, region, VPC, and subnet IDs are the ones approved for this test. Do not use a tag lookup as a production environment-selection policy.

Step 3: Run the basic EKS example

Move into the basic directory, initialise Terraform, and save the plan for review.

cd examples/basic
terraform init
terraform fmt -check
terraform validate
terraform plan -out basic.tfplan
terraform show basic.tfplan
Enter fullscreen mode Exit fullscreen mode

Read the saved plan in this order:

  1. The AWS provider region is eu-central-1, unless you intentionally changed it.
  2. The VPC data source resolves one approved VPC and the subnet IDs belong to that VPC.
  3. The EKS cluster name and IAM changes belong to this sandbox, and no shared VPC or unrelated resources are being replaced.

The basic example passes a cluster name, a VPC ID, and private subnet IDs to the module. It does not create the VPC.

Check before apply: the returned VPC and subnet IDs must be correct, the cluster name must be unique, and the plan must show only resources approved for the sandbox.

terraform apply basic.tfplan
Enter fullscreen mode Exit fullscreen mode

Step 4: Verify the basic cluster

After Terraform completes, configure kubectl for the cluster and check that nodes and system pods are available.

aws eks describe-cluster --region eu-central-1 --name test-cluster-345678 --query 'cluster.status' --output text
aws eks update-kubeconfig --region eu-central-1 --name test-cluster-345678
kubectl get nodes
kubectl get pods -A
Enter fullscreen mode Exit fullscreen mode

Expected result: kubectl can authenticate, nodes are registered, and Kubernetes system workloads can schedule. If not, inspect the EKS, IAM, VPC, subnet, and security-group layers before retrying apply.

Step 5: Inspect the Karpenter configuration

The advanced example uses the same VPC link, then separates stable system capacity from elastic workload capacity. The following settings show the intent:

node_groups = {
  default = {
    min_size     = 2
    desired_size = 2
    max_size     = 2
    taints = {
      addons = {
        key    = "CriticalAddonsOnly"
        value  = "true"
        effect = "NO_SCHEDULE"
      }
    }
  }
}

karpenter = {
  enabled = true
  resource_configs_defaults = {
    limits = {
      cpu = 11
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The system node group uses t3.small or t3a.small by default. Karpenter creates additional capacity according to the configured NodePools, including a general NodePool and an on-demand NodePool in the released example.

Technical validation required: review the system-node taint and workload tolerations, Karpenter CPU limit, instance policy, capacity type, consolidation settings, quotas, and expected cost before applying.

Step 6: Run the Karpenter example

Return to the advanced example directory and repeat the Terraform workflow. The plan includes more integrations than the basic path, so review it more carefully.

cd ../eks-with-karpenter
terraform init
terraform fmt -check
terraform validate
terraform plan -out karpenter.tfplan
terraform show karpenter.tfplan
Enter fullscreen mode Exit fullscreen mode

This configuration enables Karpenter, NGINX ingress, ExternalDNS, and two sample Helm releases. It also disables several integrations, including EBS CSI, External Secrets, cert-manager, node-problem-detector, telemetry export, and Fluent Bit. That combination is an example, not a generic default.

Check before apply: validate ExternalDNS permissions and DNS ownership, ingress exposure and TLS implications, the Helm values files, and all planned IAM and network changes.

terraform apply karpenter.tfplan
Enter fullscreen mode Exit fullscreen mode

Step 7: Verify Karpenter and the workload path

Configure kubectl for the advanced cluster, then compare the observed capacity with the planned configuration.

aws eks update-kubeconfig --region eu-central-1 --name test-eks-with-karpenter
kubectl get nodes
kubectl get pods -A
kubectl get nodepools,nodeclaims
Enter fullscreen mode Exit fullscreen mode

The system node group should provide predictable capacity for critical components. NodePools and NodeClaims show how Karpenter requests and manages workload nodes.

Expected result: nodes and pods are visible, and Karpenter resources match the example. Do not test scale-down, consolidation, or interruption behaviour without an agreed workload-resilience plan.

Step 8: Adapt an example for a real environment

Copy the example directory before changing it. Keep the release tag and the original example link in your change request.

  • Replace the default VPC lookup with an approved VPC ID and explicit private subnet IDs.
  • Set a unique cluster name, environment-specific tags, and the team's approved remote Terraform backend with locking.
  • Enable only the integrations that the environment needs, reviewing the plan after each change.
  • Set Karpenter capacity, instance, availability, interruption, and cost policies intentionally.

Step 9: Destroy the sandbox when testing is complete

Destroy from the same example directory and Terraform state used for apply. Do not switch directory, workspace, credentials, or variable files between the apply and destroy operations.

9.1 Confirm the destroy scope

First confirm the directory, Terraform workspace, and AWS identity. This prevents a destroy plan from targeting the wrong account or state.

pwd
terraform workspace show
aws sts get-caller-identity
terraform state list
Enter fullscreen mode Exit fullscreen mode

Expected result: pwd points to examples/basic or examples/eks-with-karpenter, the workspace is the one used for the sandbox, and the AWS account and role match the approved test target.

9.2 Create a reviewed destroy plan

Create a destroy plan file, then read it before applying it. Do not use -auto-approve for this walkthrough.

terraform plan -destroy -out destroy.tfplan
terraform show destroy.tfplan
Enter fullscreen mode Exit fullscreen mode

Confirm all of the following in the destroy plan:

  • The cluster name is the sandbox cluster you created: test-cluster-345678 for basic, or test-eks-with-karpenter for the advanced example, unless you deliberately changed it.
  • The shared VPC and subnets are not marked for deletion. The examples link to an existing VPC; they do not own it.
  • All EKS, IAM, security-group, Helm, and related resources listed are expected for this sandbox and are not shared with another workload.
  • For the Karpenter path, understand whether NodeClaims, controller-created nodes, load balancers, or other controller-managed resources need time to disappear after the Terraform teardown starts.

9.3 Apply the reviewed destroy plan

Only after the destroy plan matches the sandbox, apply the saved plan:

terraform apply destroy.tfplan
Enter fullscreen mode Exit fullscreen mode

Do not substitute manual deletions for a failed destroy. Keep the Terraform state intact until you understand the dependency that caused the failure.

9.4 Verify cleanup

Terraform completion is the first check, not the final one. Verify that the EKS control plane has been removed from the account:

aws eks describe-cluster --region eu-central-1 --name <cluster-name>
Enter fullscreen mode Exit fullscreen mode

The expected eventual result is ResourceNotFoundException. If the command still returns a cluster, wait for the AWS operation to finish and investigate the Terraform output before trying another destroy.

For the Karpenter example, controller-created workload nodes, Kubernetes Services of type LoadBalancer, and Helm-managed objects can complete cleanup asynchronously. If a resource remains but is not in Terraform state, identify its owning controller, tags, and dependency first. Remove it only through the owner-approved process, not by guessing or deleting broadly.

Continue with the source

Top comments (0)