From 708ca97b30cf02cda1c40959ace72f9a6ba20f3c Mon Sep 17 00:00:00 2001 From: Aleksandr Averbukh Date: Mon, 23 Mar 2020 10:51:56 +0100 Subject: [PATCH] business-units foundation example (#52) * Added folder-units module. * Business units example update (WIP) * Update all BU modules to internal ones * Refactoring business-units example, add billing and org IAM handling * update projects tests for new iam additive naming * update project README for new iam additive naming * streamline bu example and module (#53) Co-authored-by: Ludovico Magnocavallo --- foundations/business-units/README.md | 36 +-- foundations/business-units/backend.tf.sample | 4 +- foundations/business-units/main.tf | 258 ++++++++---------- .../modules/business-unit-folders/README.md | 24 -- .../modules/business-unit-folders/main.tf | 44 --- .../modules/business-unit-folders/outputs.tf | 26 -- .../business-unit-folders/variables.tf | 34 --- foundations/business-units/outputs.tf | 106 +++---- foundations/business-units/providers.tf | 30 +- .../versions.tf => terraform.tfvars.sample} | 10 +- foundations/business-units/variables.tf | 115 ++++---- foundations/business-units/versions.tf | 30 +- foundations/environments/README.md | 24 +- foundations/environments/main.tf | 20 +- foundations/environments/variables.tf | 2 +- modules/folders-unit/README.md | 56 ++++ modules/folders-unit/locals.tf | 63 +++++ modules/folders-unit/main.tf | 105 +++++++ modules/folders-unit/outputs.tf | 59 ++++ modules/folders-unit/variables.tf | 122 +++++++++ modules/folders-unit/versions.tf | 19 ++ modules/folders/main.tf | 15 - modules/net-vpc/README.md | 1 + modules/project/README.md | 7 +- modules/project/main.tf | 16 +- modules/project/variables.tf | 4 +- tests/modules/project/fixture/main.tf | 34 +-- tests/modules/project/fixture/variables.tf | 4 +- 28 files changed, 744 insertions(+), 524 deletions(-) delete mode 100644 foundations/business-units/modules/business-unit-folders/README.md delete mode 100644 foundations/business-units/modules/business-unit-folders/main.tf delete mode 100644 foundations/business-units/modules/business-unit-folders/outputs.tf delete mode 100644 foundations/business-units/modules/business-unit-folders/variables.tf rename foundations/business-units/{modules/business-unit-folders/versions.tf => terraform.tfvars.sample} (75%) create mode 100644 modules/folders-unit/README.md create mode 100644 modules/folders-unit/locals.tf create mode 100644 modules/folders-unit/main.tf create mode 100644 modules/folders-unit/outputs.tf create mode 100644 modules/folders-unit/variables.tf create mode 100644 modules/folders-unit/versions.tf diff --git a/foundations/business-units/README.md b/foundations/business-units/README.md index 684579bc06..7c74045893 100644 --- a/foundations/business-units/README.md +++ b/foundations/business-units/README.md @@ -2,7 +2,7 @@ This sample creates an organizational layout with two folder levels, where the first level is usually mapped to one business unit or team (infra, data, analytics) and the second level represents enviroments (prod, test). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and the correct roles on those to enforce separation of duties at the environment level. -This layout is well suited for medium-sized infrastructures managed by different sets of teams, and especially where the foundational infrastructure needs to be managed centrally, as the top-level automation service accounts for each environment allow cross-team management of the base resources (projects, IAM, etc.). +This layout is well suited for medium-sized infrastructures managed by different sets of teams, and in cases where the core infrastructure is managed centrally, as the top-level automation service accounts for each environment allow cross-team management of the base resources (projects, IAM, etc.). ![High-level diagram](diagram.png "High-level diagram") @@ -19,7 +19,7 @@ This sample creates several distinct groups of resources: - one project in the shared folder to set up and host centralized audit log exports - one project in the shared folder to hold services used across environments like GCS, GCR, KMS, Cloud Build, etc. -The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging the full array of [Cloud Foundation Toolkit modules](https://github.com/terraform-google-modules), especially in the shared services project. +The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging other [modules from our bundle](../../modules/), or from other sources like the [CFT suite](https://github.com/terraform-google-modules). ## Shared services @@ -31,38 +31,28 @@ This sample uses a top-level folder to encapsulate projects that host resources | name | description | type | required | default | |---|---|:---: |:---:|:---:| | billing_account_id | Billing account id used as default for new projects. | string | ✓ | | -| business_unit_1_name | Business unit 1 short name. | string | ✓ | | -| business_unit_2_name | Business unit 2 short name. | string | ✓ | | -| business_unit_3_name | Business unit 3 short name. | string | ✓ | | -| environments | Environment short names. | list(string) | ✓ | | | organization_id | Organization id. | string | ✓ | | | prefix | Prefix used for resources that need unique names. | string | ✓ | | | root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | -| *gcs_location* | GCS bucket location. | string | | EU | -| *generate_service_account_keys* | Generate and store service account keys in the state file. | bool | | false | +| *audit_filter* | Audit log filter used for the log sink. | string | | ... | +| *environments* | Environment short names. | map(string) | | ... | +| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | +| *iam_audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | +| *iam_shared_owners* | Shared services project owners, in IAM format. | list(string) | | [] | +| *iam_terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | | *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *shared_bindings_members* | List of comma-delimited IAM-format members for the additional shared project bindings. | list(string) | | [] | -| *shared_bindings_roles* | List of roles for additional shared project bindings. | list(string) | | [] | -| *terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | ## Outputs | name | description | sensitive | |---|---|:---:| -| audit_logs_bq_dataset | Bigquery dataset for the audit logs export. | | | audit_logs_project | Project that holds the audit logs export resources. | | | bootstrap_tf_gcs_bucket | GCS bucket used for the bootstrap Terraform state. | | -| business_unit_1_environment_folders_ids | Business unit 1 environment folders. | | -| business_unit_1_folder_id | Business unit 1 top-level folder ID. | | -| business_unit_2_environment_folders_ids | Business unit 2 environment folders. | | -| business_unit_2_folder_id | Business unit 2 top-level folder ID. | | -| business_unit_3_environment_folders_ids | Business unit 3 environment folders. | | -| business_unit_3_folder_id | Business unit 3 top-level folder ID. | | -| environment_service_account_keys | Service account keys used to run each environment Terraform modules. | ✓ | -| environment_service_accounts | Service accounts used to run each environment Terraform modules. | | -| environment_tf_gcs_buckets | GCS buckets used for each environment Terraform state. | | -| shared_folder_id | Shared folder ID. | | +| bu_bi | Business Intelligence attributes. | | +| bu_bi_sa_keys | Business Intelligence service account keys. | ✓ | +| bu_ml | Machine Learning attributes. | | +| bu_ml_sa_keys | Machine Learning service account keys. | ✓ | +| shared_folder_id | Shared folder id. | | | shared_resources_project | Project that holdes resources shared across business units. | | | terraform_project | Project that holds the base Terraform resources. | | diff --git a/foundations/business-units/backend.tf.sample b/foundations/business-units/backend.tf.sample index 19ccfaf66c..d63060c66e 100644 --- a/foundations/business-units/backend.tf.sample +++ b/foundations/business-units/backend.tf.sample @@ -1,4 +1,4 @@ -# Copyright 2019 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,4 +19,4 @@ terraform { # run apply again to transfer state bucket = "" } -} \ No newline at end of file +} diff --git a/foundations/business-units/main.tf b/foundations/business-units/main.tf index 2123126170..4dbc4d2601 100644 --- a/foundations/business-units/main.tf +++ b/foundations/business-units/main.tf @@ -1,130 +1,86 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# TODO(averbukh): simplify log-sink parameters once https://github.com/terraform-google-modules/terraform-google-log-export/issues/28 is done. - -locals { - parent_numeric_id = element(split("/", var.root_node), 1) - log_sink_parent_resource_type = element(split("/", var.root_node), 0) == "organizations" ? "organization" : "folder" - log_sink_name = element(split("/", var.root_node), 0) == "organizations" ? "logs-audit-org-${local.parent_numeric_id}" : "logs-audit-folder-${local.parent_numeric_id}" -} +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ ############################################################################### -# Shared resources folder # +# Terraform top-level resources # ############################################################################### +# Shared folder + module "shared-folder" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = var.root_node - names = ["shared"] + source = "../../modules/folders" + parent = var.root_node + names = ["shared"] } -############################################################################### -# Terraform top-level resources # -############################################################################### - # Terraform project -module "project-tf" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = module.shared-folder.id - billing_account = var.billing_account_id - prefix = var.prefix - name = "terraform" - lien_reason = "terraform" - owners = var.terraform_owners - activate_apis = var.project_services -} - -# Per environment service accounts - -module "service-accounts-tf-environments" { - source = "terraform-google-modules/service-accounts/google" - version = "2.0.2" - project_id = module.project-tf.project_id - org_id = var.organization_id - billing_account_id = var.billing_account_id - prefix = var.prefix - names = var.environments - grant_billing_role = true - generate_keys = var.generate_service_account_keys +module "tf-project" { + source = "../../modules/project" + name = "terraform" + parent = module.shared-folder.id + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { "roles/owner" = var.iam_terraform_owners } + iam_additive_roles = ["roles/owner"] + services = var.project_services } # Bootstrap Terraform state GCS bucket -module "gcs-tf-bootstrap" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" +module "tf-gcs-bootstrap" { + source = "../../modules/gcs" + project_id = module.tf-project.project_id names = ["tf-bootstrap"] - location = var.gcs_location -} - -# Per environment Terraform state GCS buckets - -module "gcs-tf-environments" { - source = "terraform-google-modules/cloud-storage/google" - version = "1.0.0" - project_id = module.project-tf.project_id - prefix = "${var.prefix}-tf" - names = var.environments - location = var.gcs_location - set_admin_roles = true - bucket_admins = zipmap( - var.environments, - module.service-accounts-tf-environments.iam_emails_list - ) + prefix = "${var.prefix}-tf" + location = var.gcs_defaults.location } ############################################################################### -# Business units # +# Business units # ############################################################################### -# Business unit 1 - -module "business-unit-1-folders" { - source = "./modules/business-unit-folders" - business_unit_folder_name = var.business_unit_1_name - environments = var.environments - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - root_node = var.root_node - -} - -# Business unit 2 - -module "business-unit-2-folders" { - source = "./modules/business-unit-folders" - business_unit_folder_name = var.business_unit_2_name - environments = var.environments - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - root_node = var.root_node - +module "bu-business-intelligence" { + source = "../../modules/folders-unit" + name = "Business Intelligence" + short_name = "bi" + automation_project_id = module.tf-project.project_id + billing_account_id = var.billing_account_id + environments = var.environments + gcs_defaults = var.gcs_defaults + organization_id = var.organization_id + root_node = var.root_node + # extra variables from the folders-unit module can be used here to grant + # IAM roles to the bu users, configure the automation service accounts, etc. + # iam_roles = ["viewer"] + # iam_members = { viewer = ["user:user@example.com"] } } -# Business unit 3 - -module "business-unit-3-folders" { - source = "./modules/business-unit-folders" - business_unit_folder_name = var.business_unit_3_name - environments = var.environments - per_folder_admins = module.service-accounts-tf-environments.iam_emails_list - root_node = var.root_node - +module "bu-machine-learning" { + source = "../../modules/folders-unit" + name = "Machine Learning" + short_name = "ml" + automation_project_id = module.tf-project.project_id + billing_account_id = var.billing_account_id + environments = var.environments + gcs_defaults = var.gcs_defaults + organization_id = var.organization_id + root_node = var.root_node + # extra variables from the folders-unit module can be used here to grant + # IAM roles to the bu users, configure the automation service accounts, etc. } ############################################################################### @@ -133,42 +89,50 @@ module "business-unit-3-folders" { # Audit logs project -module "project-audit" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = module.shared-folder.id - billing_account = var.billing_account_id - prefix = var.prefix +module "audit-project" { + source = "../../modules/project" name = "audit" - lien_reason = "audit" - viewers = var.audit_viewers - activate_apis = concat(var.project_services, [ + parent = var.root_node + prefix = var.prefix + billing_account = var.billing_account_id + iam_members = { + "roles/bigquery.dataEditor" = [module.audit-log-sinks.writer_identities[0]] + "roles/viewer" = var.iam_audit_viewers + } + iam_roles = [ + "roles/bigquery.dataEditor", + "roles/viewer" + ] + services = concat(var.project_services, [ "bigquery.googleapis.com", ]) } -# Audit logs destination on BigQuery - -module "bq-audit-export" { - source = "terraform-google-modules/log-export/google//modules/bigquery" - version = "3.2.0" - project_id = module.project-audit.project_id - dataset_name = "${replace(local.log_sink_name, "-", "_")}" - log_sink_writer_identity = module.log-sink-audit.writer_identity +# audit logs dataset and sink + +module "audit-datasets" { + source = "../../modules/bigquery" + project_id = module.audit-project.project_id + datasets = { + audit_export = { + name = "Audit logs export." + description = "Terraform managed." + location = "EU" + labels = null + options = null + } + } } -# Audit log sink for root node - -module "log-sink-audit" { - source = "terraform-google-modules/log-export/google" - version = "3.2.0" - filter = "logName: \"/logs/cloudaudit.googleapis.com%2Factivity\" OR logName: \"/logs/cloudaudit.googleapis.com%2Fsystem_event\"" - log_sink_name = local.log_sink_name - parent_resource_type = local.log_sink_parent_resource_type - parent_resource_id = local.parent_numeric_id - include_children = "true" - unique_writer_identity = "true" - destination_uri = "${module.bq-audit-export.destination_uri}" +module "audit-log-sinks" { + source = "../../modules/logging-sinks" + parent = var.root_node + destinations = { + audit-logs = "bigquery.googleapis.com/projects/${module.audit-project.project_id}/datasets/${module.audit-datasets.names[0]}" + } + sinks = { + audit-logs = var.audit_filter + } } ############################################################################### @@ -177,17 +141,19 @@ module "log-sink-audit" { # Shared resources project -module "project-shared-resources" { - source = "terraform-google-modules/project-factory/google//modules/fabric-project" - version = "5.0.0" - parent = module.shared-folder.id - billing_account = var.billing_account_id - prefix = var.prefix - name = "shared" - lien_reason = "shared" - activate_apis = var.project_services - extra_bindings_roles = var.shared_bindings_roles - extra_bindings_members = var.shared_bindings_members +module "shared-project" { + source = "../../modules/project" + name = "shared" + parent = module.shared-folder.id + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { + "roles/owner" = var.iam_shared_owners + } + iam_additive_roles = [ + "roles/owner" + ] + services = var.project_services } # Add further modules here for resources that are common to all business units diff --git a/foundations/business-units/modules/business-unit-folders/README.md b/foundations/business-units/modules/business-unit-folders/README.md deleted file mode 100644 index 89081edb45..0000000000 --- a/foundations/business-units/modules/business-unit-folders/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Two-level folders tree - -This module is a simple wrapper for the [Cloud Foundation Folder module](https://github.com/terraform-google-modules/terraform-google-folders), that manages one folder and one child folder under it, for each name passed in the `environments` variable. It is meant to be used for organizational layouts where a predefined number of folders representing environments, are created as child folders for business units or teams. - -For details on how the IAM variables work, please refer to the [Cloud Foundation Folder module](https://github.com/terraform-google-modules/terraform-google-folders). - - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| business\_unit\_folder\_name | Business Unit Folder name. | string | n/a | yes | -| environments | Environment short names. | list(string) | n/a | yes | -| per\_folder\_admins | List of IAM-style members per folder who will get extended permissions. | list(string) | `` | no | -| root\_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | n/a | yes | - -## Outputs - -| Name | Description | -|------|-------------| -| business\_unit\_folder\_id | Business Unit Folder ID. | -| environment\_folders\_ids | Environment folders IDs. | - - diff --git a/foundations/business-units/modules/business-unit-folders/main.tf b/foundations/business-units/modules/business-unit-folders/main.tf deleted file mode 100644 index 0b48b5444c..0000000000 --- a/foundations/business-units/modules/business-unit-folders/main.tf +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -############################################################################### -# Business Unit Folder # -############################################################################### - -module "business-unit-folder" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = var.root_node - names = [var.business_unit_folder_name] -} - -############################################################################### -# Environment Folders # -############################################################################### - -module "environment-folders" { - source = "terraform-google-modules/folders/google" - version = "2.0.2" - parent = module.business-unit-folder.id - names = var.environments - set_roles = true - per_folder_admins = var.per_folder_admins - folder_admin_roles = [ - "roles/resourcemanager.folderViewer", - "roles/resourcemanager.projectCreator", - "roles/owner", - "roles/compute.networkAdmin", - "roles/compute.xpnAdmin" - ] -} diff --git a/foundations/business-units/modules/business-unit-folders/outputs.tf b/foundations/business-units/modules/business-unit-folders/outputs.tf deleted file mode 100644 index 78e4b5d856..0000000000 --- a/foundations/business-units/modules/business-unit-folders/outputs.tf +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -output "business_unit_folder_id" { - description = "Business Unit Folder ID." - value = module.business-unit-folder.id -} - -output "environment_folders_ids" { - description = "Environment folders IDs." - value = module.environment-folders.ids -} - -# Add further outputs here for the additional modules that manage shared -# resources, like GCR, GCS buckets, KMS, etc. diff --git a/foundations/business-units/modules/business-unit-folders/variables.tf b/foundations/business-units/modules/business-unit-folders/variables.tf deleted file mode 100644 index 936eda6879..0000000000 --- a/foundations/business-units/modules/business-unit-folders/variables.tf +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -variable "business_unit_folder_name" { - description = "Business Unit Folder name." - type = string -} - -variable "environments" { - description = "Environment short names." - type = list(string) -} - -variable "per_folder_admins" { - type = list(string) - description = "List of IAM-style members per folder who will get extended permissions." - default = [] -} - -variable "root_node" { - description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." - type = string -} diff --git a/foundations/business-units/outputs.tf b/foundations/business-units/outputs.tf index b82a102aa0..51d5c21343 100644 --- a/foundations/business-units/outputs.tf +++ b/foundations/business-units/outputs.tf @@ -1,91 +1,75 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ output "terraform_project" { description = "Project that holds the base Terraform resources." - value = module.project-tf.project_id + value = module.tf-project.project_id } output "bootstrap_tf_gcs_bucket" { description = "GCS bucket used for the bootstrap Terraform state." - value = module.gcs-tf-bootstrap.name -} - -output "environment_tf_gcs_buckets" { - description = "GCS buckets used for each environment Terraform state." - value = module.gcs-tf-environments.names -} - -output "environment_service_account_keys" { - description = "Service account keys used to run each environment Terraform modules." - sensitive = true - value = module.service-accounts-tf-environments.keys -} - -output "environment_service_accounts" { - description = "Service accounts used to run each environment Terraform modules." - value = module.service-accounts-tf-environments.emails + value = module.tf-gcs-bootstrap.name } output "shared_folder_id" { - description = "Shared folder ID." + description = "Shared folder id." value = module.shared-folder.id } -output "business_unit_1_folder_id" { - description = "Business unit 1 top-level folder ID." - value = module.business-unit-1-folders.business_unit_folder_id -} - -output "business_unit_1_environment_folders_ids" { - description = "Business unit 1 environment folders." - value = module.business-unit-1-folders.environment_folders_ids -} -output "business_unit_2_folder_id" { - description = "Business unit 2 top-level folder ID." - value = module.business-unit-2-folders.business_unit_folder_id +output "bu_machine_learning" { + description = "Machine Learning attributes." + value = { + unit_folder = module.bu-machine-learning.unit_folder, + env_gcs_buckets = module.bu-machine-learning.env_gcs_buckets + env_folders = module.bu-machine-learning.env_folders + env_service_accounts = module.bu-machine-learning.env_service_accounts + } } -output "business_unit_2_environment_folders_ids" { - description = "Business unit 2 environment folders." - value = module.business-unit-2-folders.environment_folders_ids -} - -output "business_unit_3_folder_id" { - description = "Business unit 3 top-level folder ID." - value = module.business-unit-3-folders.business_unit_folder_id +output "bu_machine_learning_keys" { + description = "Machine Learning service account keys." + sensitive = true + value = module.bu-machine-learning.env_sa_keys } -output "business_unit_3_environment_folders_ids" { - description = "Business unit 3 environment folders." - value = module.business-unit-3-folders.environment_folders_ids +output "bu_business_intelligence" { + description = "Business Intelligence attributes." + value = { + unit_folder = module.bu-business-intelligence.unit_folder, + env_gcs_buckets = module.bu-business-intelligence.env_gcs_buckets + env_folders = module.bu-business-intelligence.env_folders + env_service_accounts = module.bu-business-intelligence.env_service_accounts + } } -output "audit_logs_bq_dataset" { - description = "Bigquery dataset for the audit logs export." - value = module.bq-audit-export.resource_name +output "bu_business_intelligence_keys" { + description = "Business Intelligence service account keys." + sensitive = true + value = module.bu-business-intelligence.env_sa_keys } output "audit_logs_project" { description = "Project that holds the audit logs export resources." - value = module.project-audit.project_id + value = module.audit-project.project_id } output "shared_resources_project" { description = "Project that holdes resources shared across business units." - value = module.project-shared-resources.project_id + value = module.shared-project.project_id } # Add further outputs here for the additional modules that manage shared -# resources, like GCR, GCS buckets, KMS, etc. \ No newline at end of file +# resources, like GCR, GCS buckets, KMS, etc. diff --git a/foundations/business-units/providers.tf b/foundations/business-units/providers.tf index b166f75dc4..d57e94c5de 100644 --- a/foundations/business-units/providers.tf +++ b/foundations/business-units/providers.tf @@ -1,15 +1,17 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -provider "google" {} \ No newline at end of file +provider "google" {} diff --git a/foundations/business-units/modules/business-unit-folders/versions.tf b/foundations/business-units/terraform.tfvars.sample similarity index 75% rename from foundations/business-units/modules/business-unit-folders/versions.tf rename to foundations/business-units/terraform.tfvars.sample index 4eb1500c5a..5545bf3f57 100644 --- a/foundations/business-units/modules/business-unit-folders/versions.tf +++ b/foundations/business-units/terraform.tfvars.sample @@ -1,4 +1,4 @@ -# Copyright 2019 Google LLC +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -terraform { - required_version = ">= 0.12" -} +billing_account_id = "014617-19UCBC-AF02D9" +organization_id= "500001140800" +prefix = "xyz" +root_node = "folders/9572793983696" +generate_keys = true diff --git a/foundations/business-units/variables.tf b/foundations/business-units/variables.tf index 3dbecffd1a..cf86ab424e 100644 --- a/foundations/business-units/variables.tf +++ b/foundations/business-units/variables.tf @@ -1,21 +1,27 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -variable "audit_viewers" { - description = "Audit project viewers, in IAM format." - type = list(string) - default = [] +variable "audit_filter" { + description = "Audit log filter used for the log sink." + type = string + default = <string | ✓ | | +| billing_account_id | Billing account id used as to create projects. | string | ✓ | | | environments | Environment short names. | list(string) | ✓ | | -| organization_id | Organization id. | string | ✓ | | | prefix | Prefix used for resources that need unique names. | string | ✓ | | | root_node | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| *audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | +| *audit_filter* | Audit log filter used for the log sink. | string | | ... | | *gcs_location* | GCS bucket location. | string | | EU | -| *generate_service_account_keys* | Generate and store service account keys in the state file. | bool | | false | -| *grant_xpn_folder_roles* | Grant roles needed for Shared VPC creation to service accounts at the environment folder level. | bool | | true | -| *grant_xpn_org_roles* | Grant roles needed for Shared VPC creation to service accounts at the organization level. | bool | | false | +| *iam_assets_editors* | Shared assets project editors, in IAM format. | list(string) | | [] | +| *iam_assets_owners* | Shared assets project owners, in IAM format. | list(string) | | [] | +| *iam_audit_viewers* | Audit project viewers, in IAM format. | list(string) | | [] | +| *iam_billing_config* | Control granting billing user role to service accounts. Target the billing account by default. | object({...}) | | ... | +| *iam_folder_roles* | List of roles granted to each service account on its respective folder (excluding XPN roles). | list(string) | | ... | +| *iam_sharedsvc_owners* | Shared services project owners, in IAM format. | list(string) | | [] | +| *iam_terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | +| *iam_xpn_config* | Control granting Shared VPC creation roles to service accounts. Target the root node by default. | object({...}) | | ... | +| *organization_id* | Organization id. | string | | null | | *project_services* | Service APIs enabled by default in new projects. | list(string) | | ... | -| *shared_bindings_members* | List of comma-delimited IAM-format members for the additional shared project bindings. | list(string) | | [] | -| *shared_bindings_roles* | List of roles for additional shared project bindings. | list(string) | | [] | -| *terraform_owners* | Terraform project owners, in IAM format. | list(string) | | [] | +| *service_account_keys* | Generate and store service account keys in the state file. | bool | | true | ## Outputs @@ -59,5 +62,4 @@ If no shared services are needed, the shared service project module can of cours | environment_service_accounts | Service accounts used to run each environment Terraform modules. | | | environment_tf_gcs_buckets | GCS buckets used for each environment Terraform state. | | | shared_resources_project | Project that holdes resources shared across environments. | | -| terraform_project | Project that holds the base Terraform resources. | | diff --git a/foundations/environments/main.tf b/foundations/environments/main.tf index 6332e34596..754b461a0e 100644 --- a/foundations/environments/main.tf +++ b/foundations/environments/main.tf @@ -19,14 +19,14 @@ # Terraform project module "tf-project" { - source = "../../modules/project" - name = "terraform" - parent = var.root_node - prefix = var.prefix - billing_account = var.billing_account_id - iam_nonauth_members = { "roles/owner" = var.iam_terraform_owners } - iam_nonauth_roles = ["roles/owner"] - services = var.project_services + source = "../../modules/project" + name = "terraform" + parent = var.root_node + prefix = var.prefix + billing_account = var.billing_account_id + iam_additive_members = { "roles/owner" = var.iam_terraform_owners } + iam_additive_roles = ["roles/owner"] + services = var.project_services } # per-environment service accounts @@ -163,10 +163,10 @@ module "sharedsvc-project" { parent = var.root_node prefix = var.prefix billing_account = var.billing_account_id - iam_members = { + iam_additive_members = { "roles/owner" = var.iam_sharedsvc_owners } - iam_roles = [ + iam_additive_roles = [ "roles/owner" ] services = var.project_services diff --git a/foundations/environments/variables.tf b/foundations/environments/variables.tf index f84890a21e..6bdafadf03 100644 --- a/foundations/environments/variables.tf +++ b/foundations/environments/variables.tf @@ -95,7 +95,7 @@ variable "iam_xpn_config" { description = "Control granting Shared VPC creation roles to service accounts. Target the root node by default." type = object({ grant = bool - target_org = string + target_org = bool }) default = { grant = true diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md new file mode 100644 index 0000000000..fafc4c0fd6 --- /dev/null +++ b/modules/folders-unit/README.md @@ -0,0 +1,56 @@ +# Google Cloud Unit Folders Module + +This module allows creation and management of an organizational hierarchy "unit" composed of a parent folder (usually mapped to a business unit or team), and a set of child folders (usually mapped to environments) each with a corresponding set of service accounts, IAM bindings and GCS buckets. + +## Example + +```hcl +module "folders-unit" { + source = "./modules/folders-unit" + name = "Business Intelligence" + short_name = "bi" + automation_project_id = "automation-project-394yr923811" + billing_account_id = "015617-16GHBC-AF02D9" + organization_id = "506128240800" + root_node = "folders/93469270123701" + prefix = "unique-prefix" + environments = { + dev = "Development", + test = "Testing", + prod = "Production" + } + service_account_keys = true +} +``` + + +## Variables + +| name | description | type | required | default | +|---|---|:---: |:---:|:---:| +| automation_project_id | Project id used for automation service accounts. | string | ✓ | | +| billing_account_id | Country billing account account. | string | ✓ | | +| name | Top folder name. | string | ✓ | | +| organization_id | Organization id. | string | ✓ | | +| root_node | Root node in folders/folder_id or organizations/org_id format. | string | ✓ | | +| short_name | Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces. | string | ✓ | | +| *environments* | Unit environments short names. | map(string) | | ... | +| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string) | | ... | +| *iam_billing_config* | Grant billing user role to service accounts, defaults to granting on the billing account. | object({...}) | | ... | +| *iam_enviroment_roles* | IAM roles granted to the environment service account on the environment sub-folder. | list(string) | | ... | +| *iam_members* | IAM members for roles applied on the unit folder. | map(list(string)) | | null | +| *iam_roles* | IAM roles applied on the unit folder. | list(string) | | null | +| *iam_xpn_config* | Grant Shared VPC creation roles to service accounts, defaults to granting at folder level. | object({...}) | | ... | +| *prefix* | Optional prefix used for GCS bucket names to ensure uniqueness. | string | | null | +| *service_account_keys* | Generate and store service account keys in the state file. | bool | | false | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| env_folders | Unit environments folders. | | +| env_gcs_buckets | Unit environments tfstate gcs buckets. | | +| env_sa_keys | Unit environments service account keys. | ✓ | +| env_service_accounts | Unit environments service accounts. | | +| unit_folder | Unit top level folder. | | + diff --git a/modules/folders-unit/locals.tf b/modules/folders-unit/locals.tf new file mode 100644 index 0000000000..1ad80700b9 --- /dev/null +++ b/modules/folders-unit/locals.tf @@ -0,0 +1,63 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + folder_roles = concat(var.iam_enviroment_roles, local.sa_xpn_folder_roles) + iam_members = var.iam_members == null ? {} : var.iam_members + iam_roles = var.iam_roles == null ? [] : var.iam_roles + unit_iam_bindings = { + for role in local.iam_roles : + role => lookup(local.iam_members, role, []) + } + folder_iam_service_account_bindings = { + for pair in setproduct(keys(var.environments), local.folder_roles) : + "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } + } + org_iam_service_account_bindings = { + for pair in setproduct(keys(var.environments), concat( + local.sa_xpn_org_roles, + local.sa_billing_org_roles, + local.sa_billing_org_roles)) : + "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } + } + billing_iam_service_account_bindings = { + for pair in setproduct(keys(var.environments), local.sa_billing_account_roles) : + "${pair.0}-${pair.1}" => { environment = pair.0, role = pair.1 } + } + service_accounts = { + for key, sa in google_service_account.environment : + key => "serviceAccount:${sa.email}" + } + sa_billing_account_roles = ( + var.iam_billing_config.target_org ? [] : ["roles/billing.user"] + ) + sa_billing_org_roles = ( + ! var.iam_billing_config.target_org ? [] : ["roles/billing.user"] + ) + sa_xpn_folder_roles = ( + local.sa_xpn_target_org ? [] : ["roles/compute.xpnAdmin"] + ) + sa_xpn_org_roles = ( + local.sa_xpn_target_org + ? ["roles/compute.xpnAdmin", "roles/resourcemanager.organizationViewer"] + : ["roles/resourcemanager.organizationViewer"] + ) + sa_xpn_target_org = ( + var.iam_xpn_config.target_org + || + substr(var.root_node, 0, 13) == "organizations" + ) +} diff --git a/modules/folders-unit/main.tf b/modules/folders-unit/main.tf new file mode 100644 index 0000000000..dfc7830600 --- /dev/null +++ b/modules/folders-unit/main.tf @@ -0,0 +1,105 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +############################################################################### +# Folders and folder IAM # +############################################################################### + +resource "google_folder" "unit" { + display_name = var.name + parent = var.root_node +} + +resource "google_folder" "environment" { + for_each = var.environments + display_name = each.value + parent = google_folder.unit.name +} + +resource "google_folder_iam_binding" "unit" { + for_each = local.unit_iam_bindings + folder = google_folder.unit.name + role = each.key + members = each.value +} + +resource "google_folder_iam_binding" "environment" { + for_each = local.folder_iam_service_account_bindings + folder = google_folder.environment[each.value.environment].name + role = each.value.role + members = [local.service_accounts[each.value.environment]] +} + +############################################################################### +# Billing account and org IAM # +############################################################################### + +resource "google_organization_iam_member" "org_iam_member" { + for_each = local.org_iam_service_account_bindings + org_id = var.organization_id + role = each.value.role + member = local.service_accounts[each.value.environment] +} + +resource "google_billing_account_iam_member" "billing_iam_member" { + for_each = local.billing_iam_service_account_bindings + billing_account_id = var.billing_account_id + role = each.value.role + member = local.service_accounts[each.value.environment] +} + +################################################################################ +# Service Accounts # +################################################################################ + +resource "google_service_account" "environment" { + for_each = var.environments + project = var.automation_project_id + account_id = "${var.short_name}-${each.key}" + display_name = "${var.short_name} ${each.key} (Terraform managed)." +} + +resource "google_service_account_key" "keys" { + for_each = var.service_account_keys ? var.environments : {} + service_account_id = google_service_account.environment[each.key].email +} + +################################################################################ +# GCS and GCS IAM # +################################################################################ + +resource "google_storage_bucket" "tfstate" { + for_each = var.environments + project = var.automation_project_id + name = join("", [ + var.prefix == null ? "" : "${var.prefix}-", + "${var.short_name}-${each.key}-tf" + ]) + location = var.gcs_defaults.location + storage_class = var.gcs_defaults.storage_class + force_destroy = false + bucket_policy_only = true + versioning { + enabled = true + } +} + +resource "google_storage_bucket_iam_binding" "bindings" { + for_each = var.environments + bucket = google_storage_bucket.tfstate[each.key].name + role = "roles/storage.objectAdmin" + members = [local.service_accounts[each.key]] +} diff --git a/modules/folders-unit/outputs.tf b/modules/folders-unit/outputs.tf new file mode 100644 index 0000000000..520712f50a --- /dev/null +++ b/modules/folders-unit/outputs.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "unit_folder" { + description = "Unit top level folder." + value = { + id = google_folder.unit.name, + name = google_folder.unit.display_name + } +} + +output "env_gcs_buckets" { + description = "Unit environments tfstate gcs buckets." + value = { + for key, bucket in google_storage_bucket.tfstate + : key => bucket.name + } +} + +output "env_folders" { + description = "Unit environments folders." + value = { + for key, folder in google_folder.environment + : key => { + id = folder.name, + name = folder.display_name + } + } +} + +output "env_service_accounts" { + description = "Unit environments service accounts." + value = { + for key, sa in google_service_account.environment + : key => sa.email + } +} + +output "env_sa_keys" { + description = "Unit environments service account keys." + sensitive = true + value = { + for key, sa_key in google_service_account_key.keys : + key => sa_key.private_key + } +} diff --git a/modules/folders-unit/variables.tf b/modules/folders-unit/variables.tf new file mode 100644 index 0000000000..7b1e37e9db --- /dev/null +++ b/modules/folders-unit/variables.tf @@ -0,0 +1,122 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "automation_project_id" { + description = "Project id used for automation service accounts." + type = string +} + +variable "billing_account_id" { + description = "Country billing account account." + type = string +} + +variable "environments" { + description = "Unit environments short names." + type = map(string) + default = { + non-prod = "Non production" + prod = "Production" + } +} + +variable "gcs_defaults" { + description = "Defaults use for the state GCS buckets." + type = map(string) + default = { + location = "EU" + storage_class = "MULTI_REGIONAL" + } +} + +variable "iam_billing_config" { + description = "Grant billing user role to service accounts, defaults to granting on the billing account." + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = false + } +} + +variable "iam_enviroment_roles" { + description = "IAM roles granted to the environment service account on the environment sub-folder." + type = list(string) + default = [ + "roles/compute.networkAdmin", + "roles/owner", + "roles/resourcemanager.folderAdmin", + "roles/resourcemanager.projectCreator", + ] +} + +variable "iam_members" { + description = "IAM members for roles applied on the unit folder." + type = map(list(string)) + default = null +} + +variable "iam_roles" { + description = "IAM roles applied on the unit folder." + type = list(string) + default = null +} + +variable "iam_xpn_config" { + description = "Grant Shared VPC creation roles to service accounts, defaults to granting at folder level." + type = object({ + grant = bool + target_org = bool + }) + default = { + grant = true + target_org = false + } +} + +variable "name" { + description = "Top folder name." + type = string +} + +variable "organization_id" { + description = "Organization id." + type = string +} + +variable "prefix" { + description = "Optional prefix used for GCS bucket names to ensure uniqueness." + type = string + default = null +} + +variable "root_node" { + description = "Root node in folders/folder_id or organizations/org_id format." + type = string +} + +variable "service_account_keys" { + description = "Generate and store service account keys in the state file." + type = bool + default = false +} + +variable "short_name" { + description = "Short name used as GCS bucket and service account prefixes, do not use capital letters or spaces." + type = string +} diff --git a/modules/folders-unit/versions.tf b/modules/folders-unit/versions.tf new file mode 100644 index 0000000000..ce6918e09d --- /dev/null +++ b/modules/folders-unit/versions.tf @@ -0,0 +1,19 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 0.12.6" +} diff --git a/modules/folders/main.tf b/modules/folders/main.tf index 3b2bbe8b97..9e55b4ce79 100644 --- a/modules/folders/main.tf +++ b/modules/folders/main.tf @@ -33,15 +33,6 @@ resource "google_folder" "folders" { parent = var.parent } -# give project creation access to service accounts -# https://cloud.google.com/resource-manager/docs/access-control-folders#granting_folder-specific_roles_to_enable_project_creation -# - external users need to have accepted the invitation email to join -# "roles/owner", -# "roles/resourcemanager.folderViewer", -# "roles/resourcemanager.projectCreator", -# "roles/compute.networkAdmin", - - resource "google_folder_iam_binding" "authoritative" { for_each = local.iam_keypairs folder = google_folder.folders[each.value.name].name @@ -51,9 +42,3 @@ resource "google_folder_iam_binding" "authoritative" { ) } -# resource "google_folder_iam_member" "non_authoritative" { -# for_each = length(var.iam_non_authoritative_roles) > 0 ? local.iam_non_authoritative : {} -# folder = google_project.project.project_id -# role = each.value.role -# member = each.value.member -# } diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index 6c7884f860..7259abf157 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -25,6 +25,7 @@ module "vpc" { subnet-2 = { ip_cidr_range = "10.0.16.0/24" region = "europe-west1" + secondary_ip_range = {} } } } diff --git a/modules/project/README.md b/modules/project/README.md index bd02563e1c..2d79bd6888 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -28,20 +28,19 @@ module "project" { |---|---|:---: |:---:|:---:| | name | Project name and id suffix. | string | ✓ | | | parent | The resource name of the parent Folder or Organization. Must be of the form folders/folder_id or organizations/org_id. | string | ✓ | | -| prefix | Prefix used to generate project id and name. | string | ✓ | | | *auto_create_network* | Whether to create the default network for the project | bool | | false | | *billing_account* | Billing account id. | string | | | | *custom_roles* | Map of role name => list of permissions to create in this project. | map(list(string)) | | {} | +| *iam_additive_members* | Map of member lists used to set non authoritative bindings, keyed by role. | map(list(string)) | | {} | +| *iam_additive_roles* | List of roles used to set non authoritative bindings. | list(string) | | [] | | *iam_members* | Map of member lists used to set authoritative bindings, keyed by role. | map(list(string)) | | {} | -| *iam_nonauth_members* | Map of member lists used to set non authoritative bindings, keyed by role. | map(list(string)) | | {} | -| *iam_nonauth_roles* | List of roles used to set non authoritative bindings. | list(string) | | [] | | *iam_roles* | List of roles used to set authoritative bindings. | list(string) | | [] | | *labels* | Resource labels. | map(string) | | {} | | *lien_reason* | If non-empty, creates a project lien with this description. | string | | | | *oslogin* | Enable OS Login. | bool | | false | | *oslogin_admins* | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | list(string) | | [] | | *oslogin_users* | List of IAM-style identities that will be granted roles necessary for OS Login users. | list(string) | | [] | -| *prevent_default_network_deletion* | Prevent deletion of default network (use this if your organization has skipDefaultNetworkCreation enforced) | bool | | false | +| *prefix* | Prefix used to generate project id and name. | string | | null | | *services* | Service APIs to enable. | list(string) | | [] | ## Outputs diff --git a/modules/project/main.tf b/modules/project/main.tf index b684ed0cb0..e6d8bab679 100644 --- a/modules/project/main.tf +++ b/modules/project/main.tf @@ -19,14 +19,14 @@ locals { gce_service_account = "${google_project.project.number}-compute@developer.gserviceaccount.com" gcr_service_account = "service-${google_project.project.number}@containerregistry.iam.gserviceaccount.com" gke_service_account = "service-${google_project.project.number}@container-engine-robot.iam.gserviceaccount.com" - iam_nonauth_pairs = flatten([ - for role in var.iam_nonauth_roles : [ - for member in lookup(var.iam_nonauth_members, role, []) : + iam_additive_pairs = flatten([ + for role in var.iam_additive_roles : [ + for member in lookup(var.iam_additive_members, role, []) : { role = role, member = member } ] ]) - iam_nonauth = { - for pair in local.iam_nonauth_pairs : + iam_additive = { + for pair in local.iam_additive_pairs : "${pair.role}-${pair.member}" => pair } parent_type = split("/", var.parent)[0] @@ -81,7 +81,7 @@ resource "google_project_service" "project_services" { # IAM notes: # - external users need to have accepted the invitation email to join # - oslogin roles also require role to list instances -# - non-authoritative roles might fail due to dynamic values +# - additive (non-authoritative) roles might fail due to dynamic values resource "google_project_iam_binding" "authoritative" { for_each = toset(var.iam_roles) @@ -90,8 +90,8 @@ resource "google_project_iam_binding" "authoritative" { members = lookup(var.iam_members, each.value, []) } -resource "google_project_iam_member" "non_authoritative" { - for_each = length(var.iam_nonauth_roles) > 0 ? local.iam_nonauth : {} +resource "google_project_iam_member" "additive" { + for_each = length(var.iam_additive_roles) > 0 ? local.iam_additive : {} project = google_project.project.project_id role = each.value.role member = each.value.member diff --git a/modules/project/variables.tf b/modules/project/variables.tf index fb71debd6a..b9ecc8645a 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -44,13 +44,13 @@ variable "iam_roles" { default = [] } -variable "iam_nonauth_members" { +variable "iam_additive_members" { description = "Map of member lists used to set non authoritative bindings, keyed by role." type = map(list(string)) default = {} } -variable "iam_nonauth_roles" { +variable "iam_additive_roles" { description = "List of roles used to set non authoritative bindings." type = list(string) default = [] diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf index 07a883e99c..91a838f637 100644 --- a/tests/modules/project/fixture/main.tf +++ b/tests/modules/project/fixture/main.tf @@ -15,21 +15,21 @@ */ module "test" { - source = "../../../../modules/project" - name = "my-project" - billing_account = "12345-12345-12345" - auto_create_network = var.auto_create_network - custom_roles = var.custom_roles - iam_members = var.iam_members - iam_roles = var.iam_roles - iam_nonauth_members = var.iam_nonauth_members - iam_nonauth_roles = var.iam_nonauth_roles - labels = var.labels - lien_reason = var.lien_reason - oslogin = var.oslogin - oslogin_admins = var.oslogin_admins - oslogin_users = var.oslogin_users - parent = var.parent - prefix = var.prefix - services = var.services + source = "../../../../modules/project" + name = "my-project" + billing_account = "12345-12345-12345" + auto_create_network = var.auto_create_network + custom_roles = var.custom_roles + iam_members = var.iam_members + iam_roles = var.iam_roles + iam_additive_members = var.iam_additive_members + iam_additive_roles = var.iam_additive_roles + labels = var.labels + lien_reason = var.lien_reason + oslogin = var.oslogin + oslogin_admins = var.oslogin_admins + oslogin_users = var.oslogin_users + parent = var.parent + prefix = var.prefix + services = var.services } diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf index a159af39a7..bcf7da90cd 100644 --- a/tests/modules/project/fixture/variables.tf +++ b/tests/modules/project/fixture/variables.tf @@ -34,12 +34,12 @@ variable "iam_roles" { default = [] } -variable "iam_nonauth_members" { +variable "iam_additive_members" { type = map(list(string)) default = {} } -variable "iam_nonauth_roles" { +variable "iam_additive_roles" { type = list(string) default = [] }