Overview and What You Will Learn
In this lab, you will create a firewall rule targeting a specific network tag, then create two VMs - one carrying that tag, one without it - and confirm the rule applies only to the tagged VM, directly experiencing the tag-based targeting model.
Why This Matters in Production
An engineer coming from AWS or Azure creates a firewall rule expecting it to apply to every VM in a specific subnet, the way a security group or NSG might, then discovers a newly created VM without the matching tag is completely unaffected by that rule - silently, with no error to flag the mismatch, since the rule and the VM simply never had a targeting relationship in the first place.
Core Principles
Unlike clouds where security rules attach to a subnet or a VM's network interface directly, GCP firewall rules are defined at the VPC level and applied using network tags or Service Accounts on instances, rather than being tied to a specific subnet boundary.
+------------------------------------------+| Firewall Rule || target-tags: web-server || allow: tcp:443 from 0.0.0.0/0 |+------------------------------------------+ | | applies only to VMs | carrying this exact tag v+------------------+ +------------------+| VM tagged | | VM with no tag || "web-server" | | (or a different one)|| -> rule applies | | -> rule does NOT |+------------------+ | apply at all | +------------------+Detailed Step-by-Step Practical Lab
- Create a project, a VPC, and a subnet:
gcloud projects create gcp-firewall-lab-2026 --name="Firewall Tags Lab"gcloud config set project gcp-firewall-lab-2026gcloud services enable compute.googleapis.com gcloud compute networks create vpc-lab --subnet-mode=customgcloud compute networks subnets create subnet-lab \ --network=vpc-lab \ --region=asia-south1 \ --range=10.0.1.0/24- Create a firewall rule allowing HTTPS traffic, targeting only VMs tagged
web-server:
gcloud compute firewall-rules create allow-https-web \ --network=vpc-lab \ --direction=INGRESS \ --action=ALLOW \ --rules=tcp:443 \ --source-ranges=0.0.0.0/0 \ --target-tags=web-server- Create a VM carrying the matching tag:
gcloud compute instances create vm-tagged \ --zone=asia-south1-a \ --machine-type=e2-small \ --image-family=debian-12 \ --image-project=debian-cloud \ --network=vpc-lab \ --subnet=subnet-lab \ --tags=web-server- Create a second VM with no tag at all:
gcloud compute instances create vm-untagged \ --zone=asia-south1-a \ --machine-type=e2-small \ --image-family=debian-12 \ --image-project=debian-cloud \ --network=vpc-lab \ --subnet=subnet-lab- Confirm which firewall rules actually apply to each VM:
gcloud compute instances describe vm-tagged \ --zone=asia-south1-a --format="value(tags.items)" gcloud compute instances describe vm-untagged \ --zone=asia-south1-a --format="value(tags.items)"Attempt an HTTPS connection to each VM's internal IP from a third VM in the same subnet, and confirm the tagged VM accepts the connection while the untagged VM does not, since the firewall rule's target-tags condition never matches it.
As an alternative to tags, create a rule targeting a Service Account instead, which is often more reliable at scale since it's a real IAM identity rather than a manually-typed string:
gcloud iam service-accounts create web-server-sa gcloud compute firewall-rules create allow-https-by-sa \ --network=vpc-lab \ --direction=INGRESS \ --action=ALLOW \ --rules=tcp:443 \ --source-ranges=0.0.0.0/0 \ --target-service-accounts=web-server-sa@gcp-firewall-lab-2026.iam.gserviceaccount.com- Clean up:
gcloud compute instances delete vm-tagged vm-untagged --zone=asia-south1-a --quietgcloud compute firewall-rules delete allow-https-web allow-https-by-sa --quietgcloud compute networks subnets delete subnet-lab --region=asia-south1 --quietgcloud compute networks delete vpc-lab --quietgcloud projects delete gcp-firewall-lab-2026 --quietProduction Best Practices & Common Pitfalls
Common MistakeCreating a firewall rule and assuming it automatically applies to every VM in the target VPC or subnet. If the rule specifies
--target-tagsor--target-service-accounts, it only affects VMs carrying that exact tag or identity - a newly created VM without it gets none of that rule's protection or access, silently.
TipPrefer Service Account-based targeting over network tags for anything security-critical at scale. A Service Account is a real IAM identity that must be deliberately attached to a VM at creation time and shows up clearly in IAM audits, while a network tag is a freeform string that's easy to forget, misspell, or apply inconsistently across a growing fleet.
- Firewall rules also have a priority (lower numbers evaluated first) - the same concept as NSG or security group rule priority in Azure and AWS, letting a more specific deny rule override a broader allow rule evaluated later.
- Egress rules work the same tag-based way as ingress rules. A VM's outbound traffic is governed by rules matching its own tags or Service Account as the source, not the destination it's trying to reach.
Quick Reference & Troubleshooting Commands
| Command | Description |
|---|---|
gcloud compute firewall-rules create --target-tags= |
Create a rule scoped to tagged VMs |
gcloud compute firewall-rules create --target-service-accounts= |
Create a rule scoped to a Service Account identity |
gcloud compute instances create --tags= |
Assign network tags to a VM at creation |
gcloud compute instances describe --format="value(tags.items)" |
Check a VM's currently assigned tags |