惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Blog — PlanetScale
Blog — PlanetScale
Jina AI
Jina AI
C
Check Point Blog
V
V2EX
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
A
About on SuperTechFans
D
DataBreaches.Net
腾讯CDC
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
MongoDB | Blog
MongoDB | Blog
J
Java Code Geeks
博客园_首页
T
Tailwind CSS Blog
M
MIT News - Artificial intelligence
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Terraform Outputs on GCP: Querying Useful Data from a VPC...
Abraham Naib · 2026-05-06 · via DEV Community

Hi, we are back again. Previously, I created a simple Google Cloud VPC and then improved the configuration by introducing variables.

This time, I want to continue with another Terraform concept: outputs. But, we will not be using the previous code, because adding outputs for one vpc is too simple. So, I made the lab slightly more practical.

In this lab, I will create:

  • a custom VPC network
  • a subnet with a defined CIDR range
  • a small Compute Engine VM
  • Terraform outputs to query useful information after provisioning

The goal is to understand how Terraform outputs can expose important infrastructure data after resources are created.

Reference: Hasicorp website about gcp tutorial

Why Outputs Matter

When Terraform creates infrastructure, it stores a lot of resource data in the state file. However, as users, we usually do not need to read everything.

Most of the time, we only care about specific values, such as:

  • the VM internal IP address
  • the VPC name
  • the subnet CIDR range
  • the VM self-link
  • a structured summary of the created resources

This is where outputs are useful.

Outputs allow us to expose selected resource information after terraform apply.

They can also be queried later using:

terraform output

Enter fullscreen mode Exit fullscreen mode

What This Lab Builds

This lab creates three Google Cloud resources:

Resource Purpose
google_compute_network.vpc_network Custom VPC network
google_compute_subnetwork.subnet Regional subnet inside the VPC
google_compute_instance.vm_instance Small Compute Engine VM inside the subnet

The VM will be created without an external public IP address.

This happens because the network_interface block does not include an access_config block.

For this lab, that is fine because the goal is not to SSH into the VM or expose an application. The goal is to learn how Terraform outputs work.

Create a new lab folder

Because this is a new lab, I created a new folder.

mkdir learn-terraform-gcp-lab-output
cd learn-terraform-gcp-lab-output

Enter fullscreen mode Exit fullscreen mode


Then create three files:

touch main.tf variables.tf outputs.tf

Enter fullscreen mode Exit fullscreen mode

The folder should look like this:

learn-terraform-gcp-lab-output/
├── main.tf
├── variables.tf
└── outputs.tf

Enter fullscreen mode Exit fullscreen mode

Create variables.tf

First, open variables.tf.

nano variables.tf

Enter fullscreen mode Exit fullscreen mode

Then add the following variable declarations:

variable "project" {
  description = "The Google Cloud project ID where resources will be created."
  type        = string
}

variable "region" {
  description = "The Google Cloud region where regional resources will be created."
  type        = string
  default     = "us-central1"
}

variable "zone" {
  description = "The Google Cloud zone where the VM instance will be created."
  type        = string
  default     = "us-central1-c"
}

variable "network_name" {
  description = "The name of the VPC network."
  type        = string
  default     = "terraform-output-network"
}

variable "subnet_name" {
  description = "The name of the subnet."
  type        = string
  default     = "terraform-output-subnet"
}

variable "subnet_cidr_range" {
  description = "The CIDR range for the subnet."
  type        = string
  default     = "10.10.0.0/24"
}

variable "instance_name" {
  description = "The name of the Compute Engine VM instance."
  type        = string
  default     = "terraform-output-vm"
}

variable "machine_type" {
  description = "The machine type for the Compute Engine VM instance."
  type        = string
  default     = "e2-micro"
}

Enter fullscreen mode Exit fullscreen mode

This file only declares the input variables Terraform can accept.

The important distinction is:

variables.tf declares input variables.
main.tf uses those variables.
outputs.tf exposes selected resource data after apply.

Enter fullscreen mode Exit fullscreen mode

Create main.tf

Next, open main.tf.

nano main.tf

Enter fullscreen mode Exit fullscreen mode

Then add the following Terraform configuration:

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "6.8.0"
    }
  }
}

provider "google" {
  project = var.project
  region  = var.region
  zone    = var.zone
}

resource "google_compute_network" "vpc_network" {
  name                    = var.network_name
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "subnet" {
  name          = var.subnet_name
  region        = var.region
  network       = google_compute_network.vpc_network.id
  ip_cidr_range = var.subnet_cidr_range
}

resource "google_compute_instance" "vm_instance" {
  name         = var.instance_name
  machine_type = var.machine_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-12"
    }
  }

  network_interface {
    subnetwork = google_compute_subnetwork.subnet.id
  }
}

Enter fullscreen mode Exit fullscreen mode

Understanding main.tf

Google Provider

The provider block tells Terraform which Google Cloud project, region, and zone to use.

provider "google" {
  project = var.project
  region  = var.region
  zone    = var.zone
}

Enter fullscreen mode Exit fullscreen mode

The values come from the variables declared in variables.tf.

Custom VPC Network

resource "google_compute_network" "vpc_network" {
  name                    = var.network_name
  auto_create_subnetworks = false
}

Enter fullscreen mode Exit fullscreen mode

This creates a custom VPC network.

The important part is:

auto_create_subnetworks = false

Enter fullscreen mode Exit fullscreen mode

This means Google Cloud will not automatically create subnets for us.

Instead, we will define our own subnet manually.


Subnet

resource "google_compute_subnetwork" "subnet" {
  name          = var.subnet_name
  region        = var.region
  network       = google_compute_network.vpc_network.id
  ip_cidr_range = var.subnet_cidr_range
}

Enter fullscreen mode Exit fullscreen mode

This creates a subnet inside the VPC.

The subnet uses this CIDR range:

10.10.0.0/24

Enter fullscreen mode Exit fullscreen mode

This value comes from:

var.subnet_cidr_range

Enter fullscreen mode Exit fullscreen mode

Compute Engine VM

resource "google_compute_instance" "vm_instance" {
  name         = var.instance_name
  machine_type = var.machine_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-12"
    }
  }

  network_interface {
    subnetwork = google_compute_subnetwork.subnet.id
  }
}

Enter fullscreen mode Exit fullscreen mode

This creates a small Compute Engine VM using:

e2-micro

Enter fullscreen mode Exit fullscreen mode

The VM is attached to the subnet using:

subnetwork = google_compute_subnetwork.subnet.id

Enter fullscreen mode Exit fullscreen mode

Because there is no access_config block, this VM does not get an external public IP address.

It only receives an internal IP address from the subnet.

Create outputs.tf

Now we create the output definitions.

Open outputs.tf.

nano outputs.tf

Enter fullscreen mode Exit fullscreen mode

Then add:

output "vpc_network_name" {
  description = "The name of the VPC network created by Terraform."
  value       = google_compute_network.vpc_network.name
}

output "vpc_network_id" {
  description = "The ID of the VPC network created by Terraform."
  value       = google_compute_network.vpc_network.id
}

output "subnet_name" {
  description = "The name of the subnet created by Terraform."
  value       = google_compute_subnetwork.subnet.name
}

output "subnet_cidr_range" {
  description = "The CIDR range of the subnet created by Terraform."
  value       = google_compute_subnetwork.subnet.ip_cidr_range
}

output "vm_instance_name" {
  description = "The name of the VM instance created by Terraform."
  value       = google_compute_instance.vm_instance.name
}

output "vm_instance_zone" {
  description = "The zone where the VM instance was created."
  value       = google_compute_instance.vm_instance.zone
}

output "vm_internal_ip" {
  description = "The internal IP address of the VM instance."
  value       = google_compute_instance.vm_instance.network_interface[0].network_ip
}

output "vm_self_link" {
  description = "The self-link of the VM instance."
  value       = google_compute_instance.vm_instance.self_link
}

output "lab_summary" {
  description = "A structured summary of the resources created in this lab."

  value = {
    project        = var.project
    region         = var.region
    zone           = var.zone
    vpc_name       = google_compute_network.vpc_network.name
    subnet_name    = google_compute_subnetwork.subnet.name
    subnet_cidr    = google_compute_subnetwork.subnet.ip_cidr_range
    vm_name        = google_compute_instance.vm_instance.name
    vm_internal_ip = google_compute_instance.vm_instance.network_interface[0].network_ip
  }
}

Enter fullscreen mode Exit fullscreen mode

This is the main focus of the lab.

Instead of only creating resources, we are selecting which resource attributes should be easy to view after provisioning.

Format the Terraform Files

Run:

terraform fmt

Enter fullscreen mode Exit fullscreen mode

This formats the Terraform configuration files.

Initialize Terraform

Run:

terraform init

Enter fullscreen mode Exit fullscreen mode

This initializes the working directory and downloads the Google provider.

Validate the Configuration

Run:

terraform validate

Enter fullscreen mode Exit fullscreen mode

Expected output:

Success! The configuration is valid.

Enter fullscreen mode Exit fullscreen mode

Apply the Configuration

Run:

terraform apply

Enter fullscreen mode Exit fullscreen mode

Because the project variable does not have a default value, Terraform will ask for it.

var.project
  The Google Cloud project ID where resources will be created.

  Enter a value:

Enter fullscreen mode Exit fullscreen mode

Enter your Google Cloud project ID.

Terraform will show the execution plan.

In my case, Terraform planned to create three resources:

Plan: 3 to add, 0 to change, 0 to destroy.

Enter fullscreen mode Exit fullscreen mode

The resources were:

google_compute_network.vpc_network
google_compute_subnetwork.subnet
google_compute_instance.vm_instance

Enter fullscreen mode Exit fullscreen mode

Terraform also showed that some output values were only known after apply:

vm_internal_ip = (known after apply)
vm_self_link   = (known after apply)

Enter fullscreen mode Exit fullscreen mode

That makes sense because the VM internal IP and self-link do not exist until Google Cloud creates the VM.

After reviewing the plan, type:

yes

Enter fullscreen mode Exit fullscreen mode

Apply Result

After Terraform finished provisioning, my output looked like this:

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

lab_summary = {
  "project" = "terraform-gcp-learning-lab"
  "region" = "us-central1"
  "subnet_cidr" = "10.10.0.0/24"
  "subnet_name" = "terraform-output-subnet"
  "vm_internal_ip" = "10.10.0.2"
  "vm_name" = "terraform-output-vm"
  "vpc_name" = "terraform-output-network"
  "zone" = "us-central1-c"
}
subnet_cidr_range = "10.10.0.0/24"
subnet_name = "terraform-output-subnet"
vm_instance_name = "terraform-output-vm"
vm_instance_zone = "us-central1-c"
vm_internal_ip = "10.10.0.2"
vpc_network_id = "projects/terraform-gcp-learning-lab/global/networks/terraform-output-network"
vpc_network_name = "terraform-output-network"

Enter fullscreen mode Exit fullscreen mode

At this point, Terraform had created:

  • one VPC network
  • one subnet
  • one VM instance

It also exposed selected infrastructure data through outputs.

Query the Outputs

After apply, we can query all outputs using:

terraform output

Enter fullscreen mode Exit fullscreen mode

Example output:

lab_summary = {
  "project" = "terraform-gcp-learning-lab"
  "region" = "us-central1"
  "subnet_cidr" = "10.10.0.0/24"
  "subnet_name" = "terraform-output-subnet"
  "vm_internal_ip" = "10.10.0.2"
  "vm_name" = "terraform-output-vm"
  "vpc_name" = "terraform-output-network"
  "zone" = "us-central1-c"
}
subnet_cidr_range = "10.10.0.0/24"
subnet_name = "terraform-output-subnet"
vm_instance_name = "terraform-output-vm"
vm_instance_zone = "us-central1-c"
vm_internal_ip = "10.10.0.2"
vpc_network_id = "projects/terraform-gcp-learning-lab/global/networks/terraform-output-network"
vpc_network_name = "terraform-output-network"

Enter fullscreen mode Exit fullscreen mode

We can also query one specific output.

For example:

terraform output vm_internal_ip

Enter fullscreen mode Exit fullscreen mode

The result:

"10.10.0.2"

Enter fullscreen mode Exit fullscreen mode

This is useful because I do not need to inspect the full Terraform state just to find the VM internal IP address.

Query a Structured Output

I also created a structured output called lab_summary.

Run:

terraform output lab_summary

Enter fullscreen mode Exit fullscreen mode

Example result:

{
  "project" = "terraform-gcp-learning-lab"
  "region" = "us-central1"
  "subnet_cidr" = "10.10.0.0/24"
  "subnet_name" = "terraform-output-subnet"
  "vm_internal_ip" = "10.10.0.2"
  "vm_name" = "terraform-output-vm"
  "vpc_name" = "terraform-output-network"
  "zone" = "us-central1-c"
}

Enter fullscreen mode Exit fullscreen mode

This is more readable than scanning the full state file.

Output as JSON

Terraform can also return outputs in JSON format.

Run:

terraform output -json

Enter fullscreen mode Exit fullscreen mode

This returns structured JSON data.

Example:

{
  "vm_internal_ip": {
    "sensitive": false,
    "type": "string",
    "value": "10.10.0.2"
  },
  "vpc_network_name": {
    "sensitive": false,
    "type": "string",
    "value": "terraform-output-network"
  }
}

Enter fullscreen mode Exit fullscreen mode

We can also save the output into a JSON file:

terraform output -json > terraform-outputs.json

Enter fullscreen mode Exit fullscreen mode

This can be useful when another script, pipeline, or tool needs to consume Terraform output data.

Inspect Terraform State

Outputs are useful, but they do not replace state.

Terraform still stores resource information in the state file.

To list resources tracked by Terraform, run:

terraform state list

Enter fullscreen mode Exit fullscreen mode

My result:

google_compute_instance.vm_instance
google_compute_network.vpc_network
google_compute_subnetwork.subnet

Enter fullscreen mode Exit fullscreen mode

Then I tried to inspect the Compute Engine instance with:

terraform state show google_compute_instance

Enter fullscreen mode Exit fullscreen mode

Terraform returned an error:

Error parsing instance address: google_compute_instance

This command requires that the address references one specific instance.
To view the available instances, use "terraform state list". Please modify
the address to reference a specific instance.

Enter fullscreen mode Exit fullscreen mode

This happened because google_compute_instance is only the resource type.

Terraform needs the full resource address.

The correct command is:

terraform state show google_compute_instance.vm_instance

Enter fullscreen mode Exit fullscreen mode

This command showed detailed information about the VM, including:

  • machine type
  • zone
  • internal IP
  • boot disk
  • subnet
  • scheduling configuration
  • shielded instance configuration

This was a useful mistake because it clarified the difference between:

resource type

Enter fullscreen mode Exit fullscreen mode

and:

resource address

Enter fullscreen mode Exit fullscreen mode

Destroy the Infrastructure

Because this lab creates a VM, I destroyed the resources after testing.

Run:

terraform destroy

Enter fullscreen mode Exit fullscreen mode

Terraform will ask for the project value again if it was not provided through another method.

Then it will show a destroy plan.

In my case, the destroy plan showed:

Plan: 0 to add, 0 to change, 3 to destroy.

Enter fullscreen mode Exit fullscreen mode

It also showed that outputs would be removed:

Changes to Outputs:
  - lab_summary       = {...} -> null
  - vm_internal_ip    = "10.10.0.2" -> null
  - vpc_network_name  = "terraform-output-network" -> null

Enter fullscreen mode Exit fullscreen mode

After reviewing the destroy plan, type:

yes

Enter fullscreen mode Exit fullscreen mode

Important: wait until Terraform shows:

Destroy complete! Resources: 3 destroyed.

Enter fullscreen mode Exit fullscreen mode

After that, you can also verify from the Google Cloud Console that the VM, subnet, and VPC have been removed.

What I Learned

From this lab, I learned that Terraform outputs are not just decoration at the end of terraform apply.

Outputs are a clean way to expose the specific infrastructure values that matter.

Instead of manually searching through the state file or Google Cloud Console, I can query useful values directly:

terraform output
terraform output vm_internal_ip
terraform output lab_summary
terraform output -json

Enter fullscreen mode Exit fullscreen mode

The most important idea for me is:

Terraform creates infrastructure, state tracks infrastructure, and outputs expose selected infrastructure data.

Enter fullscreen mode Exit fullscreen mode

I also learned that outputs can be simple values or structured objects.

For example, vm_internal_ip is a simple string output, while lab_summary is a structured output that groups multiple values together.

This makes outputs useful not only for humans, but also for automation.

Next Step

This lab still asks me to manually enter the project variable during terraform apply and terraform destroy.

In the next lab, I want to improve that by using:

  • terraform.tfvars
  • terraform.tfvars.example
  • safer variable value management
  • avoiding repeated manual input

That should make the Terraform workflow cleaner and more practical.