GCP for DevOps — IaC Exercises
Infrastructure as Code — Practice Exercises
Test your knowledge of Terraform with the Google provider, remote state, modules, and CFT.
Exercise 1 — VPC with Terraform
Write Terraform to create a VPC with two subnets, a Cloud NAT, and a firewall rule allowing HTTP traffic.
Show Answer
# main.tf
provider "google" {
project = var.project_id
region = "us-central1"
}
resource "google_compute_network" "vpc" {
name = "exercise-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "public" {
name = "public-subnet"
ip_cidr_range = "10.0.1.0/24"
region = "us-central1"
network = google_compute_network.vpc.id
}
resource "google_compute_subnetwork" "private" {
name = "private-subnet"
ip_cidr_range = "10.0.2.0/24"
region = "us-central1"
network = google_compute_network.vpc.id
private_ip_google_access = true
}
resource "google_compute_router" "router" {
name = "exercise-router"
region = "us-central1"
network = google_compute_network.vpc.id
}
resource "google_compute_router_nat" "nat" {
name = "exercise-nat"
router = google_compute_router.router.name
region = "us-central1"
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}
resource "google_compute_firewall" "allow_http" {
name = "exercise-allow-http"
network = google_compute_network.vpc.name
allow {
protocol = "tcp"
ports = ["80", "443"]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["http-server"]
}
$ terraform init && terraform plan
$ terraform apply
Exercise 2 — Remote State in GCS
Set up a GCS bucket for Terraform state with versioning and configure the backend.
Show Answer
# Create state bucket
$ gcloud storage buckets create gs://exercise-tf-state-$(gcloud config get-value project) \
--location=us-central1 \
--uniform-bucket-level-access
$ gcloud storage buckets update gs://exercise-tf-state-$(gcloud config get-value project) \
--versioning
# backend.tf
terraform {
backend "gcs" {
bucket = "exercise-tf-state-MY_PROJECT_ID"
prefix = "exercises/vpc"
}
}
# Migrate to remote backend
$ terraform init -migrate-state
# Verify state is remote
$ terraform state list
Exercise 3 — Reusable Module
Create a reusable Cloud Run module that accepts image, service name, region, and min/max instances as variables.
Show Answer
# modules/cloud-run/variables.tf
variable "project_id" { type = string }
variable "service_name" { type = string }
variable "region" { type = string; default = "us-central1" }
variable "image" { type = string }
variable "min_instances" { type = number; default = 0 }
variable "max_instances" { type = number; default = 10 }
variable "port" { type = number; default = 8080 }
variable "env_vars" { type = map(string); default = {} }
# modules/cloud-run/main.tf
resource "google_cloud_run_v2_service" "service" {
name = var.service_name
location = var.region
project = var.project_id
template {
scaling {
min_instance_count = var.min_instances
max_instance_count = var.max_instances
}
containers {
image = var.image
ports {
container_port = var.port
}
dynamic "env" {
for_each = var.env_vars
content {
name = env.key
value = env.value
}
}
}
}
}
# modules/cloud-run/outputs.tf
output "url" {
value = google_cloud_run_v2_service.service.uri
}
output "service_name" {
value = google_cloud_run_v2_service.service.name
}
Usage:
module "api" {
source = "./modules/cloud-run"
project_id = var.project_id
service_name = "my-api"
image = "us-central1-docker.pkg.dev/my-project/repo/api:v1.0.0"
min_instances = 1
max_instances = 50
env_vars = { ENV = "production", LOG_LEVEL = "info" }
}
Exercise 4 — Import Existing Resource
You have an existing Cloud Storage bucket created manually. Import it into Terraform state and manage it.
Show Answer
# Write the resource configuration first
resource "google_storage_bucket" "existing" {
name = "my-existing-bucket"
location = "US"
force_destroy = false
versioning {
enabled = true
}
uniform_bucket_level_access = true
}
# Import the existing bucket into state
$ terraform import google_storage_bucket.existing my-existing-bucket
google_storage_bucket.existing: Importing from ID "my-existing-bucket"...
google_storage_bucket.existing: Import prepared!
google_storage_bucket.existing: Refreshing state...
Import successful!
# Verify — plan should show no changes if config matches
$ terraform plan
No changes. Your infrastructure matches the configuration.
Exercise 5 — CFT Network Module
Use the CFT terraform-google-modules/network/google module to create a VPC with a subnet and secondary ranges for GKE.
Show Answer
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 9.0"
project_id = var.project_id
network_name = "exercise-cft-vpc"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "app-subnet"
subnet_ip = "10.0.0.0/20"
subnet_region = "us-central1"
subnet_private_access = true
},
]
secondary_ranges = {
"app-subnet" = [
{ range_name = "pods", ip_cidr_range = "10.4.0.0/14" },
{ range_name = "services", ip_cidr_range = "10.8.0.0/20" },
]
}
}
output "network_name" {
value = module.vpc.network_name
}
output "subnets" {
value = module.vpc.subnets
}
$ terraform init
$ terraform plan
$ terraform apply
Cleanup
$ terraform destroy -auto-approve
$ gcloud storage rm -r gs://exercise-tf-state-$(gcloud config get-value project)
Next Chapter → Serverless