Setting Up Amazon EKS with Terraform

Manoj ShetManoj Shet
7 min read

Introduction

Amazon Elastic Kubernetes Service (EKS) simplifies the management of Kubernetes clusters, allowing users to deploy and scale containerized applications with ease. As organizations increasingly adopt microservices architectures and containerization, a robust orchestration platform like Kubernetes becomes essential for streamlining application deployment and management.

Incorporating infrastructure as code (IaC) tools such as Terraform into the workflow enhances the setup and management of the EKS environment. Terraform enables the definition of infrastructure in a declarative manner, facilitating version control, automation of deployments, and easy replication of environments. This blog provides a step-by-step process for setting up an EKS cluster using Terraform.

Prerequisites

  • An active AWS account with the necessary permissions to create EKS clusters, IAM roles, VPCs, and associated resources.

  • Terraform installed on the local machine.

  • AWS CLI installed and configured with access keys for the AWS account.

  • Basic understanding of Kubernetes concepts, including pods, services, and deployments.

  • Familiarity with Terraform syntax and configuration files for effective implementation.

Infrastructure Setup

Setting Up the AWS Provider

The provider.tf file initializes the AWS provider, which allows Terraform to authenticate with AWS and deploy resources in the specified region.

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "5.73.0"
    }
  }
}
provider "aws" {
  region = "us-east-1"
}

1. Create VPC

A Virtual Private Cloud (VPC) provides an isolated network for your resources. It allows you to define your own IP address range, create subnets, and configure route tables and network gateways.

resource "aws_vpc" "manoj_vpc" {
  cidr_block = "10.0.0.0/16"
}
  • Resource Type: aws_vpc

  • Purpose: This block creates a VPC with the CIDR block of 10.0.0.0/16, allowing for up to 65,536 private IP addresses. This isolation is crucial for securely managing your resources.

2. Create Subnets

Two public subnets are created across two availability zones. Subnets enable you to segment your VPC for better organization and security.

resource "aws_subnet" "manoj_subnet" {
  count = 2
  vpc_id = aws_vpc.manoj_vpc.id
  cidr_block = cidrsubnet(aws_vpc.manoj_vpc.cidr_block, 8, count.index)
  availability_zone = element(["us-east-1a", "us-east-1b"], count.index)
  map_public_ip_on_launch = true
}
  • Resource Type: aws_subnet

  • Purpose: This block creates two subnets in different availability zones (us-east-1a and us-east-1b). The cidrsubnet function calculates a subnet CIDR block from the VPC CIDR, and map_public_ip_on_launch ensures instances launched in these subnets receive a public IP address.

3. Set Up the Internet Gateway and Route Table

An internet gateway allows communication between instances in the VPC and the internet, enabling external access to the resources.

resource "aws_internet_gateway" "manoj_internet_gateway" {
  vpc_id = aws_vpc.manoj_vpc.id
}

resource "aws_route_table" "manoj_route_table" {
  vpc_id = aws_vpc.manoj_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.manoj_internet_gateway.id
  }
}
  • Resource Type: aws_internet_gateway and aws_route_table

  • Purpose: The internet gateway is created to allow resources in the VPC to access the internet. The route table defines a route that sends all outbound traffic through the internet gateway.

4. Associate Subnets with the Route Table

The subnets must be associated with the route table to enable internet access for the instances within those subnets.

resource "aws_route_table_association" "manoj_route_table_association" {
  count = 2
  subnet_id = aws_subnet.manoj_subnet[count.index].id
  route_table_id = aws_route_table.manoj_route_table.id
}
  • Resource Type: aws_route_table_association

  • Purpose: This block associates each subnet with the previously created route table. The count allows for multiple associations, ensuring both subnets can reach the internet.

5. Create Security Groups

Security groups control inbound and outbound traffic for the resources. They act as virtual firewalls to control network access.

resource "aws_security_group" "manoj_cluster_secutiry_group" {
  vpc_id = aws_vpc.manoj_vpc.id

  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "manoj_node_security_group" {
  vpc_id = aws_vpc.manoj_vpc.id

  ingress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
  • Resource Type: aws_security_group

  • Purpose:

    • manoj_cluster_security_group: This security group allows all outbound traffic from the EKS cluster.

    • manoj_node_security_group: This group allows all inbound and outbound traffic, providing flexibility for communication between nodes and external clients.

6. Create the EKS Cluster

The EKS cluster is created, specifying the associated VPC and security groups.

resource "aws_eks_cluster" "manoj" {
  name = "manoj-cluster"
  role_arn = aws_iam_role.manoj_cluster_role.arn 

  vpc_config {
    subnet_ids = aws_subnet.manoj_subnet[*].id
    security_group_ids = [aws_security_group.manoj_cluster_secutiry_group.id]
  }
}
  • Resource Type: aws_eks_cluster

  • Purpose: This block defines the EKS cluster with the specified name and IAM role. The vpc_config section links the cluster to the previously created subnets and security groups, enabling it to function within the defined network.

7. Define the EKS Node Group

The node group consists of EC2 instances that run your Kubernetes workloads. Node groups allow you to scale and manage the worker nodes.

resource "aws_eks_node_group" "manoj" {
  cluster_name = aws_eks_cluster.manoj.name
  node_group_name = "manoj-node-group"
  node_role_arn = aws_iam_role.manoj_node_group_role.arn
  subnet_ids = aws_subnet.manoj_subnet[*].id

  scaling_config {
    desired_size = 2
    max_size = 5
    min_size = 2
  }

  instance_types = ["t2.medium"]

  remote_access {
    ec2_ssh_key = var.ssh_key_name
    source_security_group_ids = [aws_security_group.manoj_node_security_group.id]
  }
}
  • Resource Type: aws_eks_node_group

  • Purpose: This block defines a node group associated with the EKS cluster. It specifies the scaling configuration, desired instance type, and enables SSH access to the nodes using an SSH key. The node group automatically manages the EC2 instances that run your applications.

8. Create IAM Roles for the Cluster and Node Group

IAM roles are essential for granting permissions to the EKS cluster and nodes. They allow AWS services to perform actions on your behalf.

resource "aws_iam_role" "manoj_cluster_role" {
  name = "manoj-cluster-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "eks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "manoj_cluster_role_policy" {
  role = aws_iam_role.manoj_cluster_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
}

resource "aws_iam_role" "manoj_node_group_role" {
  name = "manoj-node-group-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "manoj_node_group_role_policy" {
  role = aws_iam_role.manoj_node_group_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
}

resource "aws_iam_role_policy_attachment" "manoj_node_group_cni_policy" {
  role = aws_iam_role.manoj_node_group_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
}

resource "aws_iam_role_policy_attachment" "manoj_node_group_registry_policy" {
  role = aws_iam_role.manoj_node_group_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
}
  • Resource Type: aws_iam_role and aws_iam_role_policy_attachment

  • Purpose:

    • manoj_cluster_role: Allows the EKS service to manage the cluster.

    • manoj_node_group_role: Grants permissions to the EC2 instances in the node group. Various policy attachments ensure that nodes can manage network interfaces, communicate with EKS, and access container images.

Deploy the Infrastructure

To apply your Terraform configuration and deploy the infrastructure, run:

  1. terraform init
  • Purpose: Initializes the Terraform working directory.

  • Explanation: This command downloads any required provider plugins and sets up the backend configuration, which is where Terraform stores its state files (like local files or a remote storage service). Running this command is mandatory when working with a new or modified Terraform configuration, as it ensures Terraform has all necessary resources to run the subsequent commands.

  1. terraform plan
  • Purpose: Creates an execution plan, showing what changes will be made to reach the desired infrastructure state.

  • Explanation: This command allows you to see the proposed changes Terraform will make before actually applying them. It compares the current infrastructure (based on the existing state file) with the configuration specified in your Terraform files. It then provides a detailed list of resources that will be added, modified, or destroyed. The plan step is especially useful for verifying that everything is correct before making changes.

  1. terraform apply
  • Purpose: Executes the changes defined by the Terraform configuration to achieve the desired state.

  • Explanation: This command applies the proposed changes from the terraform plan. Terraform will prompt for confirmation before making any changes. Once confirmed, Terraform provisions the resources in the order necessary to create the infrastructure according to the configuration files. Afterward, it updates the state file with the latest configuration

  1. terraform destroy
  • Purpose: Destroys the infrastructure managed by Terraform.

  • Explanation: This command will remove all resources defined in your Terraform configuration files. When executed, Terraform reads the current state file to identify all existing resources it manages and then deletes them.

Conclusion

This guide successfully provisions an Amazon EKS cluster using Terraform, establishing a strong foundation for scalable and manageable Kubernetes clusters on AWS. The use of Terraform’s infrastructure-as-code approach ensures configurations are version-controlled, repeatable, and adaptable, simplifying the process of managing infrastructure changes across environments.

The resulting cluster setup offers flexibility to support various workloads and can be further customized. Future steps may include enhancing the setup with monitoring and logging, refining network and security policies, or implementing autoscaling for optimized resource usage. This configuration provides a robust starting point, ready to be expanded as infrastructure requirements evolve.

0
Subscribe to my newsletter

Read articles from Manoj Shet directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Manoj Shet
Manoj Shet