Best practices for reusable modules Stay organized with collections Save and categorize content based on your preferences.
This document provides guidelines and recommendations to consider when usingreusable Terraform modules.
This guide is not an introduction to Terraform. For an introduction to usingTerraform with Google Cloud, seeGet started with Terraform.
Activate required APIs in modules
Terraform modules can activate any required services by using thegoogle_project_service resource or theproject_services module.Including API activation makes demonstrations easier.
- If API activation is included in a module, then the API activationmustbe disableable by exposing an
enable_apisvariable that defaults totrue. If API activation is included in a module, then the API activationmustset
disable_services_on_destroytofalse, because this attribute cancause issues when working with multiple instances of the module.For example:
module"project-services"{source="terraform-google-modules/project-factory/google//modules/project_services"version="~> 12.0"project_id=var.project_idenable_apis=var.enable_apisactivate_apis=["compute.googleapis.com","pubsub.googleapis.com",]disable_services_on_destroy=false}
Include an owners file
For all shared modules, include anOWNERSfile (orCODEOWNERSon GitHub), documenting who is responsible for the module. Before any pullrequest is merged, an owner should approve it.
Release tagged versions
Sometimes modules require breaking changes and you need to communicate theeffects to users so that they can pin their configurations to a specificversion.
Make sure that shared modules followSemVer v2.0.0when new versions are tagged or released.
When referencing a module, use aversion constraintto pin to themajor version. For example:
module"gke"{source="terraform-google-modules/kubernetes-engine/google"version="~> 20.0"}Don't configure providers or backends
Shared modulesmust not configure providers or backends.Instead, configure providers and backends in root modules.
For shared modules, define the minimum required provider versions in arequired_providersblock, as follows:
terraform{required_providers{google={source="hashicorp/google"version=">= 4.0.0"}}}Unless proven otherwise, assume that new provider versions will work.
Expose labels as a variable
Allow flexibility in the labeling of resources through the module's interface.Consider providing alabels variable with a default value of an empty map, asfollows:
variable"labels"{description="A map of labels to apply to contained resources."default={}type="map"}Expose outputs for all resources
Variables and outputs let you infer dependencies between modules and resources.Without any outputs, users cannot properly order your module in relation totheir Terraform configurations.
For every resource defined in a shared module, include at least one output thatreferences the resource.
Use inline submodules for complex logic
- Inline modules let you organize complex Terraform modules intosmaller units and de-duplicate common resources.
- Place inline modules in
modules/$modulename. - Treat inline modules as private, not to be used by outside modules,unless the shared module's documentation specifically states otherwise.
- Terraform doesn't track refactored resources. If you start with severalresources in the top-level module and then push them into submodules,Terraform tries to recreate all refactored resources. To mitigate thisbehavior, use
movedblocks when refactoring. - Outputs defined by internal modules aren't automatically exposed. To shareoutputs from internal modules, re-export them.
What's next
- Learn aboutgeneral style and structure best practices for Terraform on Google Cloud.
- Learn aboutbest practices when using Terraform root modules.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-12-15 UTC.