EKS using Terraform

Inderjot Singh
4 min readDec 22, 2023

--

Github URL

What is EKS

Amazon Elastic Kubernetes Service or simply EKS , is a managed Kubernetes service by AWS. Simplifying the Kubernetes architecture, EKS handles the complex control plane, including auto-scaling and management of etcd — a common pain point. With EKS, AWS takes charge of control plane management, leaving users to focus on the data plane. Utilizing managed nodes and provided scripts, users can effortlessly create and update components like nodes, streamlining the EKS deployment proces

Why Terraform

Today, we embark on creating an EKS cluster using Terraform, a prominent infrastructure-as-code management tool. Terraform’s significance lies in its seamless component updates without disruption. Auditing and version control simplify maintenance, allowing easy replication of the entire infrastructure in case of disruptions. Notably user-friendly, Terraform’s accessibility is enhanced by its well-documented features.

Network Stack

Setting Up VPC with Internet Connectivity

Step 1: Create VPC

  • Create a Virtual Private Cloud (VPC) with a specified IP range.

Step 2: Create Public Subnets

  • Establish 4 public subnets within the VPC for hosting resources.

Step 3: Attach Internet Gateway

  • Attach an Internet Gateway to the VPC to enable internet connectivity.

Step 4: Configure Route Table

  • Create and configure route table rules to direct traffic from the subnets to the internet through the attached Internet Gateway.


// VPC
resource "aws_vpc" "myVPC" {
cidr_block = "10.0.0.0/16"

enable_dns_support = true
enable_dns_hostnames = true

}



// internet gateway
resource "aws_internet_gateway" "myIGW" {
vpc_id = aws_vpc.myVPC.id
}



// public Subnet
resource "aws_subnet" "public-subnets" {
vpc_id = aws_vpc.myVPC.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index % length(data.aws_availability_zones.available.names)]
map_public_ip_on_launch = true
count = 4
}



// route table for public
resource "aws_route_table" "myRouteTable" {
vpc_id = aws_vpc.myVPC.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.myIGW.id
}

}



// route able association
resource "aws_route_table_association" "a" {
subnet_id = aws_subnet.public-subnets[count.index].id
route_table_id = aws_route_table.myRouteTable.id
count = 4
}


Roles

EKS Cluster Role Setup

Step 1: Create IAM Role

  • Create an IAM role for the AWS EKS cluster to perform necessary networking and management actions.

Step 2: Attach Policies

  • Attach the following AWS managed policies to the IAM role:
  • AmazonEKSClusterPolicy
  • AmazonEKSVPCResourceController

Nodegroup Role Setup

Step 3: Create IAM Role for Nodegroup

  • Create an IAM role for the EKS nodegroup to handle the data plane, specifically creating EC2 instances.

Step 4: Attach Policies

  • Attach policies to the nodegroup IAM role for:
  • EC2 Instances Creation
  • Access to AWS ECR for private images
  • Access to AWS VPC CNI for establishing inter-node networking.

// role for EKS cluster -----------------

resource "aws_iam_role" "myClusterRole" {
name = "${var.cluster-name}-role"
assume_role_policy = jsonencode({
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "eks.amazonaws.com"
}
}]
Version = "2012-10-17"
})
}



resource "aws_iam_role_policy_attachment" "aws-provided-policy-basic" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
role = aws_iam_role.myClusterRole.name
}



resource "aws_iam_role_policy_attachment" "aws-provided-policy-vpc-controlller" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController"
role = aws_iam_role.myClusterRole.name
}

// -------------------------




// role for NodeGroup ----------

resource "aws_iam_role" "myNodeGroup-role" {
name = "eks-node-group-${var.cluster-name}-role"

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

resource "aws_iam_role_policy_attachment" "aws-provided-policy-vpc-controlller-workerNodePolicy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
role = aws_iam_role.myNodeGroup-role.name
}



resource "aws_iam_role_policy_attachment" "aws-provided-policy-cni-policy" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
role = aws_iam_role.myNodeGroup-role.name
}



resource "aws_iam_role_policy_attachment" "aws-provided-policy-ecr-readonly" {
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
role = aws_iam_role.myNodeGroup-role.name
}

// -------------------------

Cluster and Managed NodeGroup

In this section , we are create the cluster and nodegroup and using the networking infrastructure and role we had created in previous sections.

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

provider "aws" {
region = var.region
}


// CLUSTER ---------------------------

resource "aws_eks_cluster" "myCluster" {
name = var.cluster-name
role_arn = aws_iam_role.myClusterRole.arn

vpc_config {
subnet_ids = aws_subnet.public-subnets[*].id
}



depends_on = [
aws_iam_role_policy_attachment.aws-provided-policy-basic,
aws_iam_role_policy_attachment.aws-provided-policy-vpc-controlller,
]

}

// -----------------------------------




// EKS Node Group ------------------------

resource "aws_eks_node_group" "myNodeGroup" {
cluster_name = aws_eks_cluster.myCluster.name
node_group_name = "${aws_eks_cluster.myCluster.name}-node-group"
node_role_arn = aws_iam_role.myNodeGroup-role.arn
subnet_ids = aws_subnet.public-subnets[*].id
instance_types = var.instance-types

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

update_config {
max_unavailable = 1
}


depends_on = [
aws_iam_role_policy_attachment.aws-provided-policy-ecr-readonly,
aws_iam_role_policy_attachment.aws-provided-policy-cni-policy,
aws_iam_role_policy_attachment.aws-provided-policy-vpc-controlller-workerNodePolicy,
]
}


// -----------------------------------

Variables

We can introduce modularity in our code by adding variables .



variable "region" {
type = string
default = "us-east-2"
}


variable "cluster-name" {
type = string
default = "myCluster"

}


variable "instance-types" {
type = list(string)
default = [ "t2.medium" ]

}

data "aws_availability_zones" "available" {
state = "available"
}

--

--