diff --git a/foundations/business-units/README.md b/foundations/business-units/README.md
index 70c2297cb5..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")
@@ -35,19 +35,12 @@ This sample uses a top-level folder to encapsulate projects that host resources
| 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_filter* | Audit log filter used for the log sink. | string
| | ...
|
-| *business_unit_bi* | Business unit BI configuration. | object({...})
| | ...
|
-| *business_unit_ml* | Business unit ML configuration. | object({...})
| | ...
|
-| *environments* | Environment short names. | map(string)
| | ...
|
+| *environments* | Environment short names. | map(string)
| | ...
|
| *gcs_defaults* | Defaults use for the state GCS buckets. | map(string)
| | ...
|
-| *generate_service_account_keys* | Generate and store service account keys in the state file. | bool
| | false
|
| *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_shared_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({...})
| | ...
|
| *project_services* | Service APIs enabled by default in new projects. | list(string)
| | ...
|
-| *service_account_keys* | Generate and store service account keys in the state file. | bool
| | false
|
-| *terraform_owners* | Terraform project owners, in IAM format. | list(string)
| | []
|
## Outputs
@@ -55,11 +48,11 @@ This sample uses a top-level folder to encapsulate projects that host resources
|---|---|:---:|
| audit_logs_project | Project that holds the audit logs export resources. | |
| bootstrap_tf_gcs_bucket | GCS bucket used for the bootstrap Terraform state. | |
-| bu_bi | Business Unit BI attributes. | |
-| bu_bi_sa_keys | Business Unit BI Service Accoutns keys. | ✓ |
-| bu_ml | Business Unit ML attributes. | |
-| bu_ml_sa_keys | Business Unit ML Service Accoutns keys. | ✓ |
-| 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 ec342cfd3a..4dbc4d2601 100644
--- a/foundations/business-units/main.tf
+++ b/foundations/business-units/main.tf
@@ -14,26 +14,18 @@
* limitations under the License.
*/
-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}"
-}
-
###############################################################################
-# Shared resources folder #
+# Terraform top-level resources #
###############################################################################
+# Shared folder
+
module "shared-folder" {
source = "../../modules/folders"
parent = var.root_node
names = ["shared"]
}
-###############################################################################
-# Terraform top-level resources #
-###############################################################################
-
# Terraform project
module "tf-project" {
@@ -58,49 +50,37 @@ module "tf-gcs-bootstrap" {
}
###############################################################################
-# Business units #
+# Business units #
###############################################################################
-# Business unit BI
-
-module "busines-unit-bi" {
- source = "../../modules/folders-unit"
-
- name = var.business_unit_bi.name
- short_name = var.business_unit_bi.short_name
+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
- iam_roles = var.business_unit_bi.iam_roles
- iam_members = var.business_unit_bi.iam_members
- iam_xpn_config = var.iam_xpn_config
- iam_billing_config = var.iam_billing_config
organization_id = var.organization_id
root_node = var.root_node
- prefix = var.prefix
- environments = var.environments
- service_account_keys = var.service_account_keys
+ # 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 ML
-
-module "busines-unit-ml" {
- source = "../../modules/folders-unit"
-
- name = var.business_unit_ml.name
- short_name = var.business_unit_ml.short_name
+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
- iam_roles = var.business_unit_ml.iam_roles
- iam_members = var.business_unit_ml.iam_members
- iam_xpn_config = var.iam_xpn_config
- iam_billing_config = var.iam_billing_config
organization_id = var.organization_id
root_node = var.root_node
- prefix = var.prefix
- environments = var.environments
- service_account_keys = var.service_account_keys
+ # 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.
}
###############################################################################
diff --git a/foundations/business-units/outputs.tf b/foundations/business-units/outputs.tf
index e2e16a4b12..51d5c21343 100644
--- a/foundations/business-units/outputs.tf
+++ b/foundations/business-units/outputs.tf
@@ -25,40 +25,40 @@ output "bootstrap_tf_gcs_bucket" {
}
output "shared_folder_id" {
- description = "Shared folder ID."
+ description = "Shared folder id."
value = module.shared-folder.id
}
-output "bu_ml" {
- description = "Business Unit ML attributes."
+output "bu_machine_learning" {
+ description = "Machine Learning attributes."
value = {
- unit_folder = module.busines-unit-ml.unit_folder,
- env_gcs_buckets = module.busines-unit-ml.env_gcs_buckets
- env_folders = module.busines-unit-ml.env_folders
- env_service_accounts = module.busines-unit-ml.env_service_accounts
+ 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 "bu_ml_sa_keys" {
- description = "Business Unit ML Service Accoutns keys."
+output "bu_machine_learning_keys" {
+ description = "Machine Learning service account keys."
sensitive = true
- value = module.busines-unit-ml.env_sa_keys
+ value = module.bu-machine-learning.env_sa_keys
}
-output "bu_bi" {
- description = "Business Unit BI attributes."
+output "bu_business_intelligence" {
+ description = "Business Intelligence attributes."
value = {
- unit_folder = module.busines-unit-bi.unit_folder,
- env_gcs_buckets = module.busines-unit-bi.env_gcs_buckets
- env_folders = module.busines-unit-bi.env_folders
- env_service_accounts = module.busines-unit-bi.env_service_accounts
+ 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 "bu_bi_sa_keys" {
- description = "Business Unit BI Service Accoutns keys."
+output "bu_business_intelligence_keys" {
+ description = "Business Intelligence service account keys."
sensitive = true
- value = module.busines-unit-bi.env_sa_keys
+ value = module.bu-business-intelligence.env_sa_keys
}
output "audit_logs_project" {
diff --git a/foundations/business-units/terraform.tfvars.sample b/foundations/business-units/terraform.tfvars.sample
index 2d05f977ae..5545bf3f57 100644
--- a/foundations/business-units/terraform.tfvars.sample
+++ 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.
diff --git a/foundations/business-units/variables.tf b/foundations/business-units/variables.tf
index 0a3e3e06d4..cf86ab424e 100644
--- a/foundations/business-units/variables.tf
+++ b/foundations/business-units/variables.tf
@@ -24,64 +24,11 @@ variable "audit_filter" {
END
}
-variable "iam_audit_viewers" {
- description = "Audit project viewers, in IAM format."
- type = list(string)
- default = []
-}
-
variable "billing_account_id" {
description = "Billing account id used as default for new projects."
type = string
}
-variable "business_unit_bi" {
- description = "Business unit BI configuration."
- type = object({
- name = string
- short_name = string
- iam_roles = list(string)
- iam_members = map(list(string))
- environment_iam_roles = list(string)
- })
- default = {
- name = "Business Intelligence",
- short_name = "bi"
- iam_roles = [],
- iam_members = {},
- environment_iam_roles = [
- "roles/compute.networkAdmin",
- "roles/owner",
- "roles/resourcemanager.folderAdmin",
- "roles/resourcemanager.projectCreator",
- ]
- }
-}
-
-variable "business_unit_ml" {
- description = "Business unit ML configuration."
- type = object({
- name = string
- short_name = string
- iam_roles = list(string)
- iam_members = map(list(string))
- environment_iam_roles = list(string)
- })
- default = {
- name = "Machine Learning",
- short_name = "ml"
- iam_roles = [],
- iam_members = {},
- environment_iam_roles = [
- "roles/compute.networkAdmin",
- "roles/owner",
- "roles/resourcemanager.folderAdmin",
- "roles/resourcemanager.projectCreator",
- ]
- }
-}
-
-
variable "environments" {
description = "Environment short names."
type = map(string)
@@ -92,12 +39,6 @@ variable "environments" {
}
}
-variable "generate_service_account_keys" {
- description = "Generate and store service account keys in the state file."
- type = bool
- default = false
-}
-
variable "gcs_defaults" {
description = "Defaults use for the state GCS buckets."
type = map(string)
@@ -107,19 +48,10 @@ variable "gcs_defaults" {
}
}
-variable "organization_id" {
- description = "Organization id."
- type = string
-}
-
-variable "prefix" {
- description = "Prefix used for resources that need unique names."
- type = string
-}
-
-variable "root_node" {
- description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'."
- type = string
+variable "iam_audit_viewers" {
+ description = "Audit project viewers, in IAM format."
+ type = list(string)
+ default = []
}
variable "iam_shared_owners" {
@@ -128,12 +60,22 @@ variable "iam_shared_owners" {
default = []
}
-variable "terraform_owners" {
+variable "iam_terraform_owners" {
description = "Terraform project owners, in IAM format."
type = list(string)
default = []
}
+variable "organization_id" {
+ description = "Organization id."
+ type = string
+}
+
+variable "prefix" {
+ description = "Prefix used for resources that need unique names."
+ type = string
+}
+
variable "project_services" {
description = "Service APIs enabled by default in new projects."
type = list(string)
@@ -142,39 +84,7 @@ variable "project_services" {
"stackdriver.googleapis.com",
]
}
-
-variable "iam_terraform_owners" {
- description = "Terraform project owners, in IAM format."
- type = list(string)
- default = []
-}
-
-variable "service_account_keys" {
- description = "Generate and store service account keys in the state file."
- type = bool
- default = false
-}
-
-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 = bool
- })
- default = {
- grant = true
- target_org = false
- }
-}
-
-variable "iam_billing_config" {
- description = "Control granting billing user role to service accounts. Target the billing account by default."
- type = object({
- grant = bool
- target_org = bool
- })
- default = {
- grant = true
- target_org = false
- }
+variable "root_node" {
+ description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'."
+ type = string
}
diff --git a/modules/folders-unit/README.md b/modules/folders-unit/README.md
index 2524653884..fafc4c0fd6 100644
--- a/modules/folders-unit/README.md
+++ b/modules/folders-unit/README.md
@@ -1,13 +1,12 @@
# Google Cloud Unit Folders Module
-This module allow creation and management of a set of folders (environments) and a common parent folder (unit), their environment specific Service Accounts, IAM bindings, GCS buckets.
+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"
-
+ source = "./modules/folders-unit"
name = "Business Intelligence"
short_name = "bi"
automation_project_id = "automation-project-394yr923811"
@@ -31,18 +30,18 @@ module "folders-unit" {
|---|---|:---: |:---:|:---:|
| automation_project_id | Project id used for automation service accounts. | string
| ✓ | |
| billing_account_id | Country billing account account. | string
| ✓ | |
-| iam_members | IAM members for roles applied on the unit folder. | map(list(string))
| ✓ | |
-| iam_roles | IAM roles applied on the unit folder. | list(string)
| ✓ | |
| name | Top folder name. | string
| ✓ | |
| organization_id | Organization id. | string
| ✓ | |
-| prefix | Prefix used for GCS bucket names. | string
| ✓ | |
| root_node | Root node in folders/folder_id or organizations/org_id format. | string
| ✓ | |
-| short_name | Short name. | string
| ✓ | |
-| *environments* | Unit environments short names. | map(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* | Control granting billing user role to service accounts. Target the billing account by default. | object({...})
| | ...
|
-| *iam_enviroment_roles* | IAM roles granted to service accounts on the environment sub-folders. | list(string)
| | ...
|
-| *iam_xpn_config* | Control granting Shared VPC creation roles to service accounts. Target the root node by default. | object({...})
| | ...
|
+| *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
diff --git a/modules/folders-unit/locals.tf b/modules/folders-unit/locals.tf
index 4bc6bb1c54..1ad80700b9 100644
--- a/modules/folders-unit/locals.tf
+++ b/modules/folders-unit/locals.tf
@@ -16,9 +16,11 @@
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 var.iam_roles :
- role => lookup(var.iam_members, role, [])
+ 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) :
diff --git a/modules/folders-unit/main.tf b/modules/folders-unit/main.tf
index b553b11ab6..dfc7830600 100644
--- a/modules/folders-unit/main.tf
+++ b/modules/folders-unit/main.tf
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-################################################################################
-# Folders and folder IAM #
-################################################################################
+###############################################################################
+# Folders and folder IAM #
+###############################################################################
resource "google_folder" "unit" {
display_name = var.name
@@ -40,27 +40,25 @@ 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 = [
- "serviceAccount:${google_service_account.environment[each.value.environment].email}"
- ]
+ members = [local.service_accounts[each.value.environment]]
}
-################################################################################
-# Billing Account and Org level IAM Bindings #
-################################################################################
+###############################################################################
+# 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 = "serviceAccount:${google_service_account.environment[each.value.environment].email}"
+ 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 = "serviceAccount:${google_service_account.environment[each.value.environment].email}"
+ member = local.service_accounts[each.value.environment]
}
################################################################################
@@ -84,9 +82,12 @@ resource "google_service_account_key" "keys" {
################################################################################
resource "google_storage_bucket" "tfstate" {
- for_each = var.environments
- project = var.automation_project_id
- name = "${var.prefix}-${var.short_name}-${each.key}-tf"
+ 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
diff --git a/modules/folders-unit/variables.tf b/modules/folders-unit/variables.tf
index 9b825237c1..7b1e37e9db 100644
--- a/modules/folders-unit/variables.tf
+++ b/modules/folders-unit/variables.tf
@@ -14,18 +14,13 @@
* limitations under the License.
*/
-variable "organization_id" {
- description = "Organization id."
- type = string
-}
-
-variable "root_node" {
- description = "Root node in folders/folder_id or organizations/org_id format."
+variable "automation_project_id" {
+ description = "Project id used for automation service accounts."
type = string
}
-variable "prefix" {
- description = "Prefix used for GCS bucket names."
+variable "billing_account_id" {
+ description = "Country billing account account."
type = string
}
@@ -33,32 +28,11 @@ variable "environments" {
description = "Unit environments short names."
type = map(string)
default = {
- dev = "development",
- test = "Testing",
- prod = "Production"
+ non-prod = "Non production"
+ prod = "Production"
}
}
-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 "name" {
- description = "Top folder name."
- type = string
-}
-
-variable "short_name" {
- description = "Short name."
- type = string
-}
-
variable "gcs_defaults" {
description = "Defaults use for the state GCS buckets."
type = map(string)
@@ -68,18 +42,20 @@ variable "gcs_defaults" {
}
}
-variable "iam_roles" {
- description = "IAM roles applied on the unit folder."
- type = list(string)
-}
-
-variable "iam_members" {
- description = "IAM members for roles applied on the unit folder."
- type = map(list(string))
+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 service accounts on the environment sub-folders."
+ description = "IAM roles granted to the environment service account on the environment sub-folder."
type = list(string)
default = [
"roles/compute.networkAdmin",
@@ -89,14 +65,20 @@ variable "iam_enviroment_roles" {
]
}
-variable "service_account_keys" {
- description = "Generate and store service account keys in the state file."
- type = bool
- default = false
+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 = "Control granting Shared VPC creation roles to service accounts. Target the root node by default."
+ description = "Grant Shared VPC creation roles to service accounts, defaults to granting at folder level."
type = object({
grant = bool
target_org = bool
@@ -107,14 +89,34 @@ variable "iam_xpn_config" {
}
}
-variable "iam_billing_config" {
- description = "Control granting billing user role to service accounts. Target the billing account by default."
- 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
}