Create GKE Cluster with Terraform/Opentofu 0x02
Part 2 of this post
Table of Contents
GKE Module
This module is where everything comes together—network, IAM, and node pools—to create a working GKE cluster.
Standard vs Autopilot
| Feature | Standard | Autopilot |
|---|---|---|
| Node control | Full | None |
| Scaling | Manual + autoscaler | Fully managed |
| Operations | You manage | Google manages |
| Flexibility | High | Limited |
Standard
Gives you full control over nodes and scaling.
- removes default node pool - you manage it separately
- uses VPC-native networking (pods/services ranges)
- private cluster - nodes have no public IP
- uses Workload Identity for secure access
modules/gke/standard/main.tf
1resource "google_container_cluster" "gke" {
2 name = var.cluster_name
3 location = var.region
4 remove_default_node_pool = true
5 initial_node_count = var.initial_node_count
6 network = var.network
7 subnetwork = var.subnetwork
8 networking_mode = "VPC_NATIVE"
9
10 deletion_protection = false
11
12 # Optional, if you want multi-zonal cluster
13 # node_locations = ["us-central1-b"]
14
15 node_config {
16 disk_size_gb = 15
17 machine_type = "e2-micro"
18 disk_type = "pd-balanced"
19 }
20
21 addons_config {
22 http_load_balancing {
23 disabled = true
24 }
25 horizontal_pod_autoscaling {
26 disabled = false
27 }
28 }
29
30 release_channel {
31 channel = "REGULAR"
32 }
33
34 workload_identity_config {
35 workload_pool = "${var.project_id}.svc.id.goog"
36 }
37
38 ip_allocation_policy {
39 cluster_secondary_range_name = "k8s-pods"
40 services_secondary_range_name = "k8s-services"
41 }
42
43 private_cluster_config {
44 enable_private_nodes = true
45 enable_private_endpoint = false
46 master_ipv4_cidr_block = "192.168.0.0/28"
47 }
48}
modules/gke/standard/outputs.tf
1output "cluster_id" {
2 value = google_container_cluster.gke.id
3}
4
5output "endpoint" {
6 value = google_container_cluster.gke.endpoint
7}
8
9output "ca_certificate" {
10 value = google_container_cluster.gke.master_auth[0].cluster_ca_certificate
11}
Autopilot
Fully managed—no node pool management required.
- no node pools to manage
- google handles scaling, upgrades, and operations
- best for simplicity and faster setup
modules/gke/standard/main.tf
1resource "google_container_cluster" "gke" {
2 name = var.cluster_name
3 location = var.region
4
5 enable_autopilot = true
6
7 network = var.network
8 subnetwork = var.subnetwork
9 networking_mode = "VPC_NATIVE"
10
11 deletion_protection = false
12
13 addons_config {
14 http_load_balancing {
15 disabled = false
16 }
17 horizontal_pod_autoscaling {
18 disabled = false
19 }
20 }
21
22 release_channel {
23 channel = "REGULAR"
24 }
25
26 workload_identity_config {
27 workload_pool = "${var.project_id}.svc.id.goog"
28 }
29
30 ip_allocation_policy {
31 cluster_secondary_range_name = "k8s-pods"
32 services_secondary_range_name = "k8s-services"
33 }
34
35 private_cluster_config {
36 enable_private_nodes = true
37 enable_private_endpoint = false
38 master_ipv4_cidr_block = "192.168.0.0/28"
39 }
40}
modules/gke/autopilot/outputs.tf
1output "cluster_id" {
2 value = google_container_cluster.gke.id
3}
4
5output "endpoint" {
6 value = google_container_cluster.gke.endpoint
7}
8
9output "ca_certificate" {
10 value = google_container_cluster.gke.master_auth[0].cluster_ca_certificate
11}
Storage Module
In GKE, a default storage class is already created for you. This means you can provision persistent volumes out of the box without defining anything.
This module only matters if you want to customize storage behavior—such as disk type, reclaim policy, or default class.
We define two storage classes:
- balance (default) - general-purpose workloads
- SSD - high-performance workloads
module/storage/main.tf
1resource "kubernetes_storage_class" "balanced" {
2 metadata {
3 name = var.balanced_sc_name
4 }
5
6 storage_provisioner = "pd.csi.storage.gke.io"
7
8 parameters = {
9 type = "pd-balanced"
10 }
11
12 reclaim_policy = "Retain"
13 volume_binding_mode = "WaitForFirstConsumer"
14
15 allow_volume_expansion = true
16}
17
18resource "kubernetes_storage_class" "ssd" {
19 metadata {
20 name = var.ssd_sc_name
21
22 }
23
24 storage_provisioner = "pd.csi.storage.gke.io"
25
26 parameters = {
27 type = "pd-ssd"
28 }
29
30 reclaim_policy = "Retain"
31 volume_binding_mode = "WaitForFirstConsumer"
32
33 allow_volume_expansion = true
34}
35
36resource "kubernetes_annotations" "default_storageclass" {
37 api_version = "storage.k8s.io/v1"
38 kind = "StorageClass"
39 metadata {
40 name = var.balanced_sc_name
41 }
42
43 annotations = {
44 "storageclass.kubernetes.io/is-default-class" = "true"
45 }
46
47 force = true
48}
Addons Module
These are not required for GKE to function, but they are critical for real-world applications.
In this module, we install two key components using Helm:
- Ingress NGINX - exposes services to the internet
- cert-manager - manages TLS certificates automatically
Ingress-Nginx
Ingress is how external traffic reaches services inside your cluster.
modules/addons/ingress-nginx/main.tf
1resource "helm_release" "nginx_ingress" {
2 name = "ingress-nginx"
3 repository = "https://kubernetes.github.io/ingress-nginx"
4 chart = "ingress-nginx"
5 namespace = "ingress-nginx"
6
7 create_namespace = true
8
9 set {
10 name = "controller.publishService.enabled"
11 value = "true"
12 }
13}
cert-manager
Automates the creation and renewal of SSL/TLS certificates inside Kubernetes.
modules/addons/cert-manager/main.tf
1resource "helm_release" "cert_manager" {
2 name = "cert-manager"
3 namespace = "cert-manager"
4 repository = "https://charts.jetstack.io"
5 chart = "cert-manager"
6 version = "v1.20.1"
7
8 create_namespace = true
9
10 values = [<<EOF
11crds:
12 enabled: true
13
14global:
15 leaderElection:
16 namespace: cert-manager
17EOF
18 ]
19}