What is AWS Infrastructure as Code (IaC) & Configuration & How to Implement them

In previous posts, we have learned what is Kubernetes, and its involvement in DevOps Project. So, in this project, we will learn what is Infrastructure as Code(Iac) and how to implement it using Terraform.

The first time I had to build an environment manually, it took almost two days. I installed packages one by one, configured servers, updated firewall rules, and documented everything in a spreadsheet. A few weeks later, another environment was needed. I followed the same notes.

The result?

The new setup behaved differently from the original one.

That is exactly the kind of problem Infrastructure as Code (IaC) was created to solve.

Instead of depending on manual steps, memory, and documentation that quickly becomes outdated, teams write infrastructure requirements as code. The infrastructure can then be created, modified, tested, and reproduced whenever needed.

For modern DevOps teams running Kubernetes, IaC is no longer optional. It has become a standard way to manage both cloud infrastructure and Kubernetes configurations reliably.


Table of Contents

Why Infrastructure as Code Became So Important

Think about managing hundreds of servers manually.

Every server must have:

  • Operating system configuration
  • Network settings
  • Security groups
  • Storage volumes
  • User permissions
  • Application dependencies

Now multiply that across development, testing, staging, and production environments.

Human errors become unavoidable.

A small mistake in one server can cause unexpected application failures that are difficult to troubleshoot later.

IaC solves this problem by making infrastructure predictable.

Instead of manually creating resources, you define them in code files and execute those files whenever an environment is needed.

The same code produces the same result every time.


What is Infrastructure as Code (IaC)?

Infrastructure as Code is a DevOps practice where infrastructure resources are defined using machine-readable configuration files.

These files describe exactly what should be created, including:

  • Virtual machines
  • Networks
  • Storage
  • Load balancers
  • Kubernetes clusters
  • Security policies

Rather than opening cloud dashboards and clicking buttons repeatedly, engineers write configuration files and deploy infrastructure automatically.

A simple example might look like this:

  • Create 3 virtual machines
  • Attach 100 GB storage
  • Open port 443
  • Create a Kubernetes cluster
  • Deploy monitoring tools

Everything is written in code.

The infrastructure becomes version-controlled just like application source code.


Key Benefits of Infrastructure as Code

Consistency Across Environments

One of the biggest problems in IT operations is configuration drift.

You may hear developers say:

“It works on my machine.”

That usually happens because environments are not identical.

With IaC:

  • Development environment matches production
  • Testing environment matches staging
  • Configurations remain standardized

The same code builds identical environments repeatedly.

Faster Deployments

Manual provisioning can take hours or even days.

IaC reduces this dramatically.

For example:

TaskManual SetupIaC Setup
Create VM15 minutes1 minute
Configure Network20 minutesAutomated
Deploy Kubernetes ClusterSeveral hoursFew minutes
Scale InfrastructureManual workAutomatic

This speed allows teams to release features much faster.

Version Control and Audit Trail

IaC files are usually stored in Git repositories.

This provides several advantages:

  • Track every change
  • Review code before deployment
  • Identify who made changes
  • Roll back problematic updates

Many organizations treat infrastructure code exactly the same way they treat application code.


Kubernetes and Infrastructure as Code

When I first started working with Kubernetes, one thing stood out immediately.

Everything revolves around declarations.

Instead of giving Kubernetes detailed instructions about how to run applications, you simply describe the desired outcome.

Kubernetes then works continuously to achieve that state.

For example:

You can define:

  • Run 3 application pods
  • Expose them through a service
  • Restart failed containers automatically
  • Maintain application availability

Kubernetes handles the operational work behind the scenes.

This approach aligns perfectly with Infrastructure as Code principles.


Understanding Kubernetes Configuration

Kubernetes configuration is typically stored in YAML files.

These files describe resources inside the cluster.

Common Kubernetes resources include:

ResourcePurpose
PodRuns containers
DeploymentManages pod replicas
ServiceExposes applications
ConfigMapStores configuration data
SecretStores sensitive information
Persistent VolumeProvides storage
IngressManages external traffic

A deployment file might simply specify:

  • Application image
  • Number of replicas
  • CPU limits
  • Memory limits
  • Network configuration

Once applied, Kubernetes continuously monitors the environment and keeps everything aligned with the desired state.


Why Store Kubernetes YAML Files in Git?

Many teams follow a GitOps approach.

In GitOps, Git becomes the single source of truth.

Whenever changes are needed:

  1. Update YAML files
  2. Commit changes to Git
  3. Review through pull requests
  4. Deploy automatically

This provides several benefits:

  • Full change history
  • Easier troubleshooting
  • Better collaboration
  • Faster recovery after failures
  • Security auditing

When something breaks, engineers can quickly compare previous versions and identify what changed.


Popular IaC and Kubernetes Configuration Tools

Different tools manage different layers of the infrastructure stack.

Some provision cloud resources.

Others manage Kubernetes applications.

Let’s look at the most common ones.


Terraform

Terraform is one of the most widely used Infrastructure as Code tools.

It focuses on creating and managing infrastructure resources.

Before Kubernetes even exists, Terraform can create:

  • AWS EC2 instances
  • Amazon EKS clusters
  • Google Kubernetes Engine (GKE)
  • Azure Kubernetes Service (AKS)
  • VPCs
  • Subnets
  • Security Groups
  • Load Balancers

A common workflow looks like this:

  1. Write Terraform files
  2. Execute terraform plan
  3. Review proposed changes
  4. Execute terraform apply
  5. Infrastructure gets created automatically

Many organizations use Terraform as the foundation of their cloud infrastructure strategy.

Where Terraform Fits

LayerManaged By
Cloud InfrastructureTerraform
Kubernetes ClusterTerraform
ApplicationsKubernetes/Helm

Helm

After Kubernetes is running, applications must be deployed.

This is where Helm becomes extremely useful.

Helm acts as the package manager for Kubernetes.

Instead of maintaining dozens of YAML files manually, Helm packages them into reusable units called Charts.

A Helm chart can contain:

  • Deployments
  • Services
  • ConfigMaps
  • Secrets
  • Ingress resources

Imagine deploying:

  • Prometheus
  • Grafana
  • Jenkins
  • Argo CD

Without Helm, managing all related YAML files becomes tedious.

With Helm, installation can often be completed using a single command.

Benefits of Helm

  • Reusable templates
  • Environment-specific values
  • Easier upgrades
  • Simple rollbacks
  • Reduced YAML duplication

This is one reason Helm is heavily used in production Kubernetes environments.

Helm acts as the package manager for Kubernetes.

Kustomize

Not every team wants template-based deployments.

Sometimes teams simply want to customize existing Kubernetes manifests.

Kustomize was designed for this purpose.

Instead of creating templates, Kustomize applies overlays on top of existing YAML files.

For example:

Base configuration:

  • 2 replicas
  • Standard resources

Production overlay:

  • 10 replicas
  • Higher CPU limits

Development overlay:

  • 1 replica
  • Lower resource limits

The original files remain unchanged.

This keeps configurations clean and easier to maintain.

Why Teams Like Kustomize

  • Native Kubernetes support
  • No templating language
  • Cleaner configuration management
  • Easy environment customization
  • Better readability

How Terraform, Helm, and Kubernetes Work Together

In a real-world DevOps project, these tools usually work together rather than competing with each other.

A common workflow looks like this:

Step 1: Terraform

Create:

  • VPC
  • Networking
  • Storage
  • Kubernetes cluster

Step 2: Kubernetes

Provide orchestration platform for applications.

Step 3: Helm

Deploy:

  • Monitoring tools
  • Logging stack
  • Business applications
  • Security tools

Step 4: GitOps

Store everything in Git repositories and automate deployments.

This combination creates a highly scalable and repeatable infrastructure platform.


Final Thoughts on Infrastructure as Code and Kubernetes Configuration

From my experience, Infrastructure as Code changes the way teams think about operations.

Instead of manually building servers and hoping everything matches, teams define infrastructure once and recreate it whenever needed. The process becomes predictable. Mistakes become easier to spot. Recovery becomes faster.

Kubernetes extends the same philosophy into container orchestration. You describe the desired state, store configurations in version control, and let automation handle the repetitive work.

When Terraform provisions infrastructure, Helm deploys applications, and Kubernetes manages workloads, teams gain a platform that can scale from a small startup environment to large enterprise systems without changing the core approach.

Here is the blog section rewritten in a natural, human-written style with H3 and H4 headings, practical explanations, real-world DevOps context, and approximately 1000 words.

What Is Infrastructure in AWS Kubernetes?

The first time I worked on a production Kubernetes project in AWS, I made the same mistake many beginners make. I thought Kubernetes itself was the infrastructure.

It isn’t.

Kubernetes manages containers. AWS provides the actual environment where those containers run.

Think about a large office building. Kubernetes acts like the facility manager who decides where employees sit, which meeting room gets assigned, and how resources are shared. AWS is the building itself—electricity, rooms, security, elevators, networking, and storage.

Without the building, the manager has nothing to manage.

That’s exactly how AWS Kubernetes infrastructure works.

When we say “AWS Kubernetes infrastructure,” we’re talking about all the AWS services working together underneath Kubernetes to host, run, secure, scale, and monitor applications.

Kubernetes and Infrastructure as Code

Understanding the Two Main Layers of AWS Kubernetes Infrastructure

Every Kubernetes environment running on AWS has two major layers.

1. Control Plane — The Brain of Kubernetes

The control plane makes decisions.

It decides:

  • Which node should run a new pod
  • When a pod should restart
  • How workloads should scale
  • How cluster resources should be managed

Most DevOps teams today use Amazon EKS (Elastic Kubernetes Service) for this layer.

With EKS, AWS manages the control plane for you.

That means AWS automatically handles:

  • Kubernetes API Server
  • etcd database
  • Controller Manager
  • Scheduler
  • High availability across multiple Availability Zones

This removes a huge operational burden.

Instead of managing Kubernetes master nodes manually, engineers can focus on applications and infrastructure automation.

For production environments, this is usually the preferred approach.


2. Data Plane — Where Applications Actually Run

The control plane makes decisions.

The data plane does the work.

This layer contains the worker nodes where your applications execute.

These worker nodes can be:

  • Amazon EC2 instances
  • AWS Fargate serverless containers

In most real-world projects, EC2 worker nodes remain the most common choice because they offer better flexibility and lower cost for large workloads.

A typical application deployment might look like this:

ComponentPurpose
EKS Control PlaneCluster management
EC2 Worker NodesRun application pods
VPCNetwork isolation
ALBIncoming traffic routing
EBSPersistent storage
ECRDocker image storage

This combination forms the foundation of many production Kubernetes deployments.


Networking Infrastructure in AWS Kubernetes

Networking is often where beginners struggle.

When I first started working with EKS, networking consumed more time than Kubernetes itself.

A Kubernetes cluster doesn’t exist in isolation.

It runs inside an Amazon VPC (Virtual Private Cloud).

The VPC provides:

  • Private networking
  • IP address management
  • Traffic isolation
  • Security boundaries

Most production environments use a multi-AZ design.

A common architecture looks like this:

Public Subnets

Used for:

  • Application Load Balancers
  • NAT Gateways
  • Internet-facing services

Private Subnets

Used for:

  • Worker nodes
  • Databases
  • Internal services

This design improves security because application servers are not directly exposed to the internet.

Traffic typically flows like this:

Internet User → ALB → Kubernetes Service → Pod

Simple. Secure. Proven.


Load Balancers

Applications need external access.

AWS solves this using Elastic Load Balancing.

The most common option is:

  • Application Load Balancer (ALB)

Inside Kubernetes, engineers install the AWS Load Balancer Controller.

When an Ingress resource is created, the controller automatically provisions an ALB.

No manual setup required.

This automation saves countless hours in production environments.


Storage Infrastructure in AWS Kubernetes

Containers are temporary.

Storage is not.

If a database pod crashes, you don’t want customer data disappearing.

That’s where AWS storage services come in.

Amazon EBS

Amazon Elastic Block Store (EBS) provides block storage.

Best for:

  • Databases
  • Stateful applications
  • High-performance workloads

Common examples:

  • MySQL
  • PostgreSQL
  • MongoDB

Each volume attaches to a specific node.


Amazon EFS

Amazon Elastic File System (EFS) provides shared storage.

Best for:

  • Shared application files
  • Upload directories
  • Multi-pod workloads

Unlike EBS, multiple pods can access the same EFS storage simultaneously.

This makes it useful for content management systems and shared application environments.


How Infrastructure Is Provisioned in AWS Kubernetes

Nobody wants to create AWS resources manually anymore.

Imagine creating:

  • VPCs
  • Subnets
  • Security Groups
  • EKS Clusters
  • IAM Roles

One by one.

Every time.

That quickly becomes a nightmare.

Most DevOps teams use Infrastructure as Code (IaC).

Terraform

Terraform remains the industry standard.

A Terraform project typically provisions:

  • VPC
  • Public and private subnets
  • Internet Gateway
  • NAT Gateway
  • EKS cluster
  • Node groups
  • IAM roles
  • Security groups

Once the code is written, creating a new environment becomes a single command:

terraform apply

This ensures consistency across development, testing, and production environments.


EKS Auto Mode

AWS recently introduced EKS Auto Mode.

This feature automatically manages:

  • Compute provisioning
  • Node scaling
  • Networking configuration
  • Storage integration

For smaller teams, this significantly reduces operational overhead.

Instead of managing worker nodes, engineers focus on application deployment.


Most Used Infrastructure in AWS Kubernetes for DevOps Projects

After working on several enterprise Kubernetes deployments, I can say that most production projects follow a very similar infrastructure pattern.

Not because it’s trendy.

Because it works.

Core Compute Layer

The foundation usually includes:

  • Amazon EKS
  • EC2 Managed Node Groups
  • Karpenter

Karpenter has become extremely popular because it automatically provisions right-sized EC2 instances based on workload demand.

Compared to traditional Cluster Autoscaler, Karpenter often reduces infrastructure costs.

Especially when using Spot Instances.


Container Registry

Every application image needs a home.

Most AWS Kubernetes projects use:

  • Amazon Elastic Container Registry (ECR)

Benefits include:

  • Private image storage
  • Vulnerability scanning
  • IAM integration
  • Fast image pulls inside AWS

A typical CI/CD pipeline pushes Docker images directly into ECR before deployment.


Security Layer

Security becomes more important as environments grow.

Production Kubernetes clusters commonly use:

  • AWS IAM
  • EKS Pod Identity
  • AWS Secrets Manager

Instead of storing passwords inside Kubernetes manifests, teams securely retrieve secrets from AWS Secrets Manager.

This dramatically improves security.


CI/CD and GitOps Stack

Modern DevOps projects rarely deploy applications manually.

A common workflow looks like this:

  1. Developer pushes code to GitHub.
  2. Jenkins or GitHub Actions starts the build.
  3. Docker image is built.
  4. Image is pushed to ECR.
  5. ArgoCD detects Git changes.
  6. ArgoCD deploys automatically into EKS.

This approach is called GitOps.

Many organizations have adopted it because deployments become predictable and auditable.


Monitoring and Observability

Once applications go live, monitoring becomes essential.

Most Kubernetes projects deploy:

  • Prometheus
  • Grafana

Prometheus collects metrics.

Grafana visualizes them.

Teams monitor:

  • CPU usage
  • Memory consumption
  • Pod restarts
  • Application response times
  • Node health
  • Cluster capacity

Without monitoring, troubleshooting becomes guesswork.

With monitoring, problems become visible before customers notice them.


Typical Production AWS Kubernetes Infrastructure Stack

Here’s the infrastructure stack I see most often in real-world DevOps projects:

LayerCommon Service
OrchestrationAmazon EKS
ComputeEC2 Managed Node Groups
AutoscalingKarpenter
NetworkingVPC, ALB, Route 53
SecurityIAM, Pod Identity, Secrets Manager
Container RegistryAmazon ECR
StorageEBS, EFS
Infrastructure as CodeTerraform
GitOpsArgoCD
CI/CDJenkins, GitHub Actions
MonitoringPrometheus, Grafana

This combination has become the de facto standard for building scalable, secure, and production-ready Kubernetes platforms on AWS.

I’ll provide the blog section as a reusable draft.

Understanding the Two Main Layers of AWS Kubernetes Infrastructure

How to Create Infrastructure in AWS Kubernetes for a DevOps Project

A few years ago, I made the same mistake many DevOps engineers make when they first start working with Kubernetes on AWS. I created everything manually from the AWS Console. It worked for a small demo. Then I had to rebuild the environment for testing. Then for staging. Then for production.

That was painful.

After rebuilding the same infrastructure multiple times, I switched completely to Terraform and Amazon EKS. Since then, creating Kubernetes infrastructure has become predictable, repeatable, and much easier to manage.

If you’re building a real-world DevOps project using AWS Kubernetes, this is the exact roadmap I recommend following.


Phase 1: Prepare Your Local Environment

Before creating any AWS infrastructure, set up the tools that will manage and communicate with your cloud resources.

Install Required Tools

ToolPurpose
AWS CLIConnects your local machine with AWS
TerraformCreates AWS resources automatically
kubectlManages Kubernetes clusters
HelmInstalls Kubernetes applications
GitSource code management

I usually install these first and verify their versions before touching AWS.

Example:

aws --version
terraform --version
kubectl version --client
helm version

Once everything is installed, configure AWS authentication:

aws configure

You will enter:

  • AWS Access Key
  • AWS Secret Key
  • Default Region
  • Output Format

For most projects, I prefer regions like:

  • us-east-1
  • us-west-2
  • ap-south-1 (Mumbai)

At this point, your laptop becomes the control center for the entire infrastructure.


Phase 2: Build the AWS Networking Layer

Before Kubernetes can run, it needs a secure network.

Think of the network as the foundation of a house. If the foundation is weak, everything above it becomes difficult to manage.

Create a Dedicated VPC

Using Terraform, create:

  • One VPC
  • Public Subnets
  • Private Subnets
  • Internet Gateway
  • NAT Gateway
  • Route Tables

A common production setup looks like this:

ComponentCount
VPC1
Public Subnets3
Private Subnets3
Availability Zones3
NAT Gateways1–3

I prefer private worker nodes because they aren’t directly exposed to the internet.

That adds another security layer without much additional complexity.

Why Multiple Availability Zones Matter

AWS recommends spreading workloads across multiple Availability Zones.

If one data center experiences issues, Kubernetes automatically schedules workloads in healthy zones.

For production environments, I rarely deploy a cluster into a single zone anymore.


Phase 3: Provision Amazon EKS Cluster with Terraform

This is where the real Kubernetes infrastructure comes alive.

Instead of creating resources manually through the AWS Console, Terraform creates everything from code.

Create the EKS Control Plane

Terraform provisions:

  • EKS Cluster
  • IAM Roles
  • Security Groups
  • Networking Configuration
  • Cluster Policies

AWS manages the Kubernetes control plane for you.

That means:

  • No master node maintenance
  • No etcd management
  • Automatic availability

Less operational headache.

Create Managed Node Groups

Worker nodes actually run your applications.

A typical DevOps lab environment might use:

Node TypePurpose
t3.mediumJenkins
t3.mediumMonitoring
t3.mediumApplications

For production projects, I usually separate workloads into dedicated node groups.

Example:

  • CI/CD Node Group
  • Monitoring Node Group
  • Application Node Group

This keeps noisy workloads from affecting critical services.

Deploy the Infrastructure

Once Terraform code is ready:

terraform init
terraform plan
terraform apply

Terraform starts creating:

  • VPC
  • Subnets
  • IAM Roles
  • EKS Cluster
  • Node Groups

Depending on region and configuration, cluster creation usually takes around 10 to 20 minutes.

Now AWS is doing all the heavy lifting.


Phase 4: Connect kubectl to the EKS Cluster

After Terraform finishes successfully, Kubernetes still doesn’t know who you are.

You need to update your kubeconfig.

Run:

aws eks update-kubeconfig \
--region ap-south-1 \
--name devops-eks-cluster

Now verify connectivity:

kubectl get nodes

Expected output:

NAME                STATUS   ROLES
ip-10-0-1-20        Ready
ip-10-0-2-15        Ready
ip-10-0-3-10        Ready

The first time I see worker nodes in Ready state, I know the cluster foundation is healthy.

From here, everything else becomes much easier.


Phase 5: Create Amazon ECR Repository

Applications need a place to store Docker images.

That place is Amazon Elastic Container Registry (ECR).

Create a Private Container Registry

Example:

aws ecr create-repository \
--repository-name ecommerce-app

Your repository will receive a URL similar to:

123456789012.dkr.ecr.ap-south-1.amazonaws.com/ecommerce-app

Developers push images:

docker push image-name

Kubernetes pulls them automatically.

One thing I really like about ECR is that worker nodes can access images securely using IAM roles.

No registry passwords scattered across pipelines.


Phase 6: Install AWS Load Balancer Controller

Applications inside Kubernetes need a way to receive external traffic.

That’s where AWS Load Balancer Controller helps.

Why This Controller Matters

Without it, exposing applications becomes messy.

With it, Kubernetes Ingress resources automatically create:

  • Application Load Balancers (ALB)
  • Target Groups
  • Listener Rules

Traffic routing becomes almost automatic.

Install Using Helm

helm repo add eks https://aws.github.io/eks-charts
helm install aws-load-balancer-controller

After installation, any Ingress resource can create a production-grade AWS ALB.

That saves a huge amount of manual configuration.


Phase 7: Deploy Argo CD for GitOps

One habit I encourage every DevOps engineer to develop is avoiding manual deployments.

Manual deployments eventually create configuration drift.

GitOps solves that.

Install Argo CD

Using Helm:

helm install argocd argo/argo-cd

Now Argo CD continuously watches your Git repository.

Whenever code changes:

  1. Developer pushes code
  2. CI pipeline builds image
  3. Image reaches ECR
  4. Git repository updates manifests
  5. Argo CD deploys automatically

No manual kubectl apply commands.

No deployment confusion.

Everything remains version-controlled.


Phase 8: Install Prometheus and Grafana

A Kubernetes cluster without monitoring feels like driving a car without a dashboard.

You won’t know what is happening until something breaks.

Deploy Prometheus

Prometheus collects:

  • CPU usage
  • Memory usage
  • Pod metrics
  • Node metrics
  • Application metrics

Install:

helm install prometheus prometheus-community/kube-prometheus-stack

Deploy Grafana

Grafana turns raw metrics into visual dashboards.

I usually create dashboards for:

  • Cluster Health
  • Pod Status
  • CPU Utilization
  • Memory Usage
  • Application Performance
  • Error Rates

When an issue appears at 2 AM, Grafana often helps identify the root cause within minutes.


Final AWS Kubernetes Infrastructure Architecture

A complete DevOps project infrastructure typically looks like this:

GitHub
   |
Jenkins
   |
Docker Build
   |
Amazon ECR
   |
Argo CD
   |
Amazon EKS
   |
Applications
   |
AWS Load Balancer
   |
Users

Monitoring runs alongside:

Prometheus
     |
Grafana

Infrastructure creation remains fully automated through:

Terraform

That combination—Terraform, EKS, ECR, Argo CD, Prometheus, Grafana, and AWS Load Balancer Controller—is what I see most frequently in modern enterprise DevOps projects. Once you’ve built this setup a few times, you’ll realize you’re not just creating a Kubernetes cluster. You’re building a repeatable platform that can support development, testing, staging, and production environments with very little manual work.

How to Create Infrastructure in AWS Kubernetes for a DevOps Project

Real-Time Terraform IaC for AWS EKS DevOps Project

The first time I deployed an Amazon EKS cluster manually through the AWS Console, it took almost an entire day. Networking issues. IAM permission errors. Wrong subnet selections. Everything that could go wrong, did.

That experience taught me one thing very quickly.

If you’re building serious DevOps projects, Infrastructure as Code isn’t optional anymore.

Terraform gives us a repeatable way to create the same infrastructure again and again without clicking through dozens of AWS screens. More importantly, every infrastructure change becomes visible inside Git, reviewable by teammates, and easy to roll back if something breaks.

For this project, I use Terraform to provision a complete production-style Amazon EKS environment that closely resembles what many engineering teams run in real organizations.


Why Use Terraform for AWS EKS?

When companies run Kubernetes workloads on AWS, consistency matters.

Nobody wants one engineer creating resources manually while another engineer creates them differently next month.

Terraform solves that problem by turning infrastructure into version-controlled code.

Some practical benefits I have personally seen include:

  • Faster environment creation
  • Easier disaster recovery
  • Infrastructure auditing through Git history
  • Reduced configuration drift
  • Better collaboration across DevOps teams
  • Consistent deployment across development, staging, and production environments

For EKS specifically, Terraform handles:

ComponentManaged by Terraform
VPCYes
SubnetsYes
Route TablesYes
NAT GatewaysYes
IAM RolesYes
Security GroupsYes
EKS ClusterYes
Managed Node GroupsYes
Auto ScalingYes

This means your entire Kubernetes foundation can be recreated from scratch within minutes.


Production Architecture Overview

For a real-world DevOps project, I avoid single Availability Zone deployments.

A production cluster should survive infrastructure failures without affecting applications.

The architecture looks like this:

Networking Layer

The network foundation consists of:

  • 1 Custom VPC
  • 2 Public Subnets
  • 2 Private Subnets
  • Internet Gateway
  • NAT Gateway
  • Route Tables

Public subnets mainly host:

  • Load Balancers
  • NAT Gateways

Private subnets host:

  • EKS Worker Nodes
  • Internal Services
  • Application Pods

Keeping worker nodes private adds an extra security layer because EC2 instances are not directly exposed to the internet.


Compute Layer

The compute layer contains:

  • Amazon EKS Control Plane
  • Managed Node Group
  • Auto Scaling Configuration

The Kubernetes control plane remains fully managed by AWS.

This reduces operational overhead significantly.

Instead of patching Kubernetes masters or managing etcd clusters, AWS handles those tasks automatically.

Worker nodes scale based on workload demand, which helps optimize infrastructure costs.


Security Layer

Security should never be an afterthought.

For this Terraform implementation, I configure:

  • EKS Cluster IAM Role
  • Node Group IAM Role
  • Worker Node Security Groups
  • Cluster Security Groups
  • Kubernetes API Permissions

A common mistake I see in beginner projects is assigning overly permissive IAM policies.

In production, follow the principle of least privilege.

Give only the permissions required for a specific resource.

Nothing more.


Remote State Management Using S3 and DynamoDB

One of the biggest differences between learning Terraform and using Terraform professionally is state management.

Many beginners keep the Terraform state file locally.

That works for personal testing.

It becomes a disaster in team environments.

For production projects, I always store Terraform state remotely using:

Amazon S3 Bucket

Stores:

  • terraform.tfstate
  • State history
  • Versioned infrastructure records

Benefits include:

  • Centralized storage
  • Backup capability
  • Team collaboration
  • State version recovery

DynamoDB State Locking

Terraform state corruption can occur when multiple engineers run deployments simultaneously.

DynamoDB locking prevents this.

When one engineer executes Terraform commands, a lock entry is created automatically.

Other deployment attempts wait until the lock is released.

This simple feature prevents many painful production incidents.

Before running Terraform for the first time, create:

ResourcePurpose
S3 BucketRemote state storage
DynamoDB TableState locking

These resources must exist before backend initialization.


Recommended Terraform Project Structure

As projects grow, organization becomes extremely important.

A structure that works for a small demo often becomes difficult to maintain later.

I typically use a layout similar to this:

Root Configuration Files

Contains:

  • provider.tf
  • backend.tf
  • variables.tf
  • outputs.tf
  • terraform.tfvars

Modules Directory

Contains reusable modules such as:

  • VPC Module
  • EKS Module
  • IAM Module
  • Security Module

Benefits include:

  • Reusability
  • Easier maintenance
  • Cleaner codebase
  • Better scalability

Large engineering teams almost always adopt module-based Terraform architecture.


Terraform Resources Used in This AWS EKS Project

The Terraform deployment provisions multiple AWS services automatically.

Network Resources

Terraform creates:

  • aws_vpc
  • aws_subnet
  • aws_route_table
  • aws_nat_gateway
  • aws_internet_gateway

These resources establish the networking foundation required by Kubernetes.


EKS Resources

Terraform also provisions:

  • aws_eks_cluster
  • aws_eks_node_group
  • aws_iam_role
  • aws_security_group

Together they create a fully operational Kubernetes environment.

After deployment, Terraform outputs the cluster details required for kubectl access.


Execution Blueprint and Deployment Workflow

When deploying a new EKS environment, I generally follow this sequence.

Step 1: Initialize Terraform

terraform init

This downloads providers and initializes the backend configuration.


Step 2: Validate Configuration

terraform validate

This catches syntax errors before deployment.


Step 3: Review Infrastructure Changes

terraform plan

Never skip this step.

Always inspect the execution plan before applying changes.


Step 4: Deploy Infrastructure

terraform apply

Terraform creates:

  • VPC
  • Subnets
  • IAM Roles
  • Security Groups
  • EKS Cluster
  • Managed Node Groups

Step 5: Configure Kubernetes Access

aws eks update-kubeconfig \
--region ap-south-1 \
--name devops-eks-cluster

Verify cluster connectivity:

kubectl get nodes

If worker nodes appear successfully, your infrastructure deployment is complete.


Key Next Steps for a Production DevOps Portfolio

Provisioning EKS is only the beginning.

Real DevOps projects continue beyond cluster creation.

Here are the improvements I strongly recommend.

GitHub Actions CI/CD

Automate:

  • Terraform validation
  • Security scanning
  • Infrastructure deployment
  • Application deployment

This removes manual deployment work and reduces human error.


ArgoCD GitOps Implementation

ArgoCD continuously watches Git repositories and automatically syncs Kubernetes workloads.

Benefits include:

  • Faster deployments
  • Rollback capability
  • Deployment visibility
  • Git-based operations

Many Kubernetes teams now use Git as the single source of truth.


Observability Stack

Once applications are running, monitoring becomes essential.

A common production setup includes:

  • Prometheus
  • Grafana
  • Alertmanager

This stack helps track:

  • CPU usage
  • Memory consumption
  • Node health
  • Pod health
  • Kubernetes events

Without monitoring, troubleshooting becomes guesswork.


Real-World Learning Outcome

What I like about this Terraform AWS EKS project is that it doesn’t feel like a classroom exercise.

You’re working with networking, IAM, Kubernetes, state management, security, automation, and cloud architecture at the same time.

That’s exactly what happens in real DevOps environments.

Build it once. Break it. Rebuild it. Add GitHub Actions. Add ArgoCD. Add monitoring.

Every improvement teaches something valuable, and before long, you’ll have a portfolio project that looks remarkably close to what many DevOps engineers manage in production every day.

Real-Time Terraform IaC for AWS EKS DevOps Project

How to Create S3, EC2, Load Balancer, Ingress, and EKS Using Terraform in AWS

The first time I built a complete AWS Kubernetes environment using Terraform, I made the mistake that most beginners make—I created resources one by one from the AWS Console. It worked for a small lab. Then came a real project.

Multiple environments.

Different AWS accounts.

Several engineers.

That’s when manually creating infrastructure became a nightmare.

Terraform changed everything. Instead of clicking through dozens of AWS screens, I could define the entire infrastructure in code, review changes before deployment, and recreate the same environment whenever needed.

In this setup, we’ll provision:

  • Amazon S3 Bucket
  • AWS VPC and Networking
  • Amazon EC2 Worker Nodes
  • Amazon EKS Cluster
  • AWS Load Balancer Controller
  • Kubernetes Ingress
  • Application Load Balancer (ALB)

Everything will be managed through Terraform.


Why This Architecture Is Used in Real Projects

Most production Kubernetes deployments on AWS follow a similar pattern.

ComponentPurpose
S3Storage and Terraform backend
VPCNetwork isolation
Public SubnetsInternet-facing resources
EKSKubernetes control plane
EC2 NodesRun containers
Load Balancer ControllerCreates AWS ALBs automatically
IngressRoutes external traffic
ALBPublic entry point

The biggest advantage?

Infrastructure becomes version-controlled and reproducible.

If an environment breaks, you can rebuild it from code.


Creating the Terraform Project Structure

A clean project structure makes maintenance much easier later.

Recommended Files

terraform-eks-project/

├── provider.tf
├── s3.tf
├── networking.tf
├── eks.tf
├── alb-controller.tf
└── application.tf

Each file handles a specific responsibility.

Small files are easier to troubleshoot than one giant Terraform file containing thousands of lines.


Configure AWS, Kubernetes, and Helm Providers

provider.tf

This file establishes communication between Terraform and AWS.

It also configures:

  • AWS Provider
  • Kubernetes Provider
  • Helm Provider

A useful trick here is allowing Terraform to automatically retrieve EKS cluster information and pass authentication details to Kubernetes and Helm.

That means after EKS is created, Terraform can immediately deploy Kubernetes resources without requiring manual kubeconfig updates.

This saves a surprising amount of time during deployments.


Create an Amazon S3 Bucket

s3.tf

Most teams create an S3 bucket for one of two reasons:

  • Application storage
  • Terraform remote state storage

Example use cases:

  • Store application uploads
  • Store backups
  • Store Terraform state files

A production recommendation I always follow:

  • Enable Versioning
  • Enable Server-Side Encryption
  • Block Public Access

These settings protect your infrastructure state from accidental deletion and unauthorized access.

Why Use S3 as a Terraform Backend?

Benefits include:

  • Team collaboration
  • State locking
  • Recovery of previous versions
  • Centralized storage

Without remote state, multiple engineers can accidentally overwrite infrastructure changes.

Trust me. That situation gets messy very quickly.


Build the Networking Layer

networking.tf

Before EKS can exist, AWS needs networking resources.

You’ll typically create:

  • VPC
  • Internet Gateway
  • Route Tables
  • Public Subnets
  • Security Groups

Public Subnets Across Multiple Availability Zones

AWS requires EKS clusters to span at least two Availability Zones.

For example:

Availability ZoneSubnet
us-east-1aPublic Subnet 1
us-east-1bPublic Subnet 2

This improves availability if one zone experiences problems.

Important ALB Tagging Requirement

Many engineers miss this step.

Public subnets must be tagged correctly so the AWS Load Balancer Controller knows where to deploy Application Load Balancers.

Typical tags look like:

kubernetes.io/role/elb = 1

Without proper tagging, your Ingress resources may never receive a load balancer.


Create the Amazon EKS Cluster and EC2 Worker Nodes

eks.tf

This file creates the core Kubernetes platform.

Terraform provisions:

  • EKS Control Plane
  • IAM Roles
  • Managed Node Groups
  • Worker EC2 Instances

Why Managed Node Groups?

AWS handles many operational tasks automatically.

Examples:

  • Instance replacement
  • Rolling upgrades
  • Auto scaling integration
  • Health monitoring

That means fewer headaches for the operations team.

Typical Production Node Group Configuration

Instance Type: t3.medium
Desired Capacity: 2
Minimum Nodes: 2
Maximum Nodes: 5

For learning purposes, smaller instances help reduce AWS costs.

For production, sizing depends on application workloads.


Install AWS Load Balancer Controller

alb-controller.tf

Creating an Ingress resource alone won’t automatically create an AWS Load Balancer.

Something must watch Kubernetes events and communicate with AWS APIs.

That’s the job of the AWS Load Balancer Controller.

Configure OIDC Provider

Before installation, Terraform creates:

  • OIDC Identity Provider
  • IAM Role
  • IAM Policy

This allows Kubernetes service accounts to securely assume AWS permissions.

This security model is known as IRSA (IAM Roles for Service Accounts).

Many organizations use it because it avoids storing AWS keys inside containers.

Deploy Controller Using Helm

Terraform uses the Helm provider to install the controller directly into the EKS cluster.

Benefits include:

  • Automated installation
  • Repeatable deployments
  • Easy upgrades
  • Version control

Once installed, the controller continuously monitors Ingress resources.


Deploy the Application and Configure Ingress

application.tf

Now comes the interesting part.

We’ll deploy:

  • Kubernetes Deployment
  • Kubernetes Service
  • Kubernetes Ingress

Deploy a Sample Application

A simple NGINX deployment is commonly used for testing.

Terraform creates:

Deployment
   ↓
Service
   ↓
Ingress

Each resource serves a different purpose.

ResourcePurpose
DeploymentRuns Pods
ServiceExposes Pods
IngressRoutes external traffic

How the ALB Gets Created Automatically

The process looks like this:

Ingress Created
        ↓
AWS Load Balancer Controller Detects It
        ↓
Controller Calls AWS APIs
        ↓
Application Load Balancer Created
        ↓
Traffic Routes to Pods

No manual load balancer creation is required.

That’s one of the biggest advantages of using Kubernetes on AWS.


Deployment Steps

After placing all six Terraform files into your project directory, deployment becomes straightforward.

Step 1: Initialize Terraform

terraform init

Terraform downloads all required providers and modules.

Step 2: Review Infrastructure Changes

terraform plan

Always run this command.

Even experienced engineers do it before every deployment.

It helps catch mistakes before AWS resources are created.

Step 3: Deploy Infrastructure

terraform apply

Terraform will display a summary.

Type:

yes

to begin provisioning.

Step 4: Wait for AWS Resources

EKS deployment is not instant.

Typical creation times:

ResourceApproximate Time
VPC1–2 minutes
EKS Control Plane8–12 minutes
Node Group5–10 minutes
ALB Controller2–3 minutes

Overall deployment usually finishes within 10 to 15 minutes.

Sometimes slightly longer depending on AWS region capacity.


Verify Everything Is Working

After deployment completes:

Update your local kubeconfig.

aws eks update-kubeconfig --region us-east-1 --name my-eks-cluster

Check nodes:

kubectl get nodes

Check pods:

kubectl get pods -A

Check ingress:

kubectl get ingress

You’ll see the DNS name of the newly created AWS Application Load Balancer.

Open that URL in your browser.

If everything is configured correctly, your application should be accessible from the internet through the ALB created automatically by the AWS Load Balancer Controller.

That’s a pretty satisfying moment—especially when every component, from networking to Kubernetes, was created entirely from Terraform code.

How to Create S3, EC2, Load Balancer, Ingress, and EKS Using Terraform in AWS

How to Implement Infrastructure as Code (IaC) and Configuration Management in Kubernetes

The first time I worked on a Kubernetes project in production, I noticed something strange. The cluster looked different every few weeks even though nobody officially planned any changes.

Someone updated a deployment manually.

Another engineer modified a service using kubectl.

A few configuration values were changed directly inside the cluster.

Everything worked. Until it didn’t.

That experience taught me why Infrastructure as Code (IaC) and configuration management matter so much in Kubernetes. Once your environment grows beyond a few applications, manual changes become almost impossible to track.

The goal is simple: every server, cluster, application, policy, and configuration should exist as code inside Git. If something changes, the change should be visible, reviewable, and reproducible.

Let’s walk through how this works in a real Kubernetes environment.


Layer 1: Provisioning Kubernetes Infrastructure with IaC

Before deploying applications, you need the underlying infrastructure.

That includes:

  • Virtual networks
  • Subnets
  • Security groups
  • Firewalls
  • Load balancers
  • Kubernetes clusters
  • Cloud IAM roles

Instead of creating these resources manually from a cloud console, I always define them using code.

Choosing an Infrastructure Provisioning Tool

Most DevOps teams use one of these tools:

ToolPrimary Use
TerraformMulti-cloud infrastructure provisioning
OpenTofuOpen-source Terraform alternative
PulumiInfrastructure using programming languages
AWS CloudFormationAWS-specific provisioning
Azure BicepAzure infrastructure
Google Deployment ManagerGoogle Cloud provisioning

For most Kubernetes projects, Terraform remains the most common choice because it works across AWS, Azure, Google Cloud, VMware, and many other platforms.

Define Everything as Code

A typical Terraform project may create:

  • VPC networks
  • Public and private subnets
  • NAT gateways
  • Security groups
  • Route tables
  • EKS, AKS, or GKE clusters
  • Worker nodes
  • IAM permissions

Instead of clicking buttons inside AWS, you simply commit code like this:

terraform init
terraform plan
terraform apply

The entire infrastructure becomes repeatable.

Need another environment?

Run the same code.

Need disaster recovery?

Use the same code.

Need to rebuild after a failure?

Use the same code.

That’s the beauty of IaC.

Automate Infrastructure Deployment

Most mature teams don’t run Terraform manually.

Instead, they connect Git repositories to CI/CD tools such as:

  • GitHub Actions
  • GitLab CI/CD
  • Jenkins
  • CircleCI
  • Azure DevOps

Whenever infrastructure code changes, the pipeline automatically validates and deploys the update.

No guessing.

No forgotten steps.

No hidden changes.


Layer 2: Managing Kubernetes Workloads as Code

Once the cluster exists, the next step is managing everything running inside it.

This is where Kubernetes shines.

Almost everything in Kubernetes can be declared through YAML files.

Using Standard Kubernetes YAML Manifests

Every application starts with core Kubernetes resources.

Common examples include:

ResourcePurpose
DeploymentRuns containers
ServiceExposes applications
ConfigMapStores configuration
SecretStores sensitive values
IngressManages external traffic
NamespaceLogical separation

A Deployment file might define:

  • Container image
  • CPU allocation
  • Memory limits
  • Replica count
  • Environment variables

Instead of manually scaling or configuring containers, the YAML file becomes the source of truth.

Git stores the configuration.

Kubernetes enforces it.

Package Applications with Helm

As applications grow, raw YAML files become difficult to manage.

I have seen projects with hundreds of YAML manifests spread across multiple folders. Updating them became painful.

This is where Helm helps.

Helm works like a package manager for Kubernetes.

A Helm chart contains:

Chart.yaml
values.yaml
templates/

The templates contain reusable Kubernetes manifests.

The values file contains environment-specific settings.

For example:

Development

replicaCount: 2

Production

replicaCount: 10

The same chart can deploy both environments by simply changing values.

This reduces duplication and keeps deployments consistent.

Use Kustomize for Lightweight Customization

Not every project needs Helm.

Sometimes you only want small differences between environments.

That’s where Kustomize fits nicely.

I often use Kustomize when:

  • Replica counts differ
  • Labels change
  • Resource limits vary
  • Image tags change

A base configuration stays unchanged while overlays apply modifications.

For example:

base/
overlays/dev/
overlays/staging/
overlays/prod/

The result is cleaner than maintaining separate YAML files for every environment.


Layer 3: Implement Continuous Delivery with GitOps

One of the biggest problems in Kubernetes environments is configuration drift.

Configuration drift happens when someone changes resources directly inside the cluster.

The Git repository says one thing.

The cluster says another.

Eventually nobody knows which version is correct.

I have seen outages caused entirely by this problem.

GitOps solves it.

Deploy a GitOps Controller

Popular GitOps tools include:

  • Argo CD
  • Flux CD

These tools run inside Kubernetes itself.

Their job is simple.

Monitor Git repositories continuously.

Whenever a change appears, they apply it automatically.

Connect Git Repositories

The GitOps controller watches repositories containing:

  • Helm charts
  • YAML manifests
  • Kustomize overlays
  • Infrastructure definitions

Every change follows a standard process:

  1. Developer creates code.
  2. Pull request gets reviewed.
  3. Code merges into Git.
  4. GitOps controller detects change.
  5. Kubernetes updates automatically.

No manual kubectl commands required.

Enable Auto-Reconciliation

This feature is one of my favorites.

Suppose someone manually changes a deployment:

kubectl edit deployment

The change succeeds.

But a few minutes later, Argo CD notices the cluster no longer matches Git.

It automatically restores the desired state.

The cluster heals itself.

That’s incredibly valuable in production environments.


Alternative Approach: Kubernetes-Native Infrastructure

Some organizations want Kubernetes to manage cloud resources too.

Instead of Terraform creating databases and storage buckets, Kubernetes handles them directly.

Use Crossplane

Crossplane extends Kubernetes APIs.

After installation, you can create cloud resources using standard Kubernetes manifests.

For example:

  • Databases
  • Object storage buckets
  • Virtual networks
  • Cloud services

Everything becomes Kubernetes-native.

Cloud Provider Controllers

Cloud vendors also provide integrations.

Examples include:

  • AWS Controllers for Kubernetes (ACK)
  • Google Config Connector
  • Azure Service Operators

These tools allow Kubernetes YAML files to provision cloud resources directly.

The experience feels similar to deploying Pods and Services.


Production Best Practices for IaC and Configuration Management

Getting Kubernetes deployments working is one thing.

Running them safely in production is another.

Here are practices I always recommend.

Never Store Secrets in Git

One mistake can expose:

  • Database passwords
  • API keys
  • Access tokens

Use:

  • HashiCorp Vault
  • AWS Secrets Manager
  • Google Secret Manager
  • Azure Key Vault

Tools like External Secrets Operator can automatically inject secrets into Kubernetes during runtime.

The sensitive values never live in Git.

Enforce Policy as Code

Security checks should happen before deployment.

Popular tools include:

ToolPurpose
TrivyVulnerability scanning
CheckovIaC security scanning
KyvernoKubernetes policy enforcement
OPA GatekeeperAdmission control policies

These tools block risky configurations before they reach production.

Examples include:

  • Privileged containers
  • Missing resource limits
  • Publicly exposed services
  • Insecure permissions

Use Workload Identity Instead of Static Credentials

Many beginners place cloud access keys inside Secrets.

I strongly avoid this.

Modern Kubernetes platforms support identity-based access:

  • AWS EKS IRSA
  • Google GKE Workload Identity
  • Azure AKS Workload Identity

A Kubernetes ServiceAccount maps directly to cloud IAM permissions.

No static credentials.

No key rotation headaches.

Far better security.

Final Thoughts

When I review successful Kubernetes environments, they all share one characteristic: everything lives in Git.

Infrastructure.

Applications.

Policies.

Security controls.

Secrets integration.

Nothing depends on manual changes.

A practical setup usually looks like this:

  • Terraform or OpenTofu provisions the cluster.
  • Helm or Kustomize manages applications.
  • Argo CD or Flux synchronizes deployments.
  • External Secrets Operator handles secrets.
  • Trivy, Checkov, Kyverno, or OPA enforce security.

Once this foundation is in place, deployments become predictable, recoverable, and much easier to manage—even when your Kubernetes environment grows from a few containers to hundreds of workloads.

How to Implement Infrastructure as Code (IaC) and Configuration Management in Kubernetes

Example code for Terraform

1. providers.tf

This file configures the necessary AWS, Kubernetes, and Helm providers. It also uses data sources to automatically pass the newly created EKS cluster credentials into the Kubernetes provider.

    terraform {
    required_version = “>= 1.5.0”
    required_providers {
    aws = {
    source = “hashicorp/aws”
    version = “~> 5.0”
    }
    kubernetes = {
    source = “hashicorp/kubernetes”
    version = “~> 2.0”
    }
    helm = {
    source = “hashicorp/helm”
    version = “~> 2.0”
    }
    }
    }

    provider “aws” {
    region = “us-east-1” # Change to your preferred region
    }

    Data sources to get EKS cluster authentication:

    data “aws_eks_cluster” “cluster” {
    name = aws_eks_cluster.eks.name
    }

    data “aws_eks_cluster_auth” “cluster” {
    name = aws_eks_cluster.eks.name
    }

    provider “kubernetes” {
    host = data.aws_eks_cluster.cluster.endpoint
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
    token = data.aws_eks_cluster_auth.cluster.token
    }

    provider “helm” {
    kubernetes {
    host = data.aws_eks_cluster.cluster.endpoint
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority[0].data)
    token = data.aws_eks_cluster_auth.cluster.token
    }
    }

    s3.tf

    Creates your S3 bucket. (Optional: You can also use this bucket later as a remote state backend.

    resource “aws_s3_bucket” “app_bucket” {
    bucket = “my-unique-eks-app-bucket-2026” # Must be globally unique
    force_destroy = true
    }

    resource “aws_s3_bucket_versioning” “versioning” {
    bucket = aws_s3_bucket.app_bucket.id
    configuration {
    status = “Enabled”
    }
    }

    vpc.tf

    EKS requires subnets across at least two availability zones. Public subnets must be explicitly tagged so the AWS Load Balancer Controller knows where to place internet-facing Application Load Balancers (ALBs).

    resource “aws_vpc” “main” {
    cidr_block = “10.0.0.0/16”
    enable_dns_hostnames = true
    enable_dns_support = true
    }

    resource “aws_internet_gateway” “igw” {
    vpc_id = aws_vpc.main.id
    }

    Public Subnets (For Load Balancer & NAT Gateway):

    resource “aws_subnet” “public_1” {
    vpc_id = aws_vpc.main.id
    cidr_block = “10.0.1.0/24”
    availability_zone = “us-east-1a”
    map_public_ip_on_launch = true

    tags = {
    “kubernetes.io/role/elb” = “1” # Crucial tag for ALB discovery
    }
    }

    resource “aws_subnet” “public_2” {
    vpc_id = aws_vpc.main.id
    cidr_block = “10.0.2.0/24”
    availability_zone = “us-east-1b”
    map_public_ip_on_launch = true

    tags = {
    “kubernetes.io/role/elb” = “1”
    }
    }

    Private Subnets (For EKS EC2 Worker Nodes):

    resource “aws_subnet” “private_1” {
    vpc_id = aws_vpc.main.id
    cidr_block = “10.0.3.0/24”
    availability_zone = “us-east-1a”

    tags = {
    “kubernetes.io/role/internal-elb” = “1”
    }
    }

    resource “aws_subnet” “private_2” {
    vpc_id = aws_vpc.main.id
    cidr_block = “10.0.4.0/24”
    availability_zone = “us-east-1b”

    tags = {
    “kubernetes.io/role/internal-elb” = “1”
    }
    }

    NAT Gateway for Private Worker Nodes internet access:

    resource “aws_eip” “nat” {
    domain = “vpc”
    }

    resource “aws_nat_gateway” “nat” {
    allocation_id = aws_eip.nat.id
    subnet_id = aws_subnet.public_1.id
    }

    Routing:

    resource “aws_route_table” “public” {
    vpc_id = aws_vpc.main.id
    route {
    cidr_block = “0.0.0.0/0”
    gateway_id = aws_internet_gateway.igw.id
    }
    }

    resource “aws_route_table” “private” {
    vpc_id = aws_vpc.main.id
    route {
    cidr_block = “0.0.0.0/0”
    nat_gateway_id = aws_nat_gateway.nat.id
    }
    }

    resource “aws_route_table_association” “public_1” {
    subnet_id = aws_subnet.public_1.id
    route_table_id = aws_route_table.public.id
    }

    resource “aws_route_table_association” “public_2” {
    subnet_id = aws_subnet.public_2.id
    route_table_id = aws_route_table.public.id
    }

    resource “aws_route_table_association” “private_1” {
    subnet_id = aws_subnet.private_1.id
    route_table_id = aws_route_table.private.id
    }

    resource “aws_route_table_association” “private_2” {
    subnet_id = aws_subnet.private_2.id
    route_table_id = aws_route_table.private.id
    }

    4. eks.tf

    This creates the EKS Control Plane Cluster and an EC2 Managed Node Group (Worker Nodes).

    IAM Role for EKS Cluster Control Plane:

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

    resource “aws_iam_role_policy_attachment” “eks_cluster_policy” {
    policy_arn = “arn:aws:iam::aws:policy/AmazonEKSClusterPolicy”
    role = aws_iam_role.eks_cluster.name
    }

    EKS Cluster:

    resource “aws_eks_cluster” “eks” {
    name = “my-eks-cluster”
    role_arn = aws_iam_role.eks_cluster.arn
    version = “1.29”

    vpc_config {
    subnet_ids = [
    aws_subnet.private_1.id,
    aws_subnet.private_2.id,
    aws_subnet.public_1.id,
    aws_subnet.public_2.id
    ]
    }
    depends_on = [aws_iam_role_policy_attachment.eks_cluster_policy]
    }

    IAM Role for EC2 Worker Nodes:

    resource “aws_iam_role” “nodes” {
    name = “eks-node-group-role”
    assume_role_policy = jsonencode({
    Version = “2012-10-17”
    Statement = [{
    Action = “sts:AssumeRole”
    Effect = “Allow”
    Principal = { Service = “://amazonaws.com” }
    }]
    })
    }

    resource “aws_iam_role_policy_attachment” “amazon_eks_worker_node_policy” {
    policy_arn = “arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy”
    role = aws_iam_role.nodes.name
    }

    resource “aws_iam_role_policy_attachment” “amazon_eks_cni_policy” {
    policy_arn = “arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy”
    role = aws_iam_role.nodes.name
    }

    resource “aws_iam_role_policy_attachment” “amazon_ec2_container_registry_read_only” {
    policy_arn = “arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly”
    role = aws_iam_role.nodes.name
    }

    EC2 Node Group (Creates the underlying EC2 instances):

    resource “aws_eks_node_group” “private_nodes” {
    cluster_name = aws_eks_cluster.eks.name
    node_group_name = “private-nodes”
    node_role_arn = aws_iam_role.nodes.arn

    subnet_ids = [
    aws_subnet.private_1.id,
    aws_subnet.private_2.id
    ]

    capacity_type = “ON_DEMAND”
    instance_types = [“t3.medium”]

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

    depends_on = [
    aws_iam_role_policy_attachment.amazon_eks_worker_node_policy,
    aws_iam_role_policy_attachment.amazon_eks_cni_policy,
    aws_iam_role_policy_attachment.amazon_ec2_container_registry_read_only,
    ]
    }

    5. aws_lb_controller.tf

    To handle K8s Ingress resources and spin up an actual AWS Application Load Balancer, you must configure an OIDC Provider and deploy the AWS Load Balancer Controller via Helm.

    OIDC Provider to allow K8s service accounts to assume AWS IAM roles (IRSA):

    data “tls_certificate” “eks” {
    url = aws_eks_cluster.eks.identity[0].oidc[0].issuer
    }

    resource “aws_iam_openid_connect_provider” “eks” {
    client_id_list = [“://amazonaws.com”]
    thumbprint_list = [data.tls_certificate.eks.certificates[0].sha1_fingerprint]
    url = aws_eks_cluster.eks.identity[0].oidc[0].issuer
    }

    Fetch the official AWS Load Balancer Controller IAM Policy:

    data “http” “aws_lb_controller_policy” {
    url = “https://githubusercontent.com”
    }

    resource “aws_iam_policy” “aws_lb_controller” {
    name = “AWSLoadBalancerControllerIAMPolicy”
    path = “/”
    description = “Policy for AWS Load Balancer Controller in EKS”
    policy = data.http.aws_lb_controller_policy.response_body
    }

    IAM Role for the Controller Service Account:

    resource “aws_iam_role” “aws_lb_controller” {
    name = “aws-load-balancer-controller-role”

    assume_role_policy = jsonencode({
    Version = “2012-10-17”
    Statement = [{
    Effect = “Allow”
    Principal = {
    Federated = aws_iam_openid_connect_provider.eks.arn
    }
    Action = “sts:AssumeRoleWithWebIdentity”
    Condition = {
    StringEquals = {
    “${replace(aws_iam_openid_connect_provider.eks.url, “https://”, “”)}:sub” = “system:serviceaccount:kube-system:aws-load-balancer-controller”
    }
    }
    }]
    })
    }

    resource “aws_iam_role_policy_attachment” “aws_lb_controller” {
    policy_arn = aws_iam_policy.aws_lb_controller.arn
    role = aws_iam_role.aws_lb_controller.name
    }

    Create Kubernetes Service Account for the Controller:

    resource “kubernetes_service_account” “aws_lb_controller” {
    metadata {
    name = “aws-load-balancer-controller”
    namespace = “kube-system”
    annotations = {
    “://amazonaws.com” = aws_iam_role.aws_lb_controller.arn
    }
    }
    }

    Install AWS Load Balancer Controller via Helm:

    resource “helm_release” “aws_lb_controller” {
    name = “aws-load-balancer-controller”
    repository = “https://aws.github.io/eks-charts”
    chart = “aws-load-balancer-controller”
    namespace = “kube-system”

    set {
    name = “clusterName”
    value = aws_eks_cluster.eks.name
    }
    set {
    name = “serviceAccount.create”
    value = “false”
    }
    set {
    name = “serviceAccount.name”
    value = kubernetes_service_account.aws_lb_controller.metadata[0].name
    }
    depends_on = [aws_eks_node_group.private_nodes]
    }

    6. app_and_ingress.tf

    Finally, deploy a sample application, expose it via a Kubernetes Service, and define the Ingress resource. The AWS Load Balancer Controller will notice this resource and automatically spin up a real AWS Application Load Balancer (ALB).

    Sample Nginx Deployment:

    resource “kubernetes_deployment” “sample_app” {
    metadata {
    name = “sample-nginx-app”
    labels = { app = “nginx” }
    }

    spec {
    replicas = 2
    selector { match_labels = { app = “nginx” } }
    template {
    metadata { labels = { app = “nginx” } }
    spec {
    container {
    image = “nginx:latest”
    name = “nginx”
    port { container_port = 80 }
    }
    }
    }
    }
    depends_on = [helm_release.aws_lb_controller]
    }

    NodePort Service (Required by AWS ALB Ingress):

    resource “kubernetes_service” “sample_service” {
    metadata {
    name = “sample-nginx-service”
    }
    spec {
    selector = { app = “nginx” }
    port {
    port = 80
    target_port = 80
    }
    type = “NodePort”
    }
    }

    K8s Ingress (Triggers creation of the AWS Load Balancer):

    resource “kubernetes_ingress_v1” “app_ingress” {
    metadata {
    name = “app-ingress”
    annotations = {
    “kubernetes.io/ingress.class” = “alb”
    “alb.ingress.kubernetes.io/scheme” = “internet-facing” # Provisions a public ALB
    “alb.ingress.kubernetes.io/target-type” = “ip”
    }
    }

    spec {
    rule {
    http {
    path {
    path = “/”
    path_type = “Prefix”
    backend {
    service {
    name = kubernetes_service.sample_service.metadata[0].name
    port { number = 80 }
    }
    }
    }
    }
    }
    }
    }

    Deployment Steps:

    Put all 6 files in an empty folder.
    Initialize the directory: terraform init.
    Review what will be built: terraform plan.
    Provision everything: terraform apply -auto-approve.

    Note: It can take 10 to 15 minutes for the AWS EKS cluster control plane and EC2 node groups to cleanly spin up.Once completed, you can fetch your public ALB URL using the AWS Console or via your local kubectl utility.

    Leave a Comment