Skip to content

This Module describes a Google Project with Terraform

License

Notifications You must be signed in to change notification settings

qbeyond/terraform-google-project

Repository files navigation

Project Module

This module implements the creation and management of one GCP project including IAM, organization policies, Shared VPC host or service attachment, service API activation, and tag attachment. It also offers a convenient way to refer to managed service identities (aka robot service accounts) for APIs.

Examples

Basic

This Module creates a GCP Project

provider "google" {
}

resource "random_string" "project_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

data "google_organization" "default" {
  domain = var.organization_domain
}

module "project" {
  source = "../.."
  name = random_string.project_name.result
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
}
variable "organization_domain" {
  type = string
}

variable "billing_account_id" {
  type = string
}

Cloud KMS encryption keys

The module offers a simple, centralized way to assign roles/cloudkms.cryptoKeyEncrypterDecrypter to service identities.

provider "google" {
}

resource "random_string" "project_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

resource "random_string" "keyring" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

resource "random_string" "key" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

data "google_organization" "default" {
  domain = var.organization_domain
}

resource "google_kms_key_ring" "default" {
  name     = random_string.keyring.result
  location = "europe"
  project = var.kms_project_id
}

resource "google_kms_crypto_key" "default" {
  name            = random_string.key.result
  key_ring        = google_kms_key_ring.default.id
}

module "project" {
  source = "../.."
  name = random_string.project_name.result
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
  services = [
    "compute.googleapis.com"
  ]
  service_encryption_key_ids = {
    compute = [
      "projects/${var.kms_project_id}/locations/europe/keyRings/${random_string.keyring.result}/cryptoKeys/${random_string.key.result}"
    ]
  }
}
variable "organization_domain" {
  type = string
}

variable "billing_account_id" {
  type = string
}

variable "kms_project_id" {
  type = string
}

IAM

IAM is managed via several variables that implement different levels of control:

  • group_iam and iam configure authoritative bindings that manage individual roles exclusively, mapping to the google_project_iam_binding resource
  • iam_additive and iam_additive_members configure additive bindings that only manage individual role/member pairs, mapping to the google_project_iam_member resource

Be mindful about service identity roles when using authoritative IAM, as you might inadvertently remove a role from a service identity or default service account. For example, using roles/editor with iam or group_iam will remove the default permissions for the Cloud Services identity. A simple workaround for these scenarios is described below.

provider "google" {
}

resource "random_string" "project_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

data "google_organization" "default" {
  domain = var.organization_domain
}

resource "google_cloud_identity_group" "basic" {
  parent = "customers/${data.google_organization.default.directory_customer_id}"

  group_key {
      id = "projectmailtest@${data.google_organization.default.domain}"
  }

  labels = {
    "cloudidentity.googleapis.com/groups.discussion_forum" = ""
  }
}

module "project" {
  source = "../.."
  name = random_string.project_name.result
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
  services = [
    "cloudidentity.googleapis.com"
  ]
  group_iam = {
    "${google_cloud_identity_group.basic.group_key.0.id}" = [
      "roles/cloudasset.owner",
      "roles/cloudsupport.techSupportEditor",
      "roles/iam.securityReviewer",
      "roles/logging.admin"
    ]
  }
}
variable "organization_domain" {
  type = string
}

variable "billing_account_id" {
  type = string
}

Organization policies

To manage organization policies, the orgpolicy.googleapis.com service should be enabled in the quota project. To use yaml config, it is required to create a yaml file with your configuration and add the org_policies_data_path variable.

configs/boolean.yaml

compute.disableGuestAttributesAccess:
  enforce: true
constraints/compute.skipDefaultNetworkCreation:
  enforce: true
iam.disableServiceAccountKeyCreation:
  enforce: true
iam.disableServiceAccountKeyUpload:
  enforce: false
  rules:
  - condition:
      description: test condition
      expression: resource.matchTagId("tagKeys/1234", "tagValues/1234")
      location: somewhere
      title: condition
    enforce: true
provider "google" {
  impersonate_service_account = var.impersonate_service_account
}

resource "random_string" "project_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

module "project" {
  source = "../.."
  name = random_string.project_name.result
  parent = var.organization_id
  billing_account = var.billing_account_id
  org_policies_data_path = "configs/"
  services = ["cloudbilling.googleapis.com", "cloudresourcemanager.googleapis.com", "orgpolicy.googleapis.com"]
  org_policies = {
    "compute.disableGuestAttributesAccess" = {
      enforce = true
    }
    "constraints/compute.skipDefaultNetworkCreation" = {
      enforce = true
    }
    "iam.disableServiceAccountKeyCreation" = {
      enforce = false
    }
  }
}
variable "organization_id" {
  type = string
}

variable "billing_account_id" {
  type = string
}

variable "impersonate_service_account" {
  type = string  
}

Shared VPC service

The module allows managing Shared VPC status for both hosts and service projects, and includes a simple way of assigning Shared VPC roles to service identities.

provider "google" {
}

resource "random_string" "project_name_service" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

data "google_organization" "default" {
  domain = var.organization_domain
}

module "host-project" {
  source = "../.."
  name = "foobar-project-test"
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
  shared_vpc_host_config = {
    enabled = true
  }
}

module "service-project" {
  source = "../.."
  name = random_string.project_name_service.result
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
  shared_vpc_service_config = {
    attach       = true
    host_project = module.host-project.project_id
  }
}
variable "organization_domain" {
  type = string
}

variable "billing_account_id" {
  type = string
}

Logging Sinks

This Module creates a GCP Project with sink for logging

provider "google" {
}

resource "random_string" "project_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

resource "random_string" "bucket_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}


data "google_organization" "default" {
  domain = var.organization_domain
}

resource "google_storage_bucket" "default" {
  name          = random_string.bucket_name.result
  location      = "EU"
  force_destroy = true
  project       = var.bucket_project_id
}

module "project" {
  source = "../.."
  name = random_string.project_name.result
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
  logging_sinks = {
    debug = {
      project       = var.bucket_project_id
      destination   = google_storage_bucket.default.id
      filter        = "severity=DEBUG"
      type          = "storage"
      unique_writer = true
    }
  }
}
variable "organization_domain" {
  type = string
}

variable "billing_account_id" {
  type = string
}

variable "bucket_project_id" {
  type = string
}

Tags

Refer to the Creating and managing tags documentation for details on usage.

provider "google" {
}

resource "random_string" "project_name" {
  length           = 8
  special          = false
  upper            = false
  numeric          = false
}

data "google_organization" "default" {
  domain = var.organization_domain
}

resource "google_tags_tag_key" "default" {
  parent = data.google_organization.default.id
  short_name = "keyname"
  description = "For keyname resources."
}

resource "google_tags_tag_value" "default" {
    parent = google_tags_tag_key.default.id
    short_name = "valuename"
    description = "For valuename resources."
}

module "project" {
  source = "../.."
  name = random_string.project_name.result
  parent = data.google_organization.default.id
  billing_account = var.billing_account_id
  tag_bindings = {
    foo = google_tags_tag_value.default.id
  }
}
variable "organization_domain" {
  type = string
}

variable "billing_account_id" {
  type = string
}

Requirements

Name Version
terraform >= 1.3.1
google >= 4.40.0
google-beta >= 4.40.0

Inputs

Name Description Type Default Required
name Project name and id suffix. string n/a yes
auto_create_network Whether to create the default network for the project. bool false no
billing_account Billing account id. string null no
contacts List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. map(list(string)) {} no
custom_roles Map of role name => list of permissions to create in this project. map(list(string)) {} no
default_service_account Project default service account setting: can be one of delete, deprivilege, disable, or keep. string "keep" no
descriptive_name Name of the project name. Used for project name instead of name variable. string null no
group_iam Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the iam variable. map(list(string)) {} no
iam IAM bindings in {ROLE => [MEMBERS]} format. map(list(string)) {} no
iam_additive IAM additive bindings in {ROLE => [MEMBERS]} format. map(list(string)) {} no
iam_additive_members IAM additive bindings in {MEMBERS => [ROLE]} format. This might break if members are dynamic values. map(list(string)) {} no
labels Resource labels. map(string) {} no
lien_reason If non-empty, creates a project lien with this description. string "" no
logging_exclusions Logging exclusions for this project in the form {NAME -> FILTER}. map(string) {} no
logging_sinks Logging sinks to create for this project.
map(object({
bq_partitioned_table = optional(bool)
description = optional(string)
destination = string
disabled = optional(bool, false)
exclusions = optional(map(string), {})
filter = string
iam = optional(bool, true)
type = string
unique_writer = optional(bool)
}))
{} no
metric_scopes List of projects that will act as metric scopes for this project. list(string) [] no
org_policies Organization policies applied to this project keyed by policy name.
map(object({
inherit_from_parent = optional(bool) # for list policies only.
reset = optional(bool)

# default (unconditional) values
allow = optional(object({
all = optional(bool)
values = optional(list(string))
}))
deny = optional(object({
all = optional(bool)
values = optional(list(string))
}))
enforce = optional(bool, true) # for boolean policies only.

# conditional values
rules = optional(list(object({
allow = optional(object({
all = optional(bool)
values = optional(list(string))
}))
deny = optional(object({
all = optional(bool)
values = optional(list(string))
}))
enforce = optional(bool, true) # for boolean policies only.
condition = object({
description = optional(string)
expression = optional(string)
location = optional(string)
title = optional(string)
})
})), [])
}))
{} no
org_policies_data_path Path containing org policies in YAML format. string null no
oslogin Enable OS Login. bool false no
oslogin_admins List of IAM-style identities that will be granted roles necessary for OS Login administrators. list(string) [] no
oslogin_users List of IAM-style identities that will be granted roles necessary for OS Login users. list(string) [] no
parent Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. string null no
prefix Optional prefix used to generate project id and name. string null no
project_create Create project. When set to false, uses a data source to reference existing project. bool true no
service_config Configure service API activation.
object({
disable_on_destroy = bool
disable_dependent_services = bool
})
{
"disable_dependent_services": false,
"disable_on_destroy": false
}
no
service_encryption_key_ids Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. map(list(string)) {} no
service_perimeter_bridges Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. list(string) null no
service_perimeter_standard Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. string null no
services Service APIs to enable. list(string) [] no
shared_vpc_host_config Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project).
object({
enabled = bool
service_projects = optional(list(string), [])
})
null no
shared_vpc_service_config Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config).
object({
host_project = string
service_identity_iam = optional(map(list(string)))
})
null no
skip_delete Allows the underlying resources to be destroyed without destroying the project itself. bool false no
tag_bindings Tag bindings for this project, in key => tag value id format. map(string) null no

Outputs

Name Description
custom_roles Ids of the created custom roles.
name Project name.
number Project number.
project_id Project id.
service_accounts Product robot service accounts in project.
sink_writer_identities Writer identities created for each sink.

Resource types

Type Used
google-beta_google_compute_shared_vpc_host_project 1
google-beta_google_compute_shared_vpc_service_project 2
google-beta_google_essential_contacts_contact 1
google-beta_google_monitoring_monitored_project 1
google-beta_google_project_service_identity 2
google_access_context_manager_service_perimeter_resource 2
google_bigquery_dataset_iam_member 1
google_compute_project_metadata_item 1
google_kms_crypto_key_iam_member 1
google_logging_project_exclusion 1
google_logging_project_sink 1
google_org_policy_policy 1
google_project 1
google_project_default_service_accounts 1
google_project_iam_binding 1
google_project_iam_custom_role 1
google_project_iam_member 8
google_project_service 1
google_pubsub_topic_iam_member 1
google_resource_manager_lien 1
google_storage_bucket_iam_member 1
google_tags_tag_binding 1
Used only includes resource blocks. for_each and count meta arguments, as well as resource blocks of modules are not considered.

Modules

No modules.

Resources by Files

iam.tf

Name Type
google_project_iam_binding.authoritative resource
google_project_iam_custom_role.roles resource
google_project_iam_member.additive resource
google_project_iam_member.oslogin_admins resource
google_project_iam_member.oslogin_compute_viewer resource
google_project_iam_member.oslogin_iam_serviceaccountuser resource
google_project_iam_member.oslogin_users resource

logging.tf

Name Type
google_bigquery_dataset_iam_member.bq-sinks-binding resource
google_logging_project_exclusion.logging-exclusion resource
google_logging_project_sink.sink resource
google_project_iam_member.bucket-sinks-binding resource
google_pubsub_topic_iam_member.pubsub-sinks-binding resource
google_storage_bucket_iam_member.gcs-sinks-binding resource

main.tf

Name Type
google-beta_google_essential_contacts_contact.contact resource
google-beta_google_monitoring_monitored_project.primary resource
google_compute_project_metadata_item.oslogin_meta resource
google_project.project resource
google_project_service.project_services resource
google_resource_manager_lien.lien resource
google_project.project data source

organization-policies.tf

Name Type
google_org_policy_policy.default resource

service-accounts.tf

Name Type
google-beta_google_project_service_identity.jit_si resource
google-beta_google_project_service_identity.servicenetworking resource
google_kms_crypto_key_iam_member.service_identity_cmek resource
google_project_default_service_accounts.default_service_accounts resource
google_project_iam_member.servicenetworking resource
google_bigquery_default_service_account.bq_sa data source
google_storage_project_service_account.gcs_sa data source

shared-vpc.tf

Name Type
google-beta_google_compute_shared_vpc_host_project.shared_vpc_host resource
google-beta_google_compute_shared_vpc_service_project.service_projects resource
google-beta_google_compute_shared_vpc_service_project.shared_vpc_service resource
google_project_iam_member.shared_vpc_host_robots resource

tags.tf

Name Type
google_tags_tag_binding.binding resource

vpc-sc.tf

Name Type
google_access_context_manager_service_perimeter_resource.bridge resource
google_access_context_manager_service_perimeter_resource.standard resource

Contribute

This module is derived from google cloud foundation fabric module project v19. Refer to guide in terraform-google-landing-zone repository for information on integrating changes.

About

This Module describes a Google Project with Terraform

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages