Terraform for GKE: Fundamentals and Advanced Project Structure
This blog post is based on a sample project which you can find on github.

This guide covers Terraform basics and best practices for both beginners and experienced cloud engineers, using a real-world GKE (Google Kubernetes Engine) cluster project as an example.
1. Introduction
Terraform is an open-source Infrastructure as Code (IaC) tool that lets you safely and predictably create, change, and improve infrastructure. This guide uses a real GCP/GKE project to demonstrate both fundamental and advanced Terraform patterns.
2. Terraform Fundamentals
2.1 Providers and Resources
provider "google" {
project = var.project_id
region = var.region
}
resource "google_container_cluster" "gke" {
name = var.cluster_name
location = var.region
# ... other configuration ...
}
Provider connects Terraform to GCP (or AWS, Azure, etc).
Resource defines a specific piece of infrastructure (e.g., GKE cluster).
2.2 Variables and Outputs
variable "project_id" {
description = "GCP Project ID"
type = string
}
output "cluster_endpoint" {
value = google_container_cluster.gke.endpoint
}
3. Advanced Project Structure: Modules and Environments
3.1 What Are Terraform Modules?
A Terraform module is a folder that organizes related resources for a specific purpose—such as networking, Kubernetes, or security. Modules make your code DRY, reusable, and easier to maintain. Each environment (like dev, prod) can call the same modules with different inputs.
3.2 Cloud Modules Used in This Project
Main cloud components/resources used in this project.
VPC (Virtual Private Cloud)
Subnet
Bastion Host
Cloud NAT
Firewall Rules
GKE Private Cluster
VPC (Virtual Private Cloud)
Subnet
Bastion Host
Cloud NAT
Firewall Rules
GKE Private Cluster
1. Networking (VPC, Subnet, Firewall, NAT) Module
Creates your own virtual private cloud (VPC) network for all your resources.
Defines subnets for better network segmentation.
Adds firewall rules for secure, controlled access.
Sets up Cloud NAT for outbound internet access for private resources.
VPC is your isolated cloud network.
Subnets keep environments and workloads organized.
Firewall secures all communication.
NAT keeps your nodes private, but able to access the internet for updates.
2. GKE Cluster Module
Provisions a secure, managed Kubernetes (GKE) cluster attached to your VPC and subnet.
Sets up private nodes, authorized access, and node pools.
Automates complex GKE cluster creation.
Ensures your cluster is only reachable by secure routes (e.g., from bastion).
3. Bastion Host Module

Creates a jump (bastion) host in a private subnet.
Enables secure admin access via Proxy server (Tiny Proxy).
We can follow this blog post to install Tiny Proxy in Bastion VM/server.
Lets administer SSH into the cluster securely, without exposing public IPs.
A key requirement for private GKE clusters.
How to access GKE cluster
1. Configure Jump Host to access via proxy
#Bash
gcloud compute ssh jump-host \
--tunnel-through-iap \
--project=trs-project-386114 \
--zone=europe-north1-a \
--ssh-flag="-4 -L8888:localhost:8888 -N -q -f"
2. Use proxy to access control pane of GKE private cluster
export HTTPS_PROXY=localhost:8888
kubectl get nodes
3.1 Why Use Modules?
Encapsulate logic for re-use across environments (dev, prod, etc).
Organize code for easier collaboration and maintenance.
3.2 Example Directory Layout
terraform-project/
├── modules/
│ ├── networking/
│ ├── gke_cluster/
│ └── bastion_host/
├── environments/
│ └── dev/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
├── versions.tf
├── .gitignore
└── README.md
environments/: Each environment (dev, prod, etc) gets its own configs, calling the same modules. Because of this structure, modules become reusable across multiple environments.
4. Example: Calling Modules in terraform-project/environments/dev/main.tf
module "networking" {
source = "../../modules/networking"
project_id = var.project_id
region = var.region
network_name = "gke-dev-vpc"
subnet_name = "gke-dev-subnet"
subnet_cidr = "10.10.0.0/20"
}
module "gke_cluster" {
source = "../../modules/gke_cluster"
project_id = var.project_id
location = var.zone
cluster_name = "private-dev-cluster"
network_name = module.networking.network_name
subnetwork_name = module.networking.subnet_name
}
4.1 Typical Terraform Workflow
Here’s how you run your infrastructure with Terraform for each environment (e.g., dev, staging, prod):
Navigate to the desired environment directory:
cd environments/dev
Change to the folder containing your environment’s root Terraform configs.
Initialize Terraform:
terraform init
Downloads required providers and sets up your local working directory for this project.
Validate Terraform:
terraform validate
Purpose:
Checks whether your Terraform configuration files are syntactically and structurally correct.
Key Features:
Use case:
Useful early in development, especially in CI/CD pipelines, to catch typos or malformed configs.
Review the plan:
terraform plan
Shows what Terraform will do before making any real changes—review carefully!
What does terraform plan do?
The terraform plan command performs a dry run to preview the changes Terraform would make to your infrastructure—without actually applying them.
It compares your code (what you want) with the current state (what exists) and shows:
- ✅ What will be created (+)
- ๐ What will be changed (~)
- ❌ What will be destroyed (-)
This step is crucial for verifying that your changes are intentional and safe.
How to verify the plan output
- Look for + for new resources you expect (e.g., new cluster, VPC)
- Check ~ to confirm the exact changes you want
- Be cautious with - to avoid destroying live resources
- Make sure resource names, types, and configurations match expectations
Example output snippet:
Terraform will perform the following actions:
# google_compute_network.vpc will be created
+ name = "gke-dev-vpc"
+ auto_create_subnetworks = false
# google_container_cluster.primary will be created
+ name = "private-dev-cluster"
+ network = "gke-dev-vpc"
Plan: 2 to add, 0 to change, 0 to destroy.
Example Resource:
Apply the changes:
terraform apply
Creates, updates, or destroys infrastructure to match your configuration.
5. Special Files: terraform.tfvars and versions.tf
terraform.tfvars
Supplies actual values for your variables (e.g. project_id, region, zone).
Allows easy environment switching and secrets separation.
project_id = "mycom-project"
region = "europe-north1"
Best Practice:
versions.tf
terraform {
required_version = ">= 1.3"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.23"
}
}
}
Best Practice:
6. Advanced Concepts for Cloud Engineers
Remote state: Store .tfstate in GCS for team use.
Workspaces: Use for easy multi-environment setups.
State locking and locking backends: Prevents race conditions in CI/CD.
Sensitive outputs: Mark outputs as sensitive for secrets.
7. Tips & Gotchas
Never commit .tfstate or real secrets.
Test modules in isolation.
Use module outputs to chain resources.
Read Terraform docs for new features and best practices.
8. Conclusion
A modular, environment-driven project structure lets teams scale IaC from dev to prod with safety and speed. Start with the basics and grow your repo’s complexity as your needs evolve!