So now that you have a basic understanding of how Terraform works, before you start running any terraform command against a real AWS account, two things need to happen: you need an identity Terraform can authenticate as, and you need a mental picture of what you're about to create, so the plan output in Part 4 isn't just a list of unfamiliar resource names.
Never Use Your AWS Root User!
The root user (the email/password you signed up to AWS with) can do anything, including closing the account. It should basically never be used day-to-day. Instead, create a dedicated IAM user just for this project. In real life you would create a dedicated IAM user for your CI/CD pipeline to automate deployments:
- AWS Console → IAM → Users → Create user (e.g.
terraform-voting-app). - Do not enable AWS Console access, this user only needs programmatic access, i.e. an API key pair.
- The AWS managed policy
AdministratorAccessis the path of least friction, and is what you should use for the IAM user to test things out. but in real life you would go with least privilege approach, learn more about it in AWS EKS IAM policy examples. - On the user's Security credentials tab → Create access key → choose "Command Line Interface (CLI)". You'll get an Access Key ID and a Secret Access Key, store them somewhere safe, we will be needing them later.
Give Terraform those Credentials
The rule: credentials never go inside a .tf file, and never inside terraform.tfvars. So in your local machine or CI/CD pipeline you need to export AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_REGION as environment variables in the shell. Then you won't be needing aws configure or aws login step anywhere, the aws provider has no access_key/secret_key arguments of its own, so it falls back to the AWS SDK's standard credential chain, which checks these exact environment variables first.
The AWS CLI and, later kubectl read the same variables. In my local machine I do export an env variable files using a shell script. The export only lasts for the current shell session, re-run it in any new terminal before running terraform/aws/kubectl.
What You are About to Build
This is the part most beginner Terraform/EKS tutorials skip, and it's the part that makes the actual .tf files make sense at a glance instead of feeling like a wall of unfamiliar arguments.
flowchart TB
subgraph AWS["AWS Account / Region"]
subgraph VPC["VPC — 10.0.0.0/16"]
IGW["Internet Gateway"]
subgraph AZ1["Availability Zone A"]
PubA["Public subnet\n10.0.100.0/24"]
PrivA["Private subnet\n10.0.0.0/24"]
end
subgraph AZ2["Availability Zone B"]
PubB["Public subnet\n10.0.101.0/24"]
PrivB["Private subnet\n10.0.1.0/24"]
end
NAT["NAT Gateway\n(in a public subnet)"]
CP["EKS Control Plane\n(managed by AWS,\nnot inside your subnets)"]
PrivA --- Node1["EC2 worker node"]
PrivB --- Node2["EC2 worker node"]
end
NLB["Network Load Balancer\n(public, one per exposed Service)"]
end
Internet(("Internet")) --> IGW
IGW --> PubA
IGW --> PubB
PubA --> NAT
NAT -.outbound only.-> Node1
NAT -.outbound only.-> Node2
CP <-. manages .-> Node1
CP <-. manages .-> Node2
Internet --> NLB
NLB --> Node1
NLB --> Node2
Node1 -- runs --> Pods1["voting / result / worker\nredis / postgres pods"]
Node2 -- runs --> Pods2["voting / result / worker\nredis / postgres pods"]
Reading it top to bottom:
-
VPC: a private network inside AWS,
10.0.0.0/16here (65k addresses is just the conventional default and more than what we need). - Two Availability Zones: EKS requires subnets in at least 2 AZs, so a single AZ failure can't take the whole cluster down.
- Public subnets have a route to the Internet Gateway. Their only job is hosting the NAT Gateway and, later, the public-facing Load Balancers.
- Private subnets is where the actual EC2 worker nodes live. They have no direct route in from the internet, and no public IP at all.
- NAT Gateway lets the private-subnet nodes reach out to the internet (to pull container images, talk to the EKS API, etc.) without allowing anything in from the internet. One-way door.
- EKS Control Plane is a managed AWS service which is the Kubernetes API server, scheduler, etc. It doesn't live "in" your subnets the way an EC2 instance does, though it does attach elastic network interfaces into them to talk to your nodes.
- Worker nodes are plain EC2 instances, sitting in the private subnets, that the EKS control plane schedules your pods onto. This is the "data plane", as opposed to the control plane above.
-
Network Load Balancer is created later, by Kubernetes (not Terraform) when you expose a Service as
type: LoadBalancer. This is how traffic actually reaches your app from a browser.
EKS Costs
We have two separate charges, both starting the moment terraform apply finishes:
- The EKS control plane itself has a flat hourly rate. Check current EKS pricing and this is regardless of whether any pods are running.
- The EC2 worker nodes: ordinary EC2 billing, because they're ordinary EC2 instances. Two
t3.mediumnodes, in this project.
Plus a NAT Gateway (hourly + per-GB processed) and small EBS volumes for each node's disk. None of it is free-tier.
Reference links
This article was originally published by DEV Community and written by Mohammad Jawad (Kasir) Barati.
Read original article on DEV Community