terraform-google-project-factory: opinionated Google Cloud projects with Shared VPC and IAM
Creates an opinionated Google Cloud project by using Shared VPC, IAM, and Google Cloud APIs
At a glance
- What is it?
- The Google Cloud Project Factory Terraform module creates a project and applies a fixed sequence of hardening steps: Shared VPC attachment, service account replacement, billing, API enablement and a GCS bucket with Public Access Prevention and Uniform Bucket Level Access. The opinionation is the product, and it is also the part that constrains you.
- Who is it for?
- Adopt the Project Factory module when you are standing up many Google Cloud projects that should all look the same, when your organisation already runs on Shared VPC with a controlling group, and when the opinionated defaults, such as deleting the default compute service account, match your security baseline. Skip it for a single bespoke project, since the module's fixed action list is more machinery than that scenario needs.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly HCL, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the module creates, and the audience it assumes
The Google Cloud Project Factory Terraform module exists to create opinionated Google Cloud Platform projects. It creates projects and configures aspects such as Shared VPC connectivity, IAM access, Service Accounts and API enablement to follow best practices.
The audience is a platform team, not a single developer. The shape of the inputs makes that clear: an org_id, a billing_account, a controlling group name, a shared VPC host project id and a list of subnetworks. This is infrastructure for an organisation that creates projects repeatedly, by policy, rather than for someone hand-provisioning one project.
Opinionated is the key word and the main trade. The module does not merely expose Google Cloud resources and let you compose them. It performs a fixed sequence of actions that encode a security posture: it deletes the default compute service account and replaces it, it deletes the default network, it enables a default set of APIs, and it attaches a usage report bucket if you provide one. You get that baseline without writing the policy out, and you give up the ability to skip the steps you disagree with, except where the inputs provide a switch.
The module is published in the Terraform registry under the source terraform-google-modules/project-factory/google, and the project carries an Apache-2.0 licence in its LICENSE file, with a Google LLC copyright header on the Makefile.
The fixed action list the module performs on every project
The README documents the module's behaviour as an ordered list of actions, and reading it in order tells you what your apply will actually do.
A new project is created using the project_name. If a shared VPC is specified, the new project is attached to the svpc_host_project_id. The default compute service account is deleted and a new default service account is created for the project, and that new account is given access to the shared VPC so it can launch instances. The billing account is attached. The controlling group is given access to the project with the group_role. The required and specified APIs are enabled through activate_apis. The default network is deleted. If a target usage bucket is provided, usage reporting for GCE is enabled into that central project bucket. Finally, if a bucket name is specified, the GCS bucket is created and given to a defined set of accounts.
The bucket grants are Storage Admin for the controlling group, the new default compute service account and the Google APIs service account. The role set is stated exactly: compute.networkUser on the host project or the specified subnets for the new default service account, storage.admin on the GCS bucket, the specified group_role on the project for the controlling group, plus iam.serviceAccountUser on the default service account, and the same network and storage grants for the Google APIs service account.
Deleting the default network is the step people notice first. A brand new project in Google Cloud has a default network, and this module removes it, so anything expecting it has to be wired to Shared VPC from the start.
Where compute.networkUser is granted decides how much Shared VPC you can see
This is the mechanism most likely to surprise you, and it is documented as an explicit three-case rule rather than left implicit.
A service project's access to shared VPC networks is controlled through the roles/compute.networkUser role and the location where that role is assigned. If the role is assigned to the shared VPC host project, the service project gets access to all shared VPC subnetworks. If it is assigned to individual subnetworks, the service project sees only the subnetworks the role was assigned on.
The logic for determining that location works out as follows. If svpc_host_project_id and shared_vpc_subnets are both unset, the compute.networkUser role is not assigned at all. If svpc_host_project_id is set but no subnetworks are provided through shared_vpc_subnets, the role is assigned at the host project, and the service project therefore has access to all shared VPC subnetworks. If svpc_host_project_id is set and shared_vpc_subnets contains an array of subnetworks, the role is assigned to each subnetwork in that array.
So the empty list is not the same as no list. Leaving shared_vpc_subnets empty while setting a host project grants the broadest access, and that is the opposite of what an empty list intuitively suggests to a reviewer reading the configuration.
If you need G Suite integration to create groups and add service accounts into them, the documentation points to a separate gsuite_enabled module rather than this one.
Bucket hardening and API activation defaults you can tune
The inputs table shows how much of the module is configurable, and the bucket-related variables are where most of the tuning happens.
For the GCS bucket, bucket_pap enables Public Access Prevention with possible values enforced or inherited, and the default is inherited, which means the organisation-level policy applies unless you override it. bucket_ula enables Uniform Bucket Level Access and defaults to true. bucket_versioning defaults to false, bucket_location defaults to "US", bucket_labels defaults to an empty map, and bucket_force_destroy, which deletes all objects within the bucket when the bucket is deleted, defaults to false. bucket_name and bucket_project are both empty strings by default, and a note explains that the project is useful for Terraform state.
For APIs, activate_apis takes a list of API names and defaults to a list containing compute.googleapis.com, and activate_api_identities takes a list of objects with api and roles fields and defaults to an empty list. The description of that variable is where the timing behaviour is explained: APIs in the list are appended to activate_apis, and not including an API in this list follows the default behaviour for identity creation, which is usually when the first resource using the API is created. Any roles, such as a service agent role, must be listed explicitly.
Other defaults worth knowing: auto_create_network is false, which aligns with the module deleting the default network, and budget_alert_pubsub_topic names the Cloud Pub/Sub topic where budget related messages are published.
Minimal usage, version constraints and where the examples live
The README gives a simple usage example, and it is short enough to read as the shape of every call:
module "project-factory" {
source = "terraform-google-modules/project-factory/google"
version = "~> 18.4"
name = "pf-test-1"
random_project_id = true
org_id = "1234567890"
usage_bucket_name = "pf-test-1-usage-report-bucket"
usage_bucket_prefix = "pf/test/1/integration"
billing_account = "ABCDEF-ABCDEF-ABCDEF"
svpc_host_project_id = "shared_vpc_host_name"
shared_vpc_subnets = [
"projects/base-project-196723/regions/us-east1/subnetworks/default",
"projects/base-project-196723/regions/us-central1/subnetworks/default",
"projects/base-project-196723/regions/us-central1/subnetworks/subnet-1",
]
}Compatibility is stated tightly. The module is meant for Terraform 1.3 and newer and is tested using Terraform 1.10 and newer, and the README asks you to open an issue if you find incompatibilities using Terraform 1.3 or above. If you have not upgraded from the older module interface, the last release intended for Terraform 0.12.x is version 9.2.0, which is a useful floor for planning an upgrade.
The examples folder is the practical documentation. There are separate directories for app_engine, budget_project, essential_contacts, fabric_project, gke_shared_vpc, group_project, project-hierarchy, project_services, quota_project, shared_vpc, simple_project and tags_project. A team can usually find the closest pattern to what it needs there rather than starting from the minimal snippet.
Where the module is opinionated, and where you can push back
The module's opinionation is not uniform, and the parts you can change are worth separating from the parts you cannot.
Fixed behaviour includes creating the project, deleting the default compute service account and replacing it, deleting the default network, enabling the specified APIs, attaching the billing account, and granting the controlling group its access. There is no documented input that skips the deletion of the default network or the service account replacement, apart from auto_create_network governing whether the default network is created in the first place.
Tunable behaviour includes the API list and the forced service identities, the whole bucket configuration set, the group role for the controlling group, and the shared VPC subnet list that determines the scope of network access.
That split has a consequence for review. Two projects created by the same module can differ in their network reach and their API surface while sharing an identical base, and the differences are visible only in the variables you passed. If your organisation wants a single baseline, the module gives it to you. If you want per-project exceptions, you are now maintaining a fork or a wrapper module, and the README does not describe an extension mechanism for composing with other modules.
Support material is organised under docs/, with a FAQ, a Troubleshooting Guide and a Glossary linked from the top of the README, and upgrade instructions between major releases are also documented there.
Release cadence, repository layout and the licence
The module is not archived and the last push was on 2026-10-13. Releases are frequent and follow a major version line: v18.4.0 on 2026-09-30, v18.3.0 on 2026-06-25 and v18.2.0 on 2025-11-05. The current line is 18.x, and the usage example pins to the ~> 18.4 constraint, which in Terraform allows compatible patch updates within that minor line.
The upgrade cost of moving between major versions is the thing to budget for, and the documentation treats it as a first-class concern, with detailed instructions in the docs directory and a CHANGELOG.md at the root.
The repository layout shows a fairly conventional Terraform module repository, with main.tf, variables.tf, outputs.tf and versions.tf at the top level, plus a modules directory for submodules and a helpers directory. There is a test directory, a build directory, and an examples directory. The tooling is set up with a Makefile that runs work inside a cft/developer-tools container image, a .pre-commit-config.yaml, a .kitchen.yml for the test harness, a CODEOWNERS file, and a metadata.yaml plus metadata.display.yaml used by the Terraform registry.
The licence is Apache-2.0, which is permissive and suitable for internal platform use and for redistribution of modified versions with the notice preserved. The code carries Google LLC copyright headers, and the Makefile notes that it was generated from the terraform-google-module-template, with contributions expected to go upstream to that template where relevant.
Editorial conclusion
Adopt the Project Factory module when you are standing up many Google Cloud projects that should all look the same, when your organisation already runs on Shared VPC with a controlling group, and when the opinionated defaults, such as deleting the default compute service account, match your security baseline. Skip it for a single bespoke project, since the module's fixed action list is more machinery than that scenario needs. Before adopting, confirm your Terraform version is 1.3 or newer, decide whether the compute.networkUser role is granted at host project level or per subnet, and read the upgrade notes in docs before moving between major versions.
Frequently asked questions
What does the terraform-google-project-factory module create?
It creates a Google Cloud project and configures Shared VPC connectivity, IAM access, service accounts and API enablement following best practices. It also attaches a billing account, deletes the default network, and can create a GCS bucket with Storage Admin for the controlling group and the project's service accounts.
Which Terraform version does the Project Factory module support?
The module is meant for Terraform 1.3 and newer and is tested with Terraform 1.10 and newer. If you have not upgraded from the older module interface, version 9.2.0 is the last release intended for Terraform 0.12.x.
How does the module decide whether to grant access to all Shared VPC subnetworks?
The compute.networkUser role is not assigned if neither svpc_host_project_id nor shared_vpc_subnets are set. With a host project and no subnet list, the role is granted at host project level, which gives access to all shared VPC subnetworks, while providing a subnet list assigns the role to each listed subnetwork only.
How do I make the GCS bucket created by the module private?
Public Access Prevention is controlled by bucket_pap, whose possible values are enforced or inherited with a default of inherited. Uniform Bucket Level Access is on by default through bucket_ula, which defaults to true, and bucket_force_destroy defaults to false so that deleting the bucket does not remove its objects.
Can the Project Factory module create Google Groups for service accounts?
Not in this module. The documentation directs you to a separate gsuite_enabled module for G Suite integration, which handles creating groups and adding service accounts into those groups.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/terraform-google-modules-terraform-google-project-factory)Was this useful?