Compute Engine: Deploying a VM with Firewall Rules
Scenario
You are deploying a simple Nginx web server on Google Cloud Platform. You need to provision a Compute Engine virtual machine (VM) and ensure it's accessible from the internet on port 80. In GCP, firewall rules are applied to VMs using Network Tags rather than Security Groups.
- Create a google_compute_instance running a Debian 11 image (minimum e2-micro machine type).
- Assign a network tag (e.g., web-server) to the instance.
- Use a metadata_startup_script to install and start Nginx.
- Create a google_compute_firewall rule that allows ingress on TCP port 80 for instances with that target tag, sourced from 0.0.0.0/0.
- Output the public IP address of the instance.
Additional Context
Unlike AWS where Security Groups are attached to instances via ID, GCP uses Network Tags. You tag an instance (e.g., allow-http), and then create a firewall rule targeting that tag (target_tags = ["allow-http"]).
GCP's default network creates subnets in every region automatically. If you don't specify a network/subnet in Terraform, it attaches the VM to the `default` network in the provider's region.
variables.tf
variable "project_id" { type = string; description = "Your GCP Project ID" }
variable "gcp_region" { type = string; default = "us-central1" }
variable "gcp_zone" { type = string; default = "us-central1-a" }main.tf
provider "google" {
project = var.project_id
region = var.gcp_region
zone = var.gcp_zone
}
# The Compute Engine Instance
resource "google_compute_instance" "web" {
name = "nginx-web-server"
machine_type = "e2-micro"
# Network tags map this instance to firewall rules
tags = ["web-server"]
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
network_interface {
network = "default"
# An empty access_config block assigns an ephemeral public IP
access_config {}
}
metadata_startup_script = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
echo "Deployed via Terraform on GCP
" > /var/www/html/index.html
EOF
labels = {
environment = "dev"
managed_by = "terraform"
}
}
# The Firewall Rule
resource "google_compute_firewall" "allow_http" {
name = "allow-http-ingress"
network = "default"
allow {
protocol = "tcp"
ports = ["80"]
}
# Apply this rule to any instance with this tag
target_tags = ["web-server"]
# Allow traffic from anywhere
source_ranges = ["0.0.0.0/0"]
}outputs.tf
output "instance_public_ip" {
value = google_compute_instance.web.network_interface[0].access_config[0].nat_ip
description = "The public IP address of the web server"
}Workflow Commands
terraform init
terraform apply -var="project_id=my-gcp-project-123"
# Curl the public IP to verify Nginx is running
curl http://$(terraform output -raw instance_public_ip)
terraform destroy -var="project_id=my-gcp-project-123"Apply complete! Resources: 2 added, 0 changed, 0 destroyed. Outputs: instance_public_ip = "34.122.xxx.xxx"

