Introduction

Infrastructure as Code (IaC) has completely changed how modern cloud environments are deployed and managed.

Instead of manually creating:

  • VPC networks
  • Cloud SQL databases
  • Compute instances
  • Load balancers
  • Autoscaling policies

Modern cloud environments require infrastructure that is:

  • Repeatable
  • Version-controlled
  • Automated
  • Scalable
  • Easy to maintain

we can define the entire cloud architecture using Terraform and deploy it consistently. Manually creating cloud resources through the console works for experimentation, but enterprise environments need an Infrastructure as Code (IaC) approach.

In this hands-on guide, we will deploy a complete 3-tier web application architecture on Google Cloud using Terraform.

The solution includes:

  • Custom VPC Network
  • Regional Subnet
  • Cloud NAT Gateway
  • Cloud SQL PostgreSQL Database
  • Compute Engine Managed Instance Group
  • Auto Scaling
  • External HTTP Load Balancer
  • Health Checks
  • Terraform Remote State using Cloud Storage

If you are new to Infrastructure as Code, check my Terraform beginner guide before continuing.

We will build a production-style architecture:

archite
archi 2

Architecture Components

The final infrastructure contains:

ComponentResource Name
VPC Networkcepf-vpc
Subnetcepf-subnet
Cloud SQL Instancecepf-instance
Databasecepf-db
Database Userpostgres
Instance Templatecepf-template-v2
Managed Instance Groupcepf-infra-lb-group1-mig
Backend Servicecepf-infra-lb-backend-default
HTTP Load Balancercepf-infra-lb
Health Checkcepf-http-health-check
Cloud NATcepf-nat

Project Structure

Terraform configuration:

cepf-iac

├── backend.tf
├── provider.tf
├── main.tf
├── variables.tf
├── network.tf
├── sql.tf
├── instance_template.tf
├── mig.tf
├── loadbalancer.tf
├── nat.tf
└── outputs.tf

Why Terraform?

Terraform allows us to define infrastructure using configuration files instead of manually clicking through the cloud console.

Traditional approach:

Console → Create Network
→ Create VM
→ Configure Firewall
→ Setup Database
→ Configure Load Balancer

Terraform approach:

Terraform Code
|
|
terraform apply
|
|
Google Cloud Infrastructure

Benefits:

  • Infrastructure versioning
  • Repeatable deployments
  • Disaster recovery
  • Automated provisioning
  • Collaboration through Git

The deployed architecture contains three logical layers.

1. Presentation Layer

Responsible for receiving user traffic.

Components:

  • Global External HTTP Load Balancer
  • Backend Service
  • Health Check

Traffic flow:

Client
|
|
HTTP Load Balancer
|
|
Backend Service

2. Application Layer

The application layer runs inside Compute Engine instances.

Components:

  • Instance Template
  • Regional Managed Instance Group
  • Autoscaler

Configuration:

ParameterValue
Regionus-west1
MIG Namecepf-infra-lb-group1-mig
Minimum Instances2
Maximum Instances4
CPU Threshold60%
OSDebian 12

Autoscaling:

CPU > 60%

2 VM
|
|
Scale Out
|
|
4 VM

3. Database Layer

Database layer uses:

  • Cloud SQL
  • PostgreSQL 14

Configuration:

ParameterValue
Instance Namecepf-instance
Database EnginePostgreSQL 14
Databasecepf-db
Userpostgres
Passwordpostgres

Application communicates with Cloud SQL using:

Application VM
|
|
Cloud SQL Proxy
|
|
Cloud SQL PostgreSQL

Prerequisites

Before starting, ensure:

  • Google Cloud Skills Boost lab is active
  • Terraform installed
  • gcloud configured
  • Project ID available

Verify:

terraform version

Example:

Terraform v1.x.x

Check project:

gcloud config get-value project

Output:

qwiklabs-gcp-03-838d0a020856

Step 1: Connect to Lab Setup VM

From Cloud Shell:

gcloud compute ssh lab-setup

Verify:

whoami

Output:

student-01-xxxx

Step 2: Create Terraform Workspace

Create project folder:

mkdir cepf-iac

cd cepf-iac

Install Terraform

wget https://releases.hashicorp.com/terraform/1.12.2/terraform_1.12.2_linux_amd64.zip

image

Check Terraform:

terraform version
image

Create files:

image

IAC Configuration

1. Terraform Backend Configuration

Terraform state is stored remotely in Google Cloud Storage.

backend.tf
terraform {

backend "gcs" {

bucket = "qwiklabs-gcp-03-838d0a020856-bucket-tfstate"

}

}

Terraform state:

gs://qwiklabs-gcp-03-838d0a020856-bucket-tfstate/default.tfstate

2. Provider Configuration

provider.tf
terraform {

required_providers {

google = {

source = "hashicorp/google"

version = "~>5.0"

}

}

}


provider "google" {

project = "qwiklabs-gcp-03-838d0a020856"

region = "us-west1"

zone = "us-west1-a"

}

3. Enable Google Cloud APIs

main.tf
resource "google_project_service" "compute" {

service="compute.googleapis.com"

}


resource "google_project_service" "sql" {

service="sqladmin.googleapis.com"

}

4. Create Custom VPC Network

network.tf
resource "google_compute_network" "vpc" {

name="cepf-vpc"

auto_create_subnetworks=false

}


resource "google_compute_subnetwork" "subnet" {

name="cepf-subnet"

region="us-west1"

network=google_compute_network.vpc.id

ip_cidr_range="10.10.0.0/24"

}

Allow HTTP Traffic

The load balancer requires HTTP access.

resource "google_compute_firewall" "allow_http" {

name="allow-http"

network=google_compute_network.vpc.name


allow {

protocol="tcp"

ports=["80"]

}


source_ranges=["0.0.0.0/0"]

}

5. Cloud SQL PostgreSQL Deployment

sql.tf
resource "google_sql_database_instance" "postgres" {


name="cepf-instance"


database_version="POSTGRES_14"


region="us-west1"



settings {

tier="db-f1-micro"


ip_configuration {

ipv4_enabled=true

}


}

}



resource "google_sql_database" "db" {


name="cepf-db"


instance=google_sql_database_instance.postgres.name


}



resource "google_sql_user" "user" {


name="postgres"


instance=google_sql_database_instance.postgres.name


password="postgres"


}

Created:

Instance:
cepf-instance


Database:
cepf-db


Username:
postgres


Password:
postgres

6. Cloud NAT Configuration

The VM instances do not have external IP addresses.

Therefore Cloud NAT provides outbound internet access for:

  • package installation
  • downloading Cloud SQL Proxy
  • application dependencies
nat.tf
resource "google_compute_router" "router" {


name="cepf-router"


region="us-west1"


network=google_compute_network.vpc.id


}



resource "google_compute_router_nat" "nat" {


name="cepf-nat"


router=google_compute_router.router.name


region="us-west1"


nat_ip_allocate_option="AUTO_ONLY"



source_subnetwork_ip_ranges_to_nat=
"ALL_SUBNETWORKS_ALL_IP_RANGES"


}

7. Instance Template

This was the most important component.

The VM startup script performs:

  1. Download Flask application
  2. Install Python dependencies
  3. Install Cloud SQL Proxy
  4. Connect to PostgreSQL
  5. Start Flask application
instance_template.tf
resource "google_compute_instance_template" "template" {


name="cepf-template-v2"


machine_type="e2-medium"



disk {


source_image=
"projects/debian-cloud/global/images/family/debian-12"


auto_delete=true


boot=true


}



network_interface {


subnetwork=
google_compute_subnetwork.subnet.id


}



service_account {


email=
"603008115106-compute@developer.gserviceaccount.com"


scopes=[

"https://www.googleapis.com/auth/cloud-platform"

]


}



metadata_startup_script = <<SCRIPT

#!/bin/bash


apt-get update


apt-get install unzip python3-venv wget -y



gsutil cp gs://cloud-training/cepf/cepf020/flask_cloudsql_example_v1.zip /tmp/


cd /tmp


unzip flask_cloudsql_example_v1.zip



cd flask_cloudsql_example/sqlalchemy



curl -o cloud-sql-proxy \
https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.15.2/cloud-sql-proxy.linux.amd64


chmod +x cloud-sql-proxy



./cloud-sql-proxy \
qwiklabs-gcp-03-838d0a020856:us-west1:cepf-instance &



python3 -m venv env


source env/bin/activate



pip install -r requirements.txt



sed -i 's/127.0.0.1/0.0.0.0/g' app.py



nohup python app.py &


SCRIPT

}

8. Managed Instance Group

mig.tf

Important fix:

For regional MIG, Terraform does not support:

region="us-west1"

inside the resource.

Correct:

resource "google_compute_region_instance_group_manager" "mig" {


name="cepf-infra-lb-group1-mig"



region="us-west1"



base_instance_name="cepf-vm"



version {


instance_template=
google_compute_instance_template.template.id


}



target_size=2



named_port {


name="http"


port=80


}


}

Autoscaling Configuration

resource "google_compute_region_autoscaler" "autoscaler" {


name="cepf-autoscaler"


region="us-west1"



target=
google_compute_region_instance_group_manager.mig.id



autoscaling_policy {


min_replicas=2


max_replicas=4



cpu_utilization {


target=0.6


}


}

}

9. Load Balancer Configuration

Health Check

The first deployment failed because Backend Service requires a health check.

Added:

resource "google_compute_health_check" "http_health_check" {


name="cepf-http-health-check"



http_health_check {


port=80


request_path="/"


}


}

Backend Service
resource "google_compute_backend_service" "backend" {


name="cepf-infra-lb-backend-default"



protocol="HTTP"



health_checks=[
google_compute_health_check.http_health_check.id
]



backend {


group=
google_compute_region_instance_group_manager.mig.instance_group


}


}

URL Map
resource "google_compute_url_map" "map" {


name="cepf-map"



default_service=
google_compute_backend_service.backend.id


}

Target HTTP Proxy
resource "google_compute_target_http_proxy" "proxy" {


name="cepf-proxy"



url_map=
google_compute_url_map.map.id


}

Forwarding Rule
resource "google_compute_global_forwarding_rule" "lb" {


name="cepf-infra-lb"



target=
google_compute_target_http_proxy.proxy.id



port_range="80"


}

Deployment Commands

Initialize:

terraform init

Validate:

terraform validate
image

Plan:

terraform plan
image
image
image
image
image

Deploy:

terraform apply
image
image
image

Verification

Check MIG
gcloud compute instance-groups managed list \
--regions us-west1

Expected:

cepf-infra-lb-group1-mig
SIZE 2

Check Application Health
gcloud compute backend-services get-health \
cepf-infra-lb-backend-default \
--global

Expected:

healthState: HEALTHY

Get Load Balancer IP
gcloud compute forwarding-rules list

Example:

NAME
cepf-infra-lb

IP:
8.xxx.xxx.xxx

Open:

http://LOAD_BALANCER_IP

Troubleshooting Issues Encountered

Issue 1: Backend Service Creation Failed

Error:

At least one health check needs to be specified

Solution:

Added:

google_compute_health_check

Issue 2: Terraform Invalid Expression

Problem:

group=
google_compute_instance_group_manager.mig.instance_group

Terraform requires same line:

Correct:

group = google_compute_instance_group_manager.mig.instance_group

Issue 3: Cloud SQL Proxy Permission

Error:

metadata service account token not defined

Solution:

Attach service account:

roles/cloudsql.client

and use:

service_account {}

Issue 4: MIG Update Failure

Error:

maxSurge fixed has to equal number of zones

Solution:

Regional MIG uses multiple zones.

Changed update policy:

max_surge_fixed = 0

Final Result

We successfully deployed:

  • Fully automated Google Cloud infrastructure
  • Terraform managed state
  • Regional Managed Instance Group
  • Autoscaling
  • External HTTP Load Balancer
  • Flask application
  • Cloud SQL PostgreSQL backend
  • Cloud SQL Proxy connectivity
  • High availability architecture

This project demonstrates how Terraform can automate a complete cloud-native application platform using Google Cloud services.

🚀 Join the GeekyMukesh Community

We don’t spam! Read our privacy policy for more info.

Leave a Reply

Your email address will not be published. Required fields are marked *