How to provision an EKS cluster (AWS) with Terraform

Recently, I needed an EKS cluster at work to test a scenario. I asked myself how to set this up as quickly as possible while minimising cost. I decided to use infrastructure as code. I searched the Internet for tutorials I could follow and found one from HashiCorp, which I decided to give a go.

https://developer.hashicorp.com/terraform/tutorials/kubernetes/eks

Clone the git repository

The first step is to clone the repository and download the files on your local machine.

pgulian@Mac repo % git clone https://github.com/hashicorp-education/learn-terraform-provision-eks-cluster
Cloning into 'learn-terraform-provision-eks-cluster'...
remote: Enumerating objects: 271, done.
remote: Counting objects: 100% (111/111), done.
remote: Compressing objects: 100% (42/42), done.
remote: Total 271 (delta 87), reused 69 (delta 69), pack-reused 160 (from 2)
Receiving objects: 100% (271/271), 107.81 KiB | 2.57 MiB/s, done.
Resolving deltas: 100% (144/144), done.
pgulian@Mac repo % cd learn-terraform-provision-eks-cluster
pgulian@Mac learn-terraform-provision-eks-cluster %
Clone the repository with git
Clone the repository with git

Initialise the directory with terraform init command

After you have downloaded the files and changed the directory, you need to initialise the directory with the command below.

pgulian@Mac learn-terraform-provision-eks-cluster % terraform init
Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 5.8.1 for vpc...
- vpc in .terraform/modules/vpc
Downloading registry.terraform.io/terraform-aws-modules/iam/aws 5.39.0 for irsa-ebs-csi...
- irsa-ebs-csi in .terraform/modules/irsa-ebs-csi/modules/iam-assumable-role-with-oidc
Downloading registry.terraform.io/terraform-aws-modules/eks/aws 20.8.5 for eks...
- eks in .terraform/modules/eks
- eks.fargate_profile in .terraform/modules/eks/modules/fargate-profile
- eks.self_managed_node_group in .terraform/modules/eks/modules/self-managed-node-group
- eks.eks_managed_node_group in .terraform/modules/eks/modules/eks-managed-node-group
Downloading registry.terraform.io/terraform-aws-modules/kms/aws 2.1.0 for eks.kms...
- eks.kms in .terraform/modules/eks.kms
- eks.self_managed_node_group.user_data in .terraform/modules/eks/modules/_user_data
- eks.eks_managed_node_group.user_data in .terraform/modules/eks/modules/_user_data
Initializing provider plugins found in the configuration...
- Reusing previous version of hashicorp/random from the dependency lock file
- Reusing previous version of hashicorp/tls from the dependency lock file
- Reusing previous version of hashicorp/time from the dependency lock file
- Reusing previous version of hashicorp/null from the dependency lock file
- Reusing previous version of hashicorp/cloudinit from the dependency lock file
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/null v3.2.2...
- Installed hashicorp/null v3.2.2 (signed by HashiCorp)
- Installing hashicorp/cloudinit v2.3.4...
- Installed hashicorp/cloudinit v2.3.4 (signed by HashiCorp)
- Installing hashicorp/aws v5.47.0...
- Installed hashicorp/aws v5.47.0 (signed by HashiCorp)
- Installing hashicorp/random v3.6.1...
- Installed hashicorp/random v3.6.1 (signed by HashiCorp)
- Installing hashicorp/tls v4.0.5...
- Installed hashicorp/tls v4.0.5 (signed by HashiCorp)
- Installing hashicorp/time v0.11.1...
- Installed hashicorp/time v0.11.1 (signed by HashiCorp)
Initializing the backend...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
pgulian@Mac learn-terraform-provision-eks-cluster %
Initialise the repository with terraform init command
Initialise the repository with terraform init command
Terraform has been successfully initialized!

Terraform has been successfully initialized!

Run the terraform plan command and inspect its output

The next step in the process is to run the terraform plan command. This command will generate a plan and show us which resources will be created in AWS.

pgulian@Mac learn-terraform-provision-eks-cluster % terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform planned the following actions, but then encountered a problem:
# random_string.suffix will be created
+ resource "random_string" "suffix" {
+ id = (known after apply)
+ length = 8
+ lower = true
+ min_lower = 0
+ min_numeric = 0
+ min_special = 0
+ min_upper = 0
+ number = true
+ numeric = true
+ result = (known after apply)
+ special = false
+ upper = true
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ region = "us-east-2"
╷
│ Error: Retrieving AWS account details: validating provider credentials: retrieving caller identity from STS: operation error STS: GetCallerIdentity, https response error StatusCode: 403, RequestID: 05dc8b84-9187-4a80-b8ba-5386066471c1, api error InvalidClientTokenId: The security token included in the request is invalid.
│
│ with provider["registry.terraform.io/hashicorp/aws"],
│ on main.tf line 4, in provider "aws":
│ 4: provider "aws" {
│
╵
pgulian@Mac learn-terraform-provision-eks-cluster %
Error: Retrieving AWS account details: validating provider credentials: retrieving caller identity from STS: operation error STS: GetCallerIdentity, https response error StatusCode: 403, RequestID: 05dc8b84-9187-4a80-b8ba-5386066471c1, api error InvalidClientTokenId: The security token included in the request is invalid.

Operation error STS: GetCallerIdentity, https response error StatusCode: 403

If you don’t specify any credentials, Terraform won’t be able to connect to the Cloud API. One way to expose credentials to Terraform is to use environment variables. You need to run a command similar to the one below.

pgulian@Mac learn-terraform-provision-eks-cluster % export AWS_ACCESS_KEY_ID=<YOUR-ACCESS-KEY-ID>
pgulian@Mac learn-terraform-provision-eks-cluster % export AWS_SECRET_ACCESS_KEY=<YOUR-SECRET_ACCESS-KEY>

After you have exported the access keys, run the terraform plan command again.

Run terraform plan command
Run terraform plan command
Output from terraform plan command
Output from terraform plan command

Run the terraform apply command to deploy your infrastructure

After you have inspected the output of the terraform plan, the next step is to run the terraform apply command to deploy your infrastructure.

Run the terraform apply
Run the terraform apply
Output from terraform apply command
Output from terraform apply command

If you want to accept the actions described by the terraform apply command, enter yes to approve, and terraform will deploy your infrastructure.

Approve the terraform apply command
Approve the terraform apply command

Unfortunately, Terraform is unable to deploy the infrastructure to AWS due to an error.

module.vpc.aws_nat_gateway.this[0]: Creation complete after 1m46s [id=nat-01a4f466a5322e30a]
module.vpc.aws_route.private_nat_gateway[0]: Creating...
module.vpc.aws_route.private_nat_gateway[0]: Creation complete after 1s [id=r-rtb-00770f646918a13c81080289494]
╷
│ Error: creating EKS Cluster (education-eks-lvSAyKf7): operation error EKS: CreateCluster, https response error StatusCode: 400, RequestID: e82d4e6d-93c2-468a-ab1b-3e4767734c13, InvalidParameterException: unsupported Kubernetes version 1.29
│
│ with module.eks.aws_eks_cluster.this[0],
│ on .terraform/modules/eks/main.tf line 27, in resource "aws_eks_cluster" "this":
│ 27: resource "aws_eks_cluster" "this" {
│
╵
pgulian@Mac learn-terraform-provision-eks-cluster %
InvalidParameterException: unsupported Kubernetes version 1.29

InvalidParameterException: unsupported Kubernetes version 1.29

Change the Kubernetes version in main.tf file

If you inspect the error message carefully, you will discover why the terraform apply command failed. We tried to deploy an unsupported Kubernetes version, 1.29. If we check the supported Kubernetes versions in the AWS documentation, we will find that Kubernetes version 1.29 is already out of support.

Excerpt from AWS documentation:

Available versions on standard support
The following Kubernetes versions are currently available in Amazon EKS standard support:
1.36
1.35
1.34
1.33

https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html#available-versions

The first version supported by AWS is Kubernetes 1.34. You need to update the version in the file main.tf (line 56) from the current version, 1.29, to 1.34.

Current Kubernetes version
Current Kubernetes version
Updated the Kubernetes version to 1.34
Updated the Kubernetes version to 1.34

Rerun the terraform apply command

After you have updated the Kubernetes version to 1.34, rerun the terraform apply command. I added the ‘-auto-approve’ flag to skip entering yes when prompted to accept the changes.

Terraform apply -auto-approve
Terraform apply -auto-approve

Regrettably, I faced a new error.

╷
│ Error: creating EKS Node Group (education-eks-lvSAyKf7:node-group-2-20260731085724462400000006): operation error EKS: CreateNodegroup, https response error StatusCode: 400, RequestID: 5200bb69-7b3a-47b4-b1bc-9bc8a2014ba7, InvalidParameterException: AMI Type AL2_x86_64 is only supported for kubernetes versions 1.32 or earlier
│
│ with module.eks.module.eks_managed_node_group["two"].aws_eks_node_group.this[0],
│ on .terraform/modules/eks/modules/eks-managed-node-group/main.tf line 383, in resource "aws_eks_node_group" "this":
│ 383: resource "aws_eks_node_group" "this" {
│
InvalidParameterException: AMI Type AL2_x86_64 is only supported for kubernetes versions 1.32 or earlier

InvalidParameterException: AMI Type AL2_x86_64 is only supported for kubernetes versions 1.32 or earlier

Change the AMI type from ami_type = “AL2_x86_64” to ami_type = “AL2023_x86_64_STANDARD”

If we inspect the error message, we will see that AMI Type AL2_x86_64 is supported only for Kubernetes versions 1.32 or earlier. Since we have changed the Kubernetes version to 1.34, we need to update the AMI Type from ami_type = “AL2_x86_64” to ami_type = “AL2023_x86_64_STANDARD”. Open the main.tf file and change the ami_type on line 71.

ami_type = "AL2_x86_64"
ami_type = “AL2_x86_64”
ami_type = "AL2023_x86_64_STANDARD"
ami_type = “AL2023_x86_64_STANDARD”

Repeat the terraform apply -auto-approve command

After you have changed the main.tf file, run the terraform apply -auto-approve command again. Hopefully, we ran the command for the last time.

pgulian@Mac learn-terraform-provision-eks-cluster % terraform apply -auto-approve
random_string.suffix: Refreshing state... [id=lvSAyKf7]
module.eks.module.kms.data.aws_caller_identity.current[0]: Reading...
module.eks.data.aws_caller_identity.current: Reading...
module.irsa-ebs-csi.data.aws_caller_identity.current: Reading...

Voila, this time the ‘terraform apply’ command completed successfully. The EKS cluster should be up and running.

The 'terraform apply' command completed successfully
The ‘terraform apply’ command completed successfully

Configure kubectl and verify the cluster

The last step, of this tutorial is to configure kubectl and verify the Kubernetes cluster.

You need to run the following aws command.

pgulian@Mac learn-terraform-provision-eks-cluster % aws eks --region $(terraform output -raw region) update-kubeconfig \
--name $(terraform output -raw cluster_name)
Added new context arn:aws:eks:us-east-2:533267288394:cluster/education-eks-lvSAyKf7 to /Users/pgulian/.kube/config
pgulian@Mac learn-terraform-provision-eks-cluster %
Run the aws command to update the kubeconfig and add the new context
Run the aws command to update the kubeconfig and add the new context

To verify your cluster you can run the following command.

pgulian@Mac learn-terraform-provision-eks-cluster % kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-1-96.us-east-2.compute.internal Ready <none> 9m32s v1.34.9-eks-bca9cf6
ip-10-0-2-57.us-east-2.compute.internal Ready <none> 9m31s v1.34.9-eks-bca9cf6
ip-10-0-2-8.us-east-2.compute.internal Ready <none> 9m32s v1.34.9-eks-bca9cf6
pgulian@Mac learn-terraform-provision-eks-cluster %
Verify the EKS cluster
Verify the EKS cluster

Clean all resources by running the terraform destroy command

If you no longer need your EKS cluster, don’t forget to clean up all the resources you created. Otherwise, you will incur costs. To remove all the resources you created in this tutorial, run the command below.

pgulian@Mac learn-terraform-provision-eks-cluster % terraform destroy -auto-approve
random_string.suffix: Refreshing state... [id=lvSAyKf7]
module.eks.module.eks_managed_node_group["one"].data.aws_partition.current: Reading...
module.eks.module.eks_managed_node_group["two"].data.aws_partition.current: Reading...
module.irsa-ebs-csi.data.aws_caller_identity.current: Reading...
module.irsa-ebs-csi.data.aws_partition.current: Reading...
Clean all resources by running the terraform destroy command
Clean all resources by running the terraform destroy command

That’s all. If you encounter a similar problem, you will know the likely cause and how to fix it. I opened a GitHub issue and submitted a pull request to HashiCorp Education to update the tutorial so others can follow along. Until they merge the pull request, you can follow the steps outlined above.

Processing…
Success! You're on the list.

Leave a Reply