Infrastructure as Code (IaC) has evolved from simple scripting to a sophisticated discipline of software engineering. In the ecosystem of Google Cloud Platform (GCP), Terraform serves as the primary engine for provisioning, but the raw application of Terraform resources often leads to "code sprawl"—the repetitive, error-prone practice of copy-pasting resource blocks across multiple environments. To combat this, professional cloud architects utilize Terraform modules to turn infrastructure into reusable, versioned, and scalable building blocks.
A Terraform module is fundamentally a collection of .tf files residing in a single folder. These files encapsulate the logic required to deploy a specific set of resources, exposing a clean interface through inputs and outputs. While a resource is a single object in GCP (like a google_compute_instance), a module is a logical abstraction of those resources. When these modules are bundled with specific policies and documentation to implement an opinionated solution, they transition into what are known as Blueprints.
The Architecture of a Reusable Module
To move beyond basic configurations, teams must adopt a standardized structure. A well-engineered module does not just wrap resources; it creates a contract between the infrastructure provider and the consumer. This contract is defined by three primary components:
- Inputs (variables.tf): These define the parameters the module accepts, allowing the same code to be used for different environments (e.g., dev, staging, prod) by changing the input values.
- Resources (main.tf): This is the core logic where the actual GCP resources are defined.
- Outputs (outputs.tf): These expose specific attributes of the created resources (like a VPC ID or an IP address) so that other modules can consume them.
A professional directory layout separates the generic module logic from the environment-specific implementation. This ensures that the module remains agnostic of the environment it is deployed into.
Table 1: Recommended Directory Structure for GCP Terraform Projects
| Directory | Purpose | Key Files |
|---|---|---|
terraform/modules/vpc/ |
Reusable networking logic | main.tf, variables.tf, outputs.tf, versions.tf, README.md |
terraform/envs/dev/ |
Development environment instantiation | main.tf, providers.tf, backend.tf, terraform.tfvars |
terraform/envs/prod/ |
Production environment instantiation | main.tf, providers.tf, backend.tf, terraform.tfvars |
By keeping the modules generic and the environment folders "thin," organizations can ensure that a change to the module logic propagates across all environments consistently, provided the versioning strategy is sound.
Implementing a Production-Ready VPC Module
Networking is the foundation of any GCP organization. Rather than rewriting the same Virtual Private Cloud (VPC), subnets, and firewall rules repeatedly, a VPC module allows the architect to define the "how" once and the "what" per environment.
A robust VPC module should support the creation of a single VPC network, an arbitrary number of subnets defined via a list, and optional firewall rules. This flexibility is achieved by using Terraform's iteration capabilities.
The following code demonstrates the technical implementation of a reusable VPC module. It specifies the required provider versions to ensure stability across deployments.
```hcl
terraform {
requiredversion = ">= 1.5.0"
requiredproviders {
google = {
source = "hashicorp/google"
version = ">= 5.0.0"
}
}
}
variable "project_id" {
description = "GCP project ID where resources will be created."
type = string
}
variable "vpc_name" {
description = "Name of the VPC."
type = string
}
variable "routing_mode" {
description = "VPC routing mode: REGIONAL or GLOBAL"
type = string
default = "REGIONAL"
}
variable "subnets" {
description = "List of subnets to create"
type = list(object({
name = string
region = string
cidr = string
privategoogleaccess = bool
}))
}
variable "firewallrules" {
description = "Optional firewall rules"
type = list(object({
name = string
sourceranges = list(string)
target_tags = list(string)
protocol = string
ports = list(string)
}))
default = []
}
resource "googlecomputenetwork" "this" {
name = var.vpcname
project = var.projectid
autocreatesubnetworks = false
routingmode = var.routingmode
}
resource "googlecomputesubnetwork" "this" {
foreach = { for s in var.subnets : s.name => s }
name = each.value.name
ipcidrrange = each.value.cidr
region = each.value.region
network = googlecomputenetwork.this.id
project = var.projectid
privategoogleaccess = each.value.privategoogleaccess
}
resource "googlecomputefirewall" "this" {
foreach = { for f in var.firewallrules : f.name => f }
name = each.value.name
network = googlecomputenetwork.this.name
project = var.project_id
direction = "INGRESS"
allow {
protocol = each.value.protocol
ports = each.value.ports
}
sourceranges = each.value.sourceranges
targettags = each.value.targettags
}
output "vpcid" {
value = googlecompute_network.this.id
}
output "subnetids" {
value = { for k, s in googlecompute_subnetwork.this : k => s.id }
}
```
To consume this module in a specific environment (e.g., envs/dev/main.tf), the developer simply calls the module and passes the required parameters.
```hcl
module "vpc" {
source = "../../modules/vpc"
projectid = var.projectid
vpc_name = "dev-vpc"
subnets = [
{
name = "dev-us-central1-public"
region = "us-central1"
cidr = "10.10.0.0/24"
privategoogleaccess = true
},
{
name = "dev-us-central1-private"
region = "us-central1"
cidr = "10.10.1.0/24"
privategoogleaccess = true
}
]
firewallrules = [
{
name = "dev-allow-ssh-from-office"
sourceranges = ["203.0.113.10/32"]
target_tags = ["ssh"]
protocol = "tcp"
ports = ["22"]
}
]
}
```
Google Cloud Foundation Fabric (Fabric FAST)
For enterprises, building modules from scratch can be a time-consuming process. Google provides the Cloud Foundation Fabric (Fabric FAST), an organization-wide landing zone toolkit designed to bootstrap real-world cloud foundations. Fabric FAST focuses on two primary goals: providing a design of a GCP organization that meets typical enterprise requirements and offering a reference implementation of that design via Terraform.
The Fabric repository is intended to be used as a complete toolkit. It can be cloned as a single unit and forked into separate owned repositories for production use, or used as-is for rapid prototyping. The suite of modules is designed for rapid composition and reuse, remaining simple and readable so they can be modified if third-party code is restricted.
All modules in the Fabric ecosystem share a consistent interface:
- They stay close to the underlying provider resources to avoid unnecessary abstraction.
- They support IAM (Identity and Access Management) together with resource creation and modification.
- They offer the ability to create multiple resources where logically appropriate.
- They are free of side-effects, meaning they do not execute external commands.
Table 2: Fabric FAST Module Categories and Capabilities
| Category | Key Modules / Components | Purpose |
|---|---|---|
| Foundational | Billing account, Folder, Organization, Project, Service Accounts, Logging Bucket | Core GCP hierarchy and identity setup. |
| Process Factories | Project Factory | Automated creation and standardization of projects. |
| Networking | VPC, DNS, NAT, VLAN Attachment, Firewall Policy, VPC Peering, VPN Dynamic, Load Balancers (App/Network/Regional) | End-to-end connectivity and traffic management. |
Terraform Blueprints: Implementing Opinionated Solutions
While modules provide the building blocks, Blueprints provide the architecture. A blueprint is a package of deployable, reusable modules and policies that implement a specific, opinionated solution. Blueprints move the conversation from "how do I create a database?" to "how do I deploy a secured data warehouse?"
Google Cloud offers a variety of blueprints tailored to specific business use cases. These are packaged as Terraform modules, allowing users to deploy complex environments with minimal configuration.
Table 3: Google Cloud Terraform Blueprints and Use Cases
| Blueprint Name | Category | Description |
|---|---|---|
ai-notebook |
Data Analytics / End-to-end | Protects confidential data in Vertex AI Workbench notebooks. |
crmint |
Data Analytics / End-to-end | Deploys the CRMint marketing analytics application. |
enterprise-application |
Operations / End-to-end | Deploys an enterprise developer platform on Google Cloud. |
example-foundation |
Operations / End-to-end | Composes CFT modules to build a secure cloud foundation. |
fabric |
End-to-end | Advanced examples specifically designed for prototyping. |
secure-cicd |
DevTools / Security / Identity | Builds a secure CI/CD pipeline on Google Cloud. |
secured-data-warehouse |
Data Analytics / End-to-end | Deploys a secured BigQuery data warehouse. |
secured-data-warehouse-onprem-ingest |
Data Analytics / Security | Secured warehouse for ingesting encrypted on-prem data. |
vertex-mlops |
End-to-end | Creates the Vertex AI environment required for MLOps. |
address |
Networking | Manages Google Cloud IP addresses. |
alloy-db |
Databases | Creates AlloyDB for PostgreSQL instances. |
analytics-lakehouse |
Data Analytics | Deploys a Lakehouse Architecture Solution. |
anthos-vm |
Compute | Creates VMs on Google Distributed Cloud clusters. |
apphub |
Developer tools | Creates and manages App Hub. |
Best Practices and Common Pitfalls
When designing and implementing Terraform modules for GCP, certain architectural pitfalls can lead to configuration drift or security vulnerabilities.
Avoiding Hardcoding
One of the most common mistakes in IaC is hardcoding values. Regions, CIDR blocks, environment names, and project IDs should never be hardcoded within a module. Instead, they should be passed as variables. This ensures the module can be moved from us-central1 to europe-west1 without changing the source code.
Modular Granularity
There is a temptation to create a "platform module"—a single, massive module that manages everything from the VPC to the GKE cluster. This approach leads to fragility and long execution times. The recommended approach is to create small, focused modules:
- modules/vpc
- modules/cloud_run
- modules/cloud_sql
- modules/iam
These granular modules are then composed in the environment folder (envs/dev/main.tf) to build the full stack.
Managing IAM and Authoritative Resources
IAM resources in Terraform can be dangerous if not handled correctly. Some IAM resources are "authoritative," meaning they overwrite the entire existing set of permissions. If a manual change is made in the GCP Console, an authoritative Terraform resource will remove that access during the next terraform apply. To avoid unexpected outages, it is safer to start with *_iam_member resources, which add a specific identity to a role without affecting other existing members.
Versioning and Pinning
To prevent breaking changes from flowing into production, modules should be versioned using Git tags. Instead of calling a module from a local path, production environments should pin the module to a specific version.
Example of pinning a module version:
hcl
module "vpc" {
source = "git::https://github.com/YOUR_ORG/YOUR_REPO.git//modules/vpc?ref=v1.0.0"
}
Conclusion
The transition from writing flat Terraform files to utilizing a module-based architecture is a critical step in the maturity of any DevOps practice on Google Cloud. By abstracting recurring patterns into reusable modules, organizations can drastically reduce duplication, ensure consistent environments, and implement safer change management processes.
The combination of Google's Cloud Foundation Fabric and their suite of Blueprints provides a powerful shortcut for enterprises. Fabric FAST offers the foundational "plumbing" of a GCP organization—billing, folders, and core networking—while Blueprints provide the higher-level application architecture for AI, MLOps, and Data Analytics. By adhering to the principles of generic modules, thin environments, and strict version pinning, cloud engineers can build a scalable infrastructure that evolves with the business without collapsing under its own complexity.