How the two-repository GCP Terragrunt boilerplate keeps live infrastructure separate from reusable modules
- Live configuration starts in the Terragrunt GCP projects repository. This is the repo an operator should open during a review, rollout, or incident to answer practical questions: what is deployed in dev, staging, and production; which Google Cloud projects are in scope; which inputs vary by environment; where remote state is stored; and how Terragrunt dependencies shape apply order. Keep this layer about composition, ownership, and environment wiring. If resource implementation details start piling up here, the live repo quietly becomes an unversioned module library, and every production change becomes harder to review safely.
- Reusable infrastructure belongs in the Terraform GCP modules repository. Treat it as the shared library for GCP building blocks: VPC and subnet patterns, IAM bindings, service accounts, project services, logging defaults, and other primitives that should behave consistently across environments. The point is not abstraction for its own sake; it is controlled reuse. A module can be reviewed, tested, tagged, and then consumed deliberately by live configurations. That is much easier to operate than copying resource blocks into every project directory and discovering six months later that production, staging, and dev all evolved in slightly different ways.
โ
Terraform failure modes this GCP Terragrunt boilerplate is designed to prevent
This boilerplate exists because the most expensive Terraform problems in platform teams rarely begin with one obviously wrong resource block. They begin with the operating model: unclear ownership, inconsistent directory layouts, duplicated provider configuration, postponed backend decisions, and a promotion path from development to staging to production that depends on tribal knowledge. By the time the problem shows up, it is usually not a neat plan-time error. It is a tangled incident involving state access, IAM permissions, missing APIs, dependency ordering, and pressure to ship a fix without making the blast radius worse.
When teams adopt Terraform without a structure they can still maintain after the first successful project, the same patterns show up in code reviews, handoffs, and production changes:
- Provider, backend, and remote-state settings are copied across stacks until small differences become invisible during review.
- Environment differences are patched by hand instead of expressed as explicit, reviewable inputs.
- State migrations become risky because ownership boundaries and stack responsibilities were never agreed up front.
- Reusable modules have no clear owner, versioning approach, compatibility expectations, or release path.
- Pull requests become too broad to review because module logic, environment wiring, IAM changes, and rollout sequencing all move together.
- Dependency order is discovered during failed applies instead of being modeled in Terragrunt configuration.
- Writing all of the Terraform code in one main.tf file
- Copy-pasting resources manually
- Copy-pasting configuration throughout the codebase
- No state separation and environment awareness
This is why they regretted the above:
- One
terraform applycould ruin an entire environment - Resources modifications required changes in multiple locations
- Configuration modifications required changes in multiple locations
- Accidentally deploying the wrong resources to the wrong environment
And so, I built this boilerplate for our clients (and you) to minimize regrets.
The focus of this boilerplate is managing GCP resources.
โ
Is this boilerplate for you?
If you are a CTO, a DevOps lead, or a platform engineer starting a new GCP project, the early pressure is familiar: ship infrastructure quickly, but do not create a repository layout that becomes painful after the second environment. A practical Terraform and Terragrunt boilerplate helps by giving teams a clean starting point for projects, regions, modules, and shared configuration. I could not find a GCP deployment example with a structure I would reuse in production, so I built one, opened it up, and will walk through the decisions behind it here.
โ
What should you expect from this guide?
By the conclusion of this guide, you'll have a thorough understanding of how to establish Terraform repositories using a best-practice folder structure for provisioning GCP resources. You'll also be equipped to execute a straightforward demo, witnessing an end-to-end workflow in action.
โ
What shouldn't you expect from this guide?
An exhaustive library of modules for every resource in GCP. We kept the boilerplate minimal, so that you can utilize it for your needs.
You can fairly easily utilize existing modules you created or found.
โ
Getting Started
To begin, clone the essential repositories:
โ
Primary Repository
Clone the terragrunt-gcp-projects repository to get started.
git clone git@github.com:MeteorOps/terragrunt-gcp-projects.git
โ
Modules Repository (Optional)
For the modules used, clone the terraform-gcp-modules repository.
git clone git@github.com:MeteorOps/terraform-gcp-modules.git
โ
Repository Structure Explained
The code organization follows a logical hierarchy to facilitate multiple projects, regions or environments.This structure gives you a number of benefits:
- Hierarchical configuration: The configuration at each level cascades through the folders under it
- State separation: The terraform state is saved per folder in a different path in a bucket, limiting the impact radius of changes
- Dynamic-level of deployment: The deeper into the folder you go, the more specific resources you affect with one deployment
โ
project
โ _global
โ region
โ _global
โ environment
โ resource
โ
Creating and using root (project) level variables
Once you manage more than one GCP project, environment, or region, common module inputs become one of the first sources of drift. Project IDs, default regions, labels, naming prefixes, and other shared values should not be copied into every stack by hand. Keep those defaults in the root terragrunt.hcl and let child terragrunt.hcl files inherit them, overriding only the values that genuinely differ for that environment or region.
โ
Deployment Using Terragrunt
Prerequisites
- Install Terraform version
0.12.6or newer and Terragrunt versionv0.25.1or newer. - Fill in your GCP Project ID in
my-project/project.hcl. - Make sure gcloud CLI is installed and you are authenticated, otherwise run
gcloud auth login.
Module Deployment
To deploy a single module:
cdinto the module's folder (e.g.cd my-project/us-central1/rnd-1/vpc).- Run
terragrunt planto see the changes you're about to apply. - If the plan looks good, run
terragrunt apply.
Environment Deployment
To deploy all modules within an environment:
cdinto the environment folder (e.g.cd my-project/us-central1/rnd-1).- Run
terragrunt run-all planto see all the changes you're about to apply. - If the plan looks good, run
terragrunt run-all apply.
Testing Deployed Infrastructure
After deployment, each module should expose the values an operator or downstream stack actually needs. In this example, the useful output is the application IP address:
Outputs:
ip = "35.240.219.84"
After the deployment has settled, use the exported ip value as a quick smoke test from your browser or with curl:
curl 35.240.219.84
# Output: Let MeteorOps know if the boilerplate needs any improvement!
Clean-Up Process
To remove all deployed modules within an environment:
cdinto the environment folder (e.g.cd my-project/us-central1/rnd-1).- Run
terragrunt run-all plan -destroyto see all the destroy changes you're about to apply. - If the plan looks good, run
terragrunt run-all destroy.
โ
Conclusion
This guide walks you through leveraging best practices for setting up and managing Terraform repositories for GCP with Terragrunt. These methodologies are designed to be straightforward, efficient, and easily adaptable to future projects or company needs.




