Amazon EKS — Terraform Project Structure
A well-organized Terraform project separates concerns into modules, uses remote state, and supports multiple environments without code duplication.
Recommended Directory Layout
terraform-eks/
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── eks/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── addons/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ │ └── ...
│ └── production/
│ └── ...
└── README.md
Remote State with S3
# environments/dev/backend.tf
terraform {
backend "s3" {
bucket = "my-company-terraform-state"
key = "eks/dev/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Create State Infrastructure
# Create S3 bucket
aws s3api create-bucket --bucket my-company-terraform-state --region us-east-1
# Enable versioning
aws s3api put-bucket-versioning --bucket my-company-terraform-state \
--versioning-configuration Status=Enabled
# Enable encryption
aws s3api put-bucket-encryption --bucket my-company-terraform-state \
--server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms"}}]}'
# Create DynamoDB lock table
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1
Provider Configuration
# environments/dev/main.tf
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.25"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.12"
}
}
}
provider "aws" {
region = var.region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = "eks-platform"
}
}
}
# Kubernetes provider — configured after cluster creation
provider "kubernetes" {
host = module.eks.cluster_endpoint
cluster_ca_certificate = base64decode(module.eks.cluster_ca_certificate)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name]
}
}
provider "helm" {
kubernetes {
host = module.eks.cluster_endpoint
cluster_ca_certificate = base64decode(module.eks.cluster_ca_certificate)
exec {
api_version = "client.authentication.k8s.io/v1beta1"
command = "aws"
args = ["eks", "get-token", "--cluster-name", module.eks.cluster_name]
}
}
}
Environment Variables
# environments/dev/variables.tf
variable "region" {
type = string
default = "us-east-1"
}
variable "environment" {
type = string
default = "dev"
}
variable "cluster_name" {
type = string
default = "dev-eks"
}
variable "cluster_version" {
type = string
default = "1.31"
}
variable "vpc_cidr" {
type = string
default = "10.0.0.0/16"
}
# environments/dev/terraform.tfvars
region = "us-east-1"
environment = "dev"
cluster_name = "dev-eks"
cluster_version = "1.31"
vpc_cidr = "10.0.0.0/16"
Module Composition
# environments/dev/main.tf (continued)
module "vpc" {
source = "../../modules/vpc"
cluster_name = var.cluster_name
environment = var.environment
vpc_cidr = var.vpc_cidr
}
module "eks" {
source = "../../modules/eks"
cluster_name = var.cluster_name
cluster_version = var.cluster_version
environment = var.environment
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
}
module "addons" {
source = "../../modules/addons"
cluster_name = module.eks.cluster_name
oidc_provider = module.eks.oidc_provider
oidc_issuer_url = module.eks.oidc_issuer_url
}
Best Practices
| Practice | Why |
|---|---|
| One state per environment | Blast radius isolation |
| Pin provider versions | Prevent unexpected breaking changes |
Use default_tags |
Consistent tagging without repeating |
| Store state in S3 + DynamoDB | Team collaboration + locking |
| Use modules for reusability | DRY across environments |
Keep terraform.tfvars per env |
Environment-specific values |