Home

Awesome

Google Cloud Project Factory Terraform Module

FAQ | Troubleshooting Guide | Glossary.

This module allows you to create opinionated Google Cloud Platform projects. It creates projects and configures aspects like Shared VPC connectivity, IAM access, Service Accounts, and API enablement to follow best practices.

To include G Suite integration for creating groups and adding Service Accounts into groups, use the gsuite_enabled module.

Compatibility

This module is meant for use with Terraform 0.13+ and tested using Terraform 1.3+. If you find incompatibilities using Terraform >=0.13, please open an issue. If you haven't upgraded and need a Terraform 0.12.x-compatible version of this module, the last released version intended for Terraform 0.12.x is 9.2.0.

Upgrading

See the docs for detailed instructions on upgrading between major releases of the module.

Usage

There are multiple examples included in the examples folder but simple usage is as follows:

module "project-factory" {
  source  = "terraform-google-modules/project-factory/google"
  version = "~> 17.1"

  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",
  ]
}

Features

The Project Factory module will take the following actions:

  1. Create a new GCP project using the project_name.

  2. If a shared VPC is specified, attach the new project to the svpc_host_project_id.

    It will also give the following users network access on the specified subnets:

    • The project's new default service account (see step 4)
    • The Google API service account for the project
    • The project controlling group specified in group_name
  3. Delete the default compute service account.

  4. Create a new default service account for the project.

    1. Give it access to the shared VPC (to be able to launch instances).
  5. Attach the billing account (billing_account) to the project.

  6. Give the controlling group access to the project, with the group_role.

  7. Enable the required and specified APIs (activate_apis).

  8. Delete the default network.

  9. Enable usage report for GCE into central project bucket (target_usage_bucket), if provided.

  10. If specified, create the GCS bucket bucket_name and give the following accounts Storage Admin on it:

    1. The controlling group (group_name).
    2. The new default compute service account created for the project.
    3. The Google APIs service account for the project.

The roles granted are specifically:

Shared VPC subnets and IAM permissions

A service project's access to shared VPC networks is controlled via the roles/compute.networkUser role and the location to where that role is assigned. If that role is assigned to the shared VPC host project, then the service project will have access to all shared VPC subnetworks. If that role is assigned to individual subnetworks, then the service project will have access to only the subnetworks on which that role was assigned. The logic for determining that location is as follows:

  1. If var.svpc_host_project_id and var.shared_vpc_subnets are not set then the compute.networkUser role is not assigned
  2. If var.svpc_host_project_id is set but no subnetworks are provided via var.shared_vpc_subnets then the compute.networkUser role is assigned at the host project and the service project will have access to all shared VPC subnetworks
  3. If var.svpc_host_project_id is set and var.shared_vpc_subnets contains an array of subnetworks then the compute.networkUser role is assigned to each subnetwork in the array
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

Inputs

NameDescriptionTypeDefaultRequired
activate_api_identitiesThe list of service identities (Google Managed service account for the API) to force-create for the project (e.g. in order to grant additional roles).<br> APIs in this list will automatically be appended to activate_apis.<br> Not including the API in this list will follow the default behaviour for identity creation (which is usually when the first resource using the API is created).<br> Any roles (e.g. service agent role) must be explicitly listed. See https://cloud.google.com/iam/docs/understanding-roles#service-agent-roles-roles for a list of related roles.<pre>list(object({<br> api = string<br> roles = list(string)<br> }))</pre>[]no
activate_apisThe list of apis to activate within the projectlist(string)<pre>[<br> "compute.googleapis.com"<br>]</pre>no
auto_create_networkCreate the default networkboolfalseno
billing_accountThe ID of the billing account to associate this project withstringn/ayes
bucket_force_destroyForce the deletion of all objects within the GCS bucket when deleting the bucket (optional)boolfalseno
bucket_labelsA map of key/value label pairs to assign to the bucket (optional)map(string){}no
bucket_locationThe location for a GCS bucket to create (optional)string"US"no
bucket_nameA name for a GCS bucket to create (in the bucket_project project), useful for Terraform state (optional)string""no
bucket_papEnable Public Access Prevention. Possible values are "enforced" or "inherited".string"inherited"no
bucket_projectA project to create a GCS bucket (bucket_name) in, useful for Terraform state (optional)string""no
bucket_ulaEnable Uniform Bucket Level Accessbooltrueno
bucket_versioningEnable versioning for a GCS bucket to create (optional)boolfalseno
budget_alert_pubsub_topicThe name of the Cloud Pub/Sub topic where budget related messages will be published, in the form of projects/{project_id}/topics/{topic_id}stringnullno
budget_alert_spend_basisThe type of basis used to determine if spend has passed the thresholdstring"CURRENT_SPEND"no
budget_alert_spent_percentsA list of percentages of the budget to alert on when threshold is exceededlist(number)<pre>[<br> 0.5,<br> 0.7,<br> 1<br>]</pre>no
budget_amountThe amount to use for a budget alertnumbernullno
budget_calendar_periodSpecifies the calendar period for the budget. Possible values are MONTH, QUARTER, YEAR, CALENDAR_PERIOD_UNSPECIFIED, CUSTOM. custom_period_start_date and custom_period_end_date must be set if CUSTOMstringnullno
budget_custom_period_end_dateSpecifies the end date (DD-MM-YYYY) for the calendar_period CUSTOMstringnullno
budget_custom_period_start_dateSpecifies the start date (DD-MM-YYYY) for the calendar_period CUSTOMstringnullno
budget_display_nameThe display name of the budget. If not set defaults to `Budget For <projects[0]All Projects>`stringnull
budget_labelsA single label and value pair specifying that usage from only this set of labeled resources should be included in the budget.map(string){}no
budget_monitoring_notification_channelsA list of monitoring notification channels in the form [projects/{project_id}/notificationChannels/{channel_id}]. A maximum of 5 channels are allowed.list(string)[]no
cloud_armor_tierManaged protection tier to be set. Possible values are: CA_STANDARD, CA_ENTERPRISE_PAYGOstringnullno
consumer_quotasThe quotas configuration you want to override for the project.<pre>list(object({<br> service = string,<br> metric = string,<br> dimensions = map(string),<br> limit = string,<br> value = string,<br> }))</pre>[]no
create_project_saWhether the default service account for the project shall be createdbooltrueno
default_network_tierDefault Network Service Tier for resources created in this project. If unset, the value will not be modified. See https://cloud.google.com/network-tiers/docs/using-network-service-tiers and https://cloud.google.com/network-tiers.string""no
default_service_accountProject default service account setting: can be one of delete, deprivilege, disable, or keep.string"disable"no
deletion_policyThe deletion policy for the project.string"PREVENT"no
disable_dependent_servicesWhether services that are enabled and which depend on this service should also be disabled when this service is destroyed.booltrueno
disable_services_on_destroyWhether project services will be disabled when the resources are destroyedbooltrueno
domainThe domain name (optional).string""no
enable_shared_vpc_host_projectIf this project is a shared VPC host project. If true, you must not set svpc_host_project_id variable. Default is false.boolfalseno
essential_contactsA mapping of users or groups to be assigned as Essential Contacts to the project, specifying a notification categorymap(list(string)){}no
folder_idThe ID of a folder to host this projectstring""no
grant_network_roleWhether or not to grant networkUser role on the host project/subnetsbooltrueno
grant_services_security_admin_roleWhether or not to grant Kubernetes Engine Service Agent the Security Admin role on the host project so it can manage firewall rulesboolfalseno
group_nameA group to control the project by being assigned group_role (defaults to project editor)string""no
group_roleThe role to give the controlling group (group_name) over the project (defaults to project editor)string"roles/editor"no
labelsMap of labels for projectmap(string){}no
language_tagLanguage code to be used for essential contacts notificationsstring"en-US"no
lienAdd a lien on the project to prevent accidental deletionboolfalseno
nameThe name for the projectstringn/ayes
org_idThe organization ID.stringnullno
project_idThe ID to give the project. If not provided, the name will be used.string""no
project_sa_nameDefault service account name for the project.string"project-service-account"no
random_project_idAdds a suffix of 4 random characters to the project_id.boolfalseno
random_project_id_lengthSets the length of random_project_id to the provided length, and uses a random_string for a larger collusion domain. Recommended for use with CI.numbernullno
sa_roleA role to give the default Service Account for the project (defaults to none)string""no
shared_vpc_subnetsList of subnets fully qualified subnet IDs (ie. projects/$project_id/regions/$region/subnetworks/$subnet_id)list(string)[]no
svpc_host_project_idThe ID of the host project which hosts the shared VPCstring""no
tag_binding_valuesTag values to bind the project to.list(string)[]no
usage_bucket_nameName of a GCS bucket to store GCE usage reports in (optional)string""no
usage_bucket_prefixPrefix in the GCS bucket to store GCE usage reports in (optional)string""no
vpc_service_control_attach_dry_runWhether the project will be attached to a VPC Service Control Perimeter in Dry Run Mode. vpc_service_control_attach_enabled should be false for this to be trueboolfalseno
vpc_service_control_attach_enabledWhether the project will be attached to a VPC Service Control Perimeter in ENFORCED MODE. vpc_service_control_attach_dry_run should be false for this to be trueboolfalseno
vpc_service_control_perimeter_nameThe name of a VPC Service Control Perimeter to add the created project tostringnullno
vpc_service_control_sleep_durationThe duration to sleep in seconds before adding the project to a shared VPC after the project is added to the VPC Service Control Perimeter. VPC-SC is eventually consistent.string"5s"no

Outputs

NameDescription
api_s_accountAPI service account email
api_s_account_fmtAPI service account email formatted for terraform use
budget_nameThe name of the budget if created
domainThe organization's domain
enabled_api_identitiesEnabled API identities in the project
enabled_apisEnabled APIs in the project
group_emailThe email of the G Suite group with group_name
project_bucket_self_linkProject's bucket selfLink
project_bucket_urlProject's bucket url
project_idID of the project
project_nameName of the project
project_numberNumeric identifier for the project
service_account_display_nameThe display name of the default service account
service_account_emailThe email of the default service account
service_account_idThe id of the default service account
service_account_nameThe fully-qualified name of the default service account
service_account_unique_idThe unique id of the default service account
tag_bindingsTag bindings
usage_report_export_bucketGCE usage reports bucket
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

Requirements

Software

Permissions

In order to execute this module you must have a Service Account with the following roles:

Script Helper

A helper script is included to create the Seed Service Account in the Seed Project, grant the necessary roles to the Seed Service Account, and enable the necessary API's in the Seed Project. Run it as follows:

./helpers/setup-sa.sh -o <organization id> -p <project id> [-b <billing account id>] [-f <folder id>] [-n <service account name>]

In order to execute this script, you must have an account with the following list of permissions:

Specifying credentials

The Project Factory module uses the Google Terraform provider to authenticate all GCP API calls. To configure credentials, you should configure the google and google-beta providers.

provider "google" {
  credentials = "${file(var.credentials_path)}"
}

provider "google-beta" {
  credentials = "${file(var.credentials_path)}"
}

APIs

In order to operate the Project Factory, you must activate the following APIs on the base project where the Service Account was created:

Optional APIs

Verifying setup

A preconditions checker script is included to verify that all preconditions are met before the Project Factory runs. The script will run automatically if the script dependencies (Python, "google-auth", and "google-api-python-client") are available at runtime. If the dependencies are not met, the precondition checking step will be skipped.

The precondition checker script can be directly invoked before running the project factory:

./helpers/preconditions/preconditions.py \
  --credentials_path "./credentials.json" \
  --billing_account 000000-000000-000000 \
  --org_id 000000000000 \
  --folder_id 000000000000 \
  --shared_vpc 'shared-vpc-host-ed64'

Caveats

Moving projects from org into a folder

There is currently a bug with moving a project which was originally created at the root of the organization into a folder. The bug and workaround is described here, but as a general best practice it is easier to create all projects within folders to start. Moving projects between different folders is supported.

Deleting default service accounts

Default SAs can be removed by setting default_service_account input variable to delete, but there can be certain scenarios where the default SAs are required. Hence some considerations to be aware of:

  1. Using App Engine SA.
  2. Cloud Scheduler dependency on AppEngine(default SA). Default SA is required to be able to setup Cloud scheduler, please refer to the document for more upto date information.

With a combination of project-factory's default behavior, disable, and setting constraints/iam.automaticIamGrantsForDefaultServiceAccounts org constraint will address removing the default editor IAM role on the SAs and limits the SA usage. However, when the default_service_account is set to delete please be aware of the default SA dependency for AppEngine/CloudScheduler services. Accounts deleted within 30days can be restored.

G Suite

The core Project Factory solely deals with GCP APIs and does not integrate G Suite functionality. If you would like certain group-management functionality which was previously included in the Project Factory, see the G Suite module.

Install

Terraform

Be sure you have the correct Terraform version (0.13.0+), you can choose the binary here: