diff --git a/0-bootstrap/README-GitHub.md b/0-bootstrap/README-GitHub.md index 3f9c133ca..6b25c1abf 100644 --- a/0-bootstrap/README-GitHub.md +++ b/0-bootstrap/README-GitHub.md @@ -37,19 +37,12 @@ Also make sure that you have the following: - A Google Cloud [organization](https://cloud.google.com/resource-manager/docs/creating-managing-organization). - A Google Cloud [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account). - Cloud Identity or Google Workspace groups for organization and billing admins. -- Add the Identity (user or Service Account) who will run Terraform to the `group_org_admins` group. -They must be in this group, or they won't have `roles/resourcemanager.projectCreator` access. -- For the Identity who will run the procedures in this document, grant the following roles: - - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. - - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. - - The `roles/billing.admin` role on the billing account. - - The `roles/resourcemanager.folderCreator` role. - -If other users need to be able to run these procedures, add them to the group -represented by the `org_project_creators` variable. -For more information about the permissions that are required, and the resources -that are created, see the organization bootstrap module -[documentation.](https://github.com/terraform-google-modules/terraform-google-bootstrap) +- For the user who will run the procedures in this document, grant the following roles: + - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. + - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. + - The `roles/resourcemanager.projectCreator` role on the Google Cloud organization. + - The `roles/billing.admin` role on the billing account. + - The `roles/resourcemanager.folderCreator` role. ## Instructions diff --git a/0-bootstrap/README-GitLab.md b/0-bootstrap/README-GitLab.md index f9d9a1667..bccc35ba3 100644 --- a/0-bootstrap/README-GitLab.md +++ b/0-bootstrap/README-GitLab.md @@ -37,19 +37,12 @@ Also make sure that you have the following: - A Google Cloud [organization](https://cloud.google.com/resource-manager/docs/creating-managing-organization). - A Google Cloud [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account). - Cloud Identity or Google Workspace groups for organization and billing admins. -- Add the Identity (user or Service Account) who will run Terraform to the `group_org_admins` group. -They must be in this group, or they won't have `roles/resourcemanager.projectCreator` access. -- For the Identity who will run the procedures in this document, grant the following roles: - - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. - - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. - - The `roles/billing.admin` role on the billing account. - - The `roles/resourcemanager.folderCreator` role. - -If other users need to be able to run these procedures, add them to the group -represented by the `org_project_creators` variable. -For more information about the permissions that are required, and the resources -that are created, see the organization bootstrap module -[documentation.](https://github.com/terraform-google-modules/terraform-google-bootstrap) +- For the user who will run the procedures in this document, grant the following roles: + - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. + - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. + - The `roles/resourcemanager.projectCreator` role on the Google Cloud organization. + - The `roles/billing.admin` role on the billing account. + - The `roles/resourcemanager.folderCreator` role. ## Instructions diff --git a/0-bootstrap/README-Terraform-Cloud.md b/0-bootstrap/README-Terraform-Cloud.md index 83c3f8261..43c53f4b0 100644 --- a/0-bootstrap/README-Terraform-Cloud.md +++ b/0-bootstrap/README-Terraform-Cloud.md @@ -43,19 +43,12 @@ Also make sure that you have the following: - A Google Cloud [organization](https://cloud.google.com/resource-manager/docs/creating-managing-organization). - A Google Cloud [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account). - Cloud Identity or Google Workspace groups for organization and billing admins. -- Add the Identity (user or Service Account) who will run Terraform to the `group_org_admins` group. -They must be in this group, or they won't have `roles/resourcemanager.projectCreator` access. -- For the Identity who will run the procedures in this document, grant the following roles: - - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. - - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. - - The `roles/billing.admin` role on the billing account. - - The `roles/resourcemanager.folderCreator` role. - -If other users need to be able to run these procedures, add them to the group -represented by the `org_project_creators` variable. -For more information about the permissions that are required, and the resources -that are created, see the organization bootstrap module -[documentation.](https://github.com/terraform-google-modules/terraform-google-bootstrap) +- For the user who will run the procedures in this document, grant the following roles: + - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. + - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. + - The `roles/resourcemanager.projectCreator` role on the Google Cloud organization. + - The `roles/billing.admin` role on the billing account. + - The `roles/resourcemanager.folderCreator` role. ### Instructions diff --git a/0-bootstrap/README.md b/0-bootstrap/README.md index 29481b1ea..5cae19a76 100644 --- a/0-bootstrap/README.md +++ b/0-bootstrap/README.md @@ -75,37 +75,26 @@ Also make sure that you've done the following: [organization](https://cloud.google.com/resource-manager/docs/creating-managing-organization). 1. Set up a Google Cloud [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account). -1. Create Cloud Identity or Google Workspace groups for - organization and billing admins. -1. Add the user who will use Terraform to the `group_org_admins` group. - They must be in this group, or they won't have - `roles/resourcemanager.projectCreator` access. +1. Create Cloud Identity or Google Workspace groups as defined in [groups for access control](https://cloud.google.com/architecture/security-foundations/authentication-authorization#groups_for_access_control). +Set the variables in **terraform.tfvars** (`groups` block) to use the specific group names you create. 1. For the user who will run the procedures in this document, grant the following roles: - The `roles/resourcemanager.organizationAdmin` role on the Google Cloud organization. - The `roles/orgpolicy.policyAdmin` role on the Google Cloud organization. + - The `roles/resourcemanager.projectCreator` role on the Google Cloud organization. - The `roles/billing.admin` role on the billing account. - The `roles/resourcemanager.folderCreator` role. -If other users need to be able to run these procedures, add them to the group -represented by the `org_project_creators` variable. -For more information about the permissions that are required, and the resources -that are created, see the organization bootstrap module -[documentation.](https://github.com/terraform-google-modules/terraform-google-bootstrap) - ### Optional - Automatic creation of Google Cloud Identity groups In the foundation, Google Cloud Identity groups are used for [authentication and access management](https://cloud.google.com/architecture/security-foundations/authentication-authorization) . -To enable automatic creation of the [required groups](https://cloud.google.com/architecture/security-foundations/authentication-authorization#users_and_groups), complete the following actions: +To enable automatic creation of the [groups](https://cloud.google.com/architecture/security-foundations/authentication-authorization#groups_for_access_control), complete the following actions: - Have an existing project for Cloud Identity API billing. - Enable the Cloud Identity API (`cloudidentity.googleapis.com`) on the billing project. - Grant role `roles/serviceusage.serviceUsageConsumer` to the user running Terraform on the billing project. -- Provide values for the groups and billing project in the variable `groups`. - -All groups in the `groups.required_groups` are required. - -All groups in the `groups.optional_groups` are optional. +- Change the field `groups.create_required_groups` to **true** to create the required groups. +- Change the field `groups.create_optional_groups` to **true** and fill the `groups.optional_groups` with the emails to be created. ### Optional - Cloud Build access to on-prem @@ -305,13 +294,10 @@ Each step has instructions for this change. | bucket\_tfstate\_kms\_force\_destroy | When deleting a bucket, this boolean option will delete the KMS keys used for the Terraform state bucket. | `bool` | `false` | no | | default\_region | Default region to create resources where applicable. | `string` | `"us-central1"` | no | | folder\_prefix | Name prefix to use for folders created. Should be the same in all steps. | `string` | `"fldr"` | no | -| group\_billing\_admins | Google Group for GCP Billing Administrators | `string` | n/a | yes | -| group\_org\_admins | Google Group for GCP Organization Administrators | `string` | n/a | yes | -| groups | Contain the details of the Groups to be created. |
object({
create_groups = bool
billing_project = string
required_groups = object({
group_org_admins = string
group_billing_admins = string
billing_data_users = string
audit_data_users = string
monitoring_workspace_users = string
})
optional_groups = object({
gcp_platform_viewer = string
gcp_security_reviewer = string
gcp_network_viewer = string
gcp_scc_admin = string
gcp_global_secrets_admin = string
gcp_audit_viewer = string
})
})
|
{
"billing_project": null,
"create_groups": false,
"optional_groups": {
"gcp_audit_viewer": "",
"gcp_global_secrets_admin": "",
"gcp_network_viewer": "",
"gcp_platform_viewer": "",
"gcp_scc_admin": "",
"gcp_security_reviewer": ""
},
"required_groups": {
"audit_data_users": "",
"billing_data_users": "",
"group_billing_admins": "",
"group_org_admins": "",
"monitoring_workspace_users": ""
}
}
| no | +| groups | Contain the details of the Groups to be created. |
object({
create_required_groups = optional(bool, false)
create_optional_groups = optional(bool, false)
billing_project = optional(string, null)
required_groups = object({
group_org_admins = string
group_billing_admins = string
billing_data_users = string
audit_data_users = string
monitoring_workspace_users = string
})
optional_groups = optional(object({
gcp_security_reviewer = optional(string, "")
gcp_network_viewer = optional(string, "")
gcp_scc_admin = optional(string, "")
gcp_global_secrets_admin = optional(string, "")
gcp_kms_admin = optional(string, "")
}), {})
})
| n/a | yes | | initial\_group\_config | Define the group configuration when it is initialized. Valid values are: WITH\_INITIAL\_OWNER, EMPTY and INITIAL\_GROUP\_CONFIG\_UNSPECIFIED. | `string` | `"WITH_INITIAL_OWNER"` | no | | org\_id | GCP Organization ID | `string` | n/a | yes | | org\_policy\_admin\_role | Additional Org Policy Admin role for admin group. You can use this for testing purposes. | `bool` | `false` | no | -| org\_project\_creators | Additional list of members to have project creator role across the organization. Prefix of group: user: or serviceAccount: is required. | `list(string)` | `[]` | no | | parent\_folder | Optional - for an organization with existing projects or for development/validation. It will place all the example foundation resources under the provided folder instead of the root organization. The value is the numeric folder ID. The folder must already exist. | `string` | `""` | no | | project\_prefix | Name prefix to use for projects created. Should be the same in all steps. Max size is 3 characters. | `string` | `"prj"` | no | @@ -332,8 +318,6 @@ Each step has instructions for this change. | gcs\_bucket\_cloudbuild\_artifacts | Bucket used to store Cloud Build artifacts in cicd project. | | gcs\_bucket\_cloudbuild\_logs | Bucket used to store Cloud Build logs in cicd project. | | gcs\_bucket\_tfstate | Bucket used for storing terraform state for Foundations Pipelines in Seed Project. | -| group\_billing\_admins | Google Group for GCP Billing Administrators. | -| group\_org\_admins | Google Group for GCP Organization Administrators. | | networks\_step\_terraform\_service\_account\_email | Networks Step Terraform Account | | optional\_groups | List of Google Groups created that are optional to the Example Foundation steps. | | organization\_step\_terraform\_service\_account\_email | Organization Step Terraform Account | diff --git a/0-bootstrap/cb.tf b/0-bootstrap/cb.tf index 827f0d2a0..2b9829654 100644 --- a/0-bootstrap/cb.tf +++ b/0-bootstrap/cb.tf @@ -93,7 +93,7 @@ module "tf_source" { project_id = "${var.project_prefix}-b-cicd-${random_string.suffix.result}" location = var.default_region billing_account = var.billing_account - group_org_admins = local.group_org_admins + group_org_admins = var.groups.required_groups.group_org_admins buckets_force_destroy = var.bucket_force_destroy activate_apis = [ diff --git a/0-bootstrap/groups.tf b/0-bootstrap/groups.tf index 8254c6aca..6f9a8d160 100644 --- a/0-bootstrap/groups.tf +++ b/0-bootstrap/groups.tf @@ -17,18 +17,18 @@ #Groups creation resources locals { - optional_groups_to_create = { - for key, value in var.groups.optional_groups : key => value - if value != "" && var.groups.create_groups == true - } required_groups_to_create = { for key, value in var.groups.required_groups : key => value - if var.groups.create_groups == true + if var.groups.create_required_groups == true + } + optional_groups_to_create = { + for key, value in var.groups.optional_groups : key => value + if value != "" && var.groups.create_optional_groups == true } } data "google_organization" "org" { - count = var.groups.create_groups ? 1 : 0 + count = var.groups.create_required_groups || var.groups.create_optional_groups ? 1 : 0 organization = var.org_id } diff --git a/0-bootstrap/main.tf b/0-bootstrap/main.tf index 0ed502ed4..353908f45 100644 --- a/0-bootstrap/main.tf +++ b/0-bootstrap/main.tf @@ -28,13 +28,10 @@ locals { "serviceAccount:${google_service_account.terraform-env-sa["net"].email}", "serviceAccount:${google_service_account.terraform-env-sa["proj"].email}", ] - org_project_creators = distinct(concat(var.org_project_creators, local.step_terraform_sa)) - parent = var.parent_folder != "" ? "folders/${var.parent_folder}" : "organizations/${var.org_id}" + parent = var.parent_folder != "" ? "folders/${var.parent_folder}" : "organizations/${var.org_id}" org_admins_org_iam_permissions = var.org_policy_admin_role == true ? [ "roles/orgpolicy.policyAdmin", "roles/resourcemanager.organizationAdmin", "roles/billing.user" ] : ["roles/resourcemanager.organizationAdmin", "roles/billing.user"] - group_org_admins = var.groups.create_groups ? module.required_group["group_org_admins"].id : var.group_org_admins - group_billing_admins = var.groups.create_groups ? module.required_group["group_billing_admins"].id : var.group_billing_admins } resource "google_folder" "bootstrap" { @@ -52,10 +49,10 @@ module "seed_bootstrap" { state_bucket_name = "${var.bucket_prefix}-${var.project_prefix}-b-seed-tfstate" force_destroy = var.bucket_force_destroy billing_account = var.billing_account - group_org_admins = local.group_org_admins - group_billing_admins = local.group_billing_admins + group_org_admins = var.groups.required_groups.group_org_admins + group_billing_admins = var.groups.required_groups.group_billing_admins default_region = var.default_region - org_project_creators = local.org_project_creators + org_project_creators = local.step_terraform_sa sa_enable_impersonation = true create_terraform_sa = false parent_folder = var.parent_folder == "" ? "" : local.parent diff --git a/0-bootstrap/outputs.tf b/0-bootstrap/outputs.tf index fa3d51636..3b33ebe17 100644 --- a/0-bootstrap/outputs.tf +++ b/0-bootstrap/outputs.tf @@ -63,24 +63,14 @@ output "common_config" { } } -output "group_org_admins" { - description = "Google Group for GCP Organization Administrators." - value = var.groups.create_groups == true ? module.required_group["group_org_admins"].id : var.group_org_admins -} - -output "group_billing_admins" { - description = "Google Group for GCP Billing Administrators." - value = var.groups.create_groups == true ? module.required_group["group_billing_admins"].id : var.group_billing_admins -} - output "required_groups" { description = "List of Google Groups created that are required by the Example Foundation steps." - value = var.groups.create_groups == true ? module.required_group : {} + value = var.groups.create_required_groups == true ? module.required_group : tomap(var.groups.required_groups) } output "optional_groups" { description = "List of Google Groups created that are optional to the Example Foundation steps." - value = var.groups.create_groups == true ? module.optional_group : {} + value = var.groups.create_optional_groups == true ? module.optional_group : tomap(var.groups.optional_groups) } /* ---------------------------------------- diff --git a/0-bootstrap/sa.tf b/0-bootstrap/sa.tf index 205995603..e0ed0da54 100644 --- a/0-bootstrap/sa.tf +++ b/0-bootstrap/sa.tf @@ -227,3 +227,10 @@ resource "google_billing_account_iam_member" "billing_admin_user" { google_billing_account_iam_member.tf_billing_user ] } + +resource "google_billing_account_iam_member" "billing_account_sink" { + billing_account_id = var.billing_account + role = "roles/logging.configWriter" + member = "serviceAccount:${google_service_account.terraform-env-sa["org"].email}" +} + diff --git a/0-bootstrap/terraform.example.tfvars b/0-bootstrap/terraform.example.tfvars index 67bea0614..0ef591ca4 100644 --- a/0-bootstrap/terraform.example.tfvars +++ b/0-bootstrap/terraform.example.tfvars @@ -18,13 +18,27 @@ org_id = "REPLACE_ME" # format "000000000000" billing_account = "REPLACE_ME" # format "000000-000000-000000" -group_org_admins = "REPLACE_ME" - -group_billing_admins = "REPLACE_ME" - -# Example of values for the groups -# group_org_admins = "gcp-organization-admins@example.com" -# group_billing_admins = "gcp-billing-admins@example.com" +// For enabling the automatic groups creation, uncoment the +// variables and update the values with the group names +groups = { + # create_required_groups = false # Change to true to create the required_groups + # create_optional_groups = false # Change to true to create the optional_groups + # billing_project = "REPLACE_ME" # Fill to create required or optional groups + required_groups = { + group_org_admins = "REPLACE_ME" # example "gcp-organization-admins@example.com" + group_billing_admins = "REPLACE_ME" # example "gcp-billing-admins@example.com" + } + # optional_groups = { + # billing_data_users = "" #"billing_data_users_local_test@example.com" + # audit_data_users = "" #"audit_data_users_local_test@example.com" + # monitoring_workspace_users = "" #"monitoring_workspace_users_local_test@example.com" + # gcp_security_reviewer = "" #"gcp_security_reviewer_local_test@example.com" + # gcp_network_viewer = "" #"gcp_network_viewer_local_test@example.com" + # gcp_scc_admin = "" #"gcp_scc_admin_local_test@example.com" + # gcp_global_secrets_admin = "" #"gcp_global_secrets_admin_local_test@example.com" + # gcp_kms_admin = "" #"gcp_kms_admin_local_test@example.com" + # } +} default_region = "us-central1" @@ -35,29 +49,6 @@ default_region = "us-central1" # The folder must already exist. # parent_folder = "01234567890" -# Optional - for enabling the automatic groups creation, uncoment the groups -# variable and update the values with the desired group names -# groups = { -# create_groups = true, -# billing_project = "billing-project", -# required_groups = { -# group_org_admins = "group_org_admins_local_test@example.com" -# group_billing_admins = "group_billing_admins_local_test@example.com" -# billing_data_users = "billing_data_users_local_test@example.com" -# audit_data_users = "audit_data_users_local_test@example.com" -# monitoring_workspace_users = "monitoring_workspace_users_local_test@example.com" -# }, -# optional_groups = { -# gcp_platform_viewer = "gcp_platform_viewer_local_test@example.com" -# gcp_security_reviewer = "gcp_security_reviewer_local_test@example.com" -# gcp_network_viewer = "gcp_network_viewer_local_test@example.com" -# gcp_scc_admin = "gcp_scc_admin_local_test@example.com" -# gcp_global_secrets_admin = "gcp_global_secrets_admin_local_test@example.com" -# gcp_audit_viewer = "gcp_audit_viewer_local_test@example.com" -# } -# } - - /* ---------------------------------------- Specific to github_bootstrap diff --git a/0-bootstrap/variables.tf b/0-bootstrap/variables.tf index a1598a3d1..58e70f730 100644 --- a/0-bootstrap/variables.tf +++ b/0-bootstrap/variables.tf @@ -24,16 +24,6 @@ variable "billing_account" { type = string } -variable "group_org_admins" { - description = "Google Group for GCP Organization Administrators" - type = string -} - -variable "group_billing_admins" { - description = "Google Group for GCP Billing Administrators" - type = string -} - variable "default_region" { description = "Default region to create resources where applicable." type = string @@ -46,12 +36,6 @@ variable "parent_folder" { default = "" } -variable "org_project_creators" { - description = "Additional list of members to have project creator role across the organization. Prefix of group: user: or serviceAccount: is required." - type = list(string) - default = [] -} - variable "org_policy_admin_role" { description = "Additional Org Policy Admin role for admin group. You can use this for testing purposes." type = bool @@ -94,8 +78,9 @@ variable "bucket_tfstate_kms_force_destroy" { variable "groups" { description = "Contain the details of the Groups to be created." type = object({ - create_groups = bool - billing_project = string + create_required_groups = optional(bool, false) + create_optional_groups = optional(bool, false) + billing_project = optional(string, null) required_groups = object({ group_org_admins = string group_billing_admins = string @@ -103,65 +88,44 @@ variable "groups" { audit_data_users = string monitoring_workspace_users = string }) - optional_groups = object({ - gcp_platform_viewer = string - gcp_security_reviewer = string - gcp_network_viewer = string - gcp_scc_admin = string - gcp_global_secrets_admin = string - gcp_audit_viewer = string - }) + optional_groups = optional(object({ + gcp_security_reviewer = optional(string, "") + gcp_network_viewer = optional(string, "") + gcp_scc_admin = optional(string, "") + gcp_global_secrets_admin = optional(string, "") + gcp_kms_admin = optional(string, "") + }), {}) }) - default = { - create_groups = false - billing_project = null - required_groups = { - group_org_admins = "" - group_billing_admins = "" - billing_data_users = "" - audit_data_users = "" - monitoring_workspace_users = "" - } - optional_groups = { - gcp_platform_viewer = "" - gcp_security_reviewer = "" - gcp_network_viewer = "" - gcp_scc_admin = "" - gcp_global_secrets_admin = "" - gcp_audit_viewer = "" - } - } validation { - condition = var.groups.create_groups == true ? (var.groups.billing_project != "" ? true : false) : true + condition = var.groups.create_required_groups || var.groups.create_optional_groups ? (var.groups.billing_project != null ? true : false) : true error_message = "A billing_project must be passed to use the automatic group creation." } validation { - condition = var.groups.create_groups == true ? (var.groups.required_groups.group_org_admins != "" ? true : false) : true - error_message = "The group group_org_admins is invalid, it must be a valid email." + condition = var.groups.required_groups.group_org_admins != "" + error_message = "The group group_org_admins is invalid, it must be a valid email" } validation { - condition = var.groups.create_groups == true ? (var.groups.required_groups.group_billing_admins != "" ? true : false) : true - error_message = "The group group_billing_admins is invalid, it must be a valid email." + condition = var.groups.required_groups.group_billing_admins != "" + error_message = "The group group_billing_admins is invalid, it must be a valid email" } validation { - condition = var.groups.create_groups == true ? (var.groups.required_groups.billing_data_users != "" ? true : false) : true - error_message = "The group billing_data_users is invalid, it must be a valid email." + condition = var.groups.required_groups.billing_data_users != "" + error_message = "The group billing_data_users is invalid, it must be a valid email" } validation { - condition = var.groups.create_groups == true ? (var.groups.required_groups.audit_data_users != "" ? true : false) : true - error_message = "The group audit_data_users is invalid, it must be a valid email." + condition = var.groups.required_groups.audit_data_users != "" + error_message = "The group audit_data_users is invalid, it must be a valid email" } validation { - condition = var.groups.create_groups == true ? (var.groups.required_groups.monitoring_workspace_users != "" ? true : false) : true - error_message = "The group monitoring_workspace_users is invalid, it must be a valid email." + condition = var.groups.required_groups.monitoring_workspace_users != "" + error_message = "The group monitoring_workspace_users is invalid, it must be a valid email" } - } variable "initial_group_config" { diff --git a/1-org/envs/shared/README.md b/1-org/envs/shared/README.md index 3ccdf7739..0d8c22779 100644 --- a/1-org/envs/shared/README.md +++ b/1-org/envs/shared/README.md @@ -3,8 +3,6 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| audit\_data\_users | Google Workspace or Cloud Identity group that have access to audit logs. | `string` | n/a | yes | -| billing\_data\_users | Google Workspace or Cloud Identity group that have access to billing data set. | `string` | n/a | yes | | billing\_export\_dataset\_location | The location of the dataset for billing data export. | `string` | `"US"` | no | | cai\_monitoring\_kms\_force\_destroy | If set to true, delete KMS keyring and keys when destroying the module; otherwise, destroying the module will fail if KMS keys are present. | `bool` | `false` | no | | create\_access\_context\_manager\_access\_policy | Whether to create access context manager access policy. | `bool` | `true` | no | @@ -15,8 +13,7 @@ | enforce\_allowed\_worker\_pools | Whether to enforce the organization policy restriction on allowed worker pools for Cloud Build. | `bool` | `false` | no | | essential\_contacts\_domains\_to\_allow | The list of domains that email addresses added to Essential Contacts can have. | `list(string)` | n/a | yes | | essential\_contacts\_language | Essential Contacts preferred language for notifications, as a ISO 639-1 language code. See [Supported languages](https://cloud.google.com/resource-manager/docs/managing-notification-contacts#supported-languages) for a list of supported languages. | `string` | `"en"` | no | -| gcp\_groups | Groups to grant specific roles in the Organization.
platform\_viewer: Google Workspace or Cloud Identity group that have the ability to view resource information across the Google Cloud organization.
security\_reviewer: Google Workspace or Cloud Identity group that members are part of the security team responsible for reviewing cloud security
network\_viewer: Google Workspace or Cloud Identity group that members are part of the networking team and review network configurations.
scc\_admin: Google Workspace or Cloud Identity group that can administer Security Command Center.
audit\_viewer: Google Workspace or Cloud Identity group that members are part of an audit team and view audit logs in the logging project.
global\_secrets\_admin: Google Workspace or Cloud Identity group that members are responsible for putting secrets into Secrets Manage |
object({
platform_viewer = optional(string, null)
security_reviewer = optional(string, null)
network_viewer = optional(string, null)
scc_admin = optional(string, null)
audit_viewer = optional(string, null)
global_secrets_admin = optional(string, null)
})
| `{}` | no | -| gcp\_user | Users to grant specific roles in the Organization.
org\_admin: Identity that has organization administrator permissions.
billing\_creator: Identity that can create billing accounts.
billing\_admin: Identity that has billing administrator permissions. |
object({
org_admin = optional(string, null)
billing_creator = optional(string, null)
billing_admin = optional(string, null)
})
| `{}` | no | +| gcp\_groups | Groups to grant specific roles in the Organization.
platform\_viewer: Google Workspace or Cloud Identity group that have the ability to view resource information across the Google Cloud organization.
security\_reviewer: Google Workspace or Cloud Identity group that members are part of the security team responsible for reviewing cloud security
network\_viewer: Google Workspace or Cloud Identity group that members are part of the networking team and review network configurations.
scc\_admin: Google Workspace or Cloud Identity group that can administer Security Command Center.
audit\_viewer: Google Workspace or Cloud Identity group that members are part of an audit team and view audit logs in the logging project.
global\_secrets\_admin: Google Workspace or Cloud Identity group that members are responsible for putting secrets into Secrets Manage |
object({
audit_viewer = optional(string, null)
security_reviewer = optional(string, null)
network_viewer = optional(string, null)
scc_admin = optional(string, null)
global_secrets_admin = optional(string, null)
kms_admin = optional(string, null)
})
| `{}` | no | | log\_export\_storage\_force\_destroy | (Optional) If set to true, delete all contents when destroying the resource; otherwise, destroying the resource will fail if contents are present. | `bool` | `false` | no | | log\_export\_storage\_location | The location of the storage bucket used to export logs. | `string` | `"US"` | no | | log\_export\_storage\_retention\_policy | Configuration of the bucket's data retention policy for how long objects in the bucket should be retained. |
object({
is_locked = bool
retention_period_days = number
})
| `null` | no | @@ -32,6 +29,7 @@ | Name | Description | |------|-------------| | base\_net\_hub\_project\_id | The Base Network hub project ID | +| billing\_sink\_names | The name of the sinks under billing account level. | | cai\_monitoring\_artifact\_registry | CAI Monitoring Cloud Function Artifact Registry name. | | cai\_monitoring\_asset\_feed | CAI Monitoring Cloud Function Organization Asset Feed name. | | cai\_monitoring\_bucket | CAI Monitoring Cloud Function Source Bucket name. | diff --git a/1-org/envs/shared/essential_contacts.tf b/1-org/envs/shared/essential_contacts.tf index 2b43fc1a1..1aee708d4 100644 --- a/1-org/envs/shared/essential_contacts.tf +++ b/1-org/envs/shared/essential_contacts.tf @@ -15,19 +15,20 @@ */ locals { + group_org_admins = local.required_groups["group_org_admins"] + group_billing_admins = local.required_groups["group_billing_admins"] gcp_scc_admin = var.gcp_groups.scc_admin == null ? local.group_org_admins : var.gcp_groups.scc_admin - gcp_platform_viewer = var.gcp_groups.platform_viewer == null ? local.group_org_admins : var.gcp_groups.platform_viewer gcp_security_reviewer = var.gcp_groups.security_reviewer == null ? local.group_org_admins : var.gcp_groups.security_reviewer gcp_network_viewer = var.gcp_groups.network_viewer == null ? local.group_org_admins : var.gcp_groups.network_viewer # Notification categories details: https://cloud.google.com/resource-manager/docs/managing-notification-contacts#notification-categories categories_map = { - "BILLING" = setunion([local.group_billing_admins, var.billing_data_users]) - "LEGAL" = setunion([local.group_org_admins, var.audit_data_users]) - "PRODUCT_UPDATES" = setunion([local.gcp_scc_admin, local.gcp_platform_viewer]) + "BILLING" = setunion([local.group_billing_admins, local.required_groups["billing_data_users"]]) + "LEGAL" = setunion([local.group_org_admins, local.required_groups["audit_data_users"]]) + "PRODUCT_UPDATES" = [local.group_org_admins] "SECURITY" = setunion([local.gcp_scc_admin, local.gcp_security_reviewer]) "SUSPENSION" = [local.group_org_admins] - "TECHNICAL" = setunion([local.gcp_platform_viewer, local.gcp_security_reviewer, local.gcp_network_viewer]) + "TECHNICAL" = setunion([local.gcp_security_reviewer, local.gcp_network_viewer]) } # Convert a map indexed by category to a map indexed by email diff --git a/1-org/envs/shared/iam.tf b/1-org/envs/shared/iam.tf index d30c1c414..9398a4c98 100644 --- a/1-org/envs/shared/iam.tf +++ b/1-org/envs/shared/iam.tf @@ -64,16 +64,22 @@ resource "google_folder_iam_audit_config" "folder_config" { } } +resource "google_project_iam_member" "audit_log_logging_viewer" { + project = module.org_audit_logs.project_id + role = "roles/logging.viewer" + member = "group:${local.required_groups["audit_data_users"]}" +} + resource "google_project_iam_member" "audit_log_bq_user" { project = module.org_audit_logs.project_id role = "roles/bigquery.user" - member = "group:${var.audit_data_users}" + member = "group:${local.required_groups["audit_data_users"]}" } resource "google_project_iam_member" "audit_log_bq_data_viewer" { project = module.org_audit_logs.project_id role = "roles/bigquery.dataViewer" - member = "group:${var.audit_data_users}" + member = "group:${local.required_groups["audit_data_users"]}" } /****************************************** @@ -83,13 +89,13 @@ resource "google_project_iam_member" "audit_log_bq_data_viewer" { resource "google_project_iam_member" "billing_bq_user" { project = module.org_billing_logs.project_id role = "roles/bigquery.user" - member = "group:${var.billing_data_users}" + member = "group:${local.required_groups["billing_data_users"]}" } resource "google_project_iam_member" "billing_bq_viewer" { project = module.org_billing_logs.project_id role = "roles/bigquery.dataViewer" - member = "group:${var.billing_data_users}" + member = "group:${local.required_groups["billing_data_users"]}" } /****************************************** @@ -99,27 +105,13 @@ resource "google_project_iam_member" "billing_bq_viewer" { resource "google_organization_iam_member" "billing_viewer" { org_id = local.org_id role = "roles/billing.viewer" - member = "group:${var.billing_data_users}" + member = "group:${local.required_groups["billing_data_users"]}" } /****************************************** - Groups permissions according to SFB (Section 6.2 - Users and groups) - IAM + Groups permissions *****************************************/ -resource "google_organization_iam_member" "organization_viewer" { - count = var.gcp_groups.platform_viewer != null && local.parent_folder == "" ? 1 : 0 - org_id = local.org_id - role = "roles/viewer" - member = "group:${var.gcp_groups.platform_viewer}" -} - -resource "google_folder_iam_member" "organization_viewer" { - count = var.gcp_groups.platform_viewer != null && local.parent_folder != "" ? 1 : 0 - folder = "folders/${local.parent_folder}" - role = "roles/viewer" - member = "group:${var.gcp_groups.platform_viewer}" -} - resource "google_organization_iam_member" "security_reviewer" { count = var.gcp_groups.security_reviewer != null && local.parent_folder == "" ? 1 : 0 org_id = local.org_id @@ -169,7 +161,14 @@ resource "google_project_iam_member" "audit_bq_data_viewer" { member = "group:${var.gcp_groups.audit_viewer}" } -resource "google_project_iam_member" "scc_admin" { +resource "google_organization_iam_member" "org_scc_admin" { + count = var.gcp_groups.scc_admin != null && local.parent_folder == "" ? 1 : 0 + org_id = local.org_id + role = "roles/securitycenter.adminEditor" + member = "group:${var.gcp_groups.scc_admin}" +} + +resource "google_project_iam_member" "project_scc_admin" { count = var.gcp_groups.scc_admin != null ? 1 : 0 project = module.scc_notifications.project_id role = "roles/securitycenter.adminEditor" @@ -183,34 +182,9 @@ resource "google_project_iam_member" "global_secrets_admin" { member = "group:${var.gcp_groups.global_secrets_admin}" } -/****************************************** - Privileged accounts permissions according to SFB (Section 6.3 - Privileged identities) -*****************************************/ - -resource "google_organization_iam_member" "org_admin_user" { - count = var.gcp_user.org_admin != null && local.parent_folder == "" ? 1 : 0 - org_id = local.org_id - role = "roles/resourcemanager.organizationAdmin" - member = "user:${var.gcp_user.org_admin}" -} - -resource "google_folder_iam_member" "org_admin_user" { - count = var.gcp_user.org_admin != null && local.parent_folder != "" ? 1 : 0 - folder = "folders/${local.parent_folder}" - role = "roles/resourcemanager.folderAdmin" - member = "user:${var.gcp_user.org_admin}" -} - -resource "google_organization_iam_member" "billing_creator_user" { - count = var.gcp_user.billing_creator != null && local.parent_folder == "" ? 1 : 0 - org_id = local.org_id - role = "roles/billing.creator" - member = "user:${var.gcp_user.billing_creator}" -} - -resource "google_billing_account_iam_member" "billing_admin_user" { - count = var.gcp_user.billing_admin != null ? 1 : 0 - billing_account_id = local.billing_account - role = "roles/billing.admin" - member = "user:${var.gcp_user.billing_admin}" +resource "google_project_iam_member" "kms_admin" { + count = var.gcp_groups.kms_admin != null ? 1 : 0 + project = module.org_kms.project_id + role = "roles/cloudkms.viewer" + member = "group:${var.gcp_groups.kms_admin}" } diff --git a/1-org/envs/shared/log_sinks.tf b/1-org/envs/shared/log_sinks.tf index a884e6db2..88c5e51f4 100644 --- a/1-org/envs/shared/log_sinks.tf +++ b/1-org/envs/shared/log_sinks.tf @@ -42,6 +42,9 @@ module "logs_export" { resources = local.parent_resources resource_type = local.parent_resource_type logging_destination_project_id = module.org_audit_logs.project_id + billing_account = local.billing_account + enable_billing_account_sink = true + /****************************************** Send logs to Storage diff --git a/1-org/envs/shared/org_policy.tf b/1-org/envs/shared/org_policy.tf index 6d391e85d..5c43dbbc1 100644 --- a/1-org/envs/shared/org_policy.tf +++ b/1-org/envs/shared/org_policy.tf @@ -90,6 +90,13 @@ module "restrict_protocol_fowarding" { IAM *******************************************/ +resource "time_sleep" "wait_logs_export" { + create_duration = "30s" + depends_on = [ + module.logs_export + ] +} + module "org_domain_restricted_sharing" { source = "terraform-google-modules/org-policy/google//modules/domain_restricted_sharing" version = "~> 5.1" @@ -98,6 +105,10 @@ module "org_domain_restricted_sharing" { folder_id = local.folder_id policy_for = local.policy_for domains_to_allow = var.domains_to_allow + + depends_on = [ + time_sleep.wait_logs_export + ] } /****************************************** diff --git a/1-org/envs/shared/outputs.tf b/1-org/envs/shared/outputs.tf index 32c298728..171315e2d 100644 --- a/1-org/envs/shared/outputs.tf +++ b/1-org/envs/shared/outputs.tf @@ -119,6 +119,11 @@ output "logs_export_logbucket_name" { description = "The log bucket for destination of log exports. See https://cloud.google.com/logging/docs/routing/overview#buckets ." } +output "billing_sink_names" { + value = module.logs_export.billing_sink_names + description = "The name of the sinks under billing account level." +} + output "logs_export_logbucket_linked_dataset_name" { value = module.logs_export.logbucket_linked_dataset_name description = "The resource name of the Log Bucket linked BigQuery dataset created for Log Analytics. See https://cloud.google.com/logging/docs/log-analytics ." diff --git a/1-org/envs/shared/remote.tf b/1-org/envs/shared/remote.tf index a32fc99aa..9b54670c8 100644 --- a/1-org/envs/shared/remote.tf +++ b/1-org/envs/shared/remote.tf @@ -26,12 +26,11 @@ locals { default_region = data.terraform_remote_state.bootstrap.outputs.common_config.default_region project_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.project_prefix folder_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.folder_prefix - group_billing_admins = data.terraform_remote_state.bootstrap.outputs.group_billing_admins - group_org_admins = data.terraform_remote_state.bootstrap.outputs.group_org_admins networks_step_terraform_service_account_email = data.terraform_remote_state.bootstrap.outputs.networks_step_terraform_service_account_email org_step_terraform_service_account_email = data.terraform_remote_state.bootstrap.outputs.organization_step_terraform_service_account_email bootstrap_folder_name = data.terraform_remote_state.bootstrap.outputs.common_config.bootstrap_folder_name cloud_build_private_worker_pool_id = try(data.terraform_remote_state.bootstrap.outputs.cloud_build_private_worker_pool_id, "") + required_groups = data.terraform_remote_state.bootstrap.outputs.required_groups } data "terraform_remote_state" "bootstrap" { diff --git a/1-org/envs/shared/variables.tf b/1-org/envs/shared/variables.tf index bb439163d..75645deda 100644 --- a/1-org/envs/shared/variables.tf +++ b/1-org/envs/shared/variables.tf @@ -20,16 +20,6 @@ variable "enable_hub_and_spoke" { default = false } -variable "billing_data_users" { - description = "Google Workspace or Cloud Identity group that have access to billing data set." - type = string -} - -variable "audit_data_users" { - description = "Google Workspace or Cloud Identity group that have access to audit logs." - type = string -} - variable "domains_to_allow" { description = "The list of domains to allow users from in IAM. Used by Domain Restricted Sharing Organization Policy. Must include the domain of the organization you are deploying the foundation. To add other domains you must also grant access to these domains to the Terraform Service Account used in the deploy." type = list(string) @@ -165,27 +155,12 @@ variable "gcp_groups" { global_secrets_admin: Google Workspace or Cloud Identity group that members are responsible for putting secrets into Secrets Manage EOT type = object({ - platform_viewer = optional(string, null) + audit_viewer = optional(string, null) security_reviewer = optional(string, null) network_viewer = optional(string, null) scc_admin = optional(string, null) - audit_viewer = optional(string, null) global_secrets_admin = optional(string, null) - }) - default = {} -} - -variable "gcp_user" { - description = <- name: The name of the log bucket to be created and used for log entries matching the filter.
- logging\_sink\_name: The name of the log sink to be created.
- logging\_sink\_filter: The filter to apply when exporting logs. Only log entries that match the filter are exported. Default is "" which exports all logs.
- location: The location of the log bucket. Default: global.
- enable\_analytics: Whether or not Log Analytics is enabled. A Log bucket with Log Analytics enabled can be queried in the Log Analytics page using SQL queries. Cannot be disabled once enabled.
- linked\_dataset\_id: The ID of the linked BigQuery dataset. A valid link dataset ID must only have alphanumeric characters and underscores within it and have up to 100 characters.
- linked\_dataset\_description: A use-friendly description of the linked BigQuery dataset. The maximum length of the description is 8000 characters.
- retention\_days: The number of days data should be retained for the log bucket. Default 30. |
object({
name = optional(string, null)
logging_sink_name = optional(string, null)
logging_sink_filter = optional(string, "")
location = optional(string, "global")
enable_analytics = optional(bool, true)
linked_dataset_id = optional(string, null)
linked_dataset_description = optional(string, null)
retention_days = optional(number, 30)
})
| `null` | no | | logging\_destination\_project\_id | The ID of the project that will have the resources where the logs will be created. | `string` | n/a | yes | | logging\_project\_key | (Optional) The key of logging destination project if it is inside resources map. It is mandatory when resource\_type = project and logging\_target\_type = logbucket. | `string` | `""` | no | @@ -71,6 +73,7 @@ module "logging_logbucket" { | Name | Description | |------|-------------| +| billing\_sink\_names | Map of log sink names with billing suffix | | logbucket\_destination\_name | The resource name for the destination Log Bucket. | | logbucket\_linked\_dataset\_name | The resource name of the Log Bucket linked BigQuery dataset. | | pubsub\_destination\_name | The resource name for the destination Pub/Sub. | diff --git a/1-org/modules/centralized-logging/main.tf b/1-org/modules/centralized-logging/main.tf index 5a7a974f6..f15acc3be 100644 --- a/1-org/modules/centralized-logging/main.tf +++ b/1-org/modules/centralized-logging/main.tf @@ -62,6 +62,12 @@ locals { lbk = try(module.destination_logbucket[0].destination_uri, "") } + destination_resource_name = merge( + var.pubsub_options != null ? { pub = module.destination_pubsub[0].resource_name } : {}, + var.storage_options != null ? { sto = module.destination_storage[0].resource_name } : {}, + var.logbucket_options != null ? { lbk = module.destination_logbucket[0].resource_name } : {} + ) + logging_tgt_prefix = { pub = "tp-logs-" sto = try("bkt-logs-${var.logging_destination_project_id}-", "bkt-logs-") @@ -90,6 +96,28 @@ module "log_export" { include_children = local.include_children } + +module "log_export_billing" { + source = "terraform-google-modules/log-export/google" + version = "~> 7.4" + + for_each = var.enable_billing_account_sink ? local.destination_resource_name : {} + + destination_uri = local.destination_uri_map[each.key] + filter = "" + log_sink_name = "${coalesce(local.destinations_options[each.key].logging_sink_name, local.logging_sink_name_map[each.key])}-billing-${random_string.suffix.result}" + parent_resource_id = var.billing_account + parent_resource_type = "billing_account" + unique_writer_identity = true +} + +resource "time_sleep" "wait_sa_iam_membership" { + create_duration = "30s" + depends_on = [ + module.log_export_billing + ] +} + #-------------------------# # Send logs to Log Bucket # #-------------------------# @@ -124,6 +152,25 @@ resource "google_project_iam_member" "logbucket_sink_member" { member = module.log_export["${each.value}_lbk"].writer_identity } +#------------------------------------------------------------------# +# Log Bucket Service account IAM membership for log_export_billing # +#------------------------------------------------------------------# +resource "google_project_iam_member" "logbucket_sink_member_billing" { + count = var.enable_billing_account_sink == true && var.logbucket_options != null ? 1 : 0 + + project = var.logging_destination_project_id + role = "roles/logging.bucketWriter" + + # Set permission only on sinks for this destination using + # module.log_export_billing key "_" + member = module.log_export_billing["lbk"].writer_identity + + + depends_on = [ + time_sleep.wait_sa_iam_membership + ] +} + #----------------------# # Send logs to Storage # #----------------------# @@ -158,6 +205,22 @@ resource "google_storage_bucket_iam_member" "storage_sink_member" { member = module.log_export["${each.value}_sto"].writer_identity } +#---------------------------------------------------------------# +# Storage Service account IAM membership for log_export_billing # +#---------------------------------------------------------------# +resource "google_storage_bucket_iam_member" "storage_sink_member_billing" { + count = var.enable_billing_account_sink == true && var.storage_options != null ? length(var.resources) : 0 + + bucket = module.destination_storage[0].resource_name + role = "roles/storage.objectCreator" + member = module.log_export_billing["sto"].writer_identity + + + depends_on = [ + google_project_iam_member.logbucket_sink_member_billing + ] +} + #----------------------# # Send logs to Pub\Sub # @@ -185,3 +248,20 @@ resource "google_pubsub_topic_iam_member" "pubsub_sink_member" { role = "roles/pubsub.publisher" member = module.log_export["${each.value}_pub"].writer_identity } + +#--------------------------------------------------------------# +# Pubsub Service account IAM membership for log_export_billing # +#--------------------------------------------------------------# +resource "google_pubsub_topic_iam_member" "pubsub_sink_member_billing" { + count = var.enable_billing_account_sink && var.pubsub_options != null ? length(var.resources) : 0 + + + project = var.logging_destination_project_id + topic = module.destination_pubsub[0].resource_name + role = "roles/pubsub.publisher" + member = module.log_export_billing["pub"].writer_identity + + depends_on = [ + google_storage_bucket_iam_member.storage_sink_member_billing + ] +} diff --git a/1-org/modules/centralized-logging/outputs.tf b/1-org/modules/centralized-logging/outputs.tf index abd1a86b8..e58788d5a 100644 --- a/1-org/modules/centralized-logging/outputs.tf +++ b/1-org/modules/centralized-logging/outputs.tf @@ -29,6 +29,14 @@ output "logbucket_destination_name" { value = try(module.destination_logbucket[0].resource_name, "") } +output "billing_sink_names" { + description = "Map of log sink names with billing suffix" + value = { + for key, options in local.destinations_options : + key => "${coalesce(options.logging_sink_name, local.logging_sink_name_map[key])}-billing-${random_string.suffix.result}" + } +} + output "logbucket_linked_dataset_name" { description = "The resource name of the Log Bucket linked BigQuery dataset." value = try(module.destination_logbucket[0].linked_dataset_name, "") diff --git a/1-org/modules/centralized-logging/variables.tf b/1-org/modules/centralized-logging/variables.tf index 3cebb9d70..955948af2 100644 --- a/1-org/modules/centralized-logging/variables.tf +++ b/1-org/modules/centralized-logging/variables.tf @@ -34,6 +34,18 @@ variable "resource_type" { } } +variable "billing_account" { + description = "Billing Account ID used in case sinks are under billing account level. Format 000000-000000-000000." + type = string + default = null +} + +variable "enable_billing_account_sink" { + description = "If true, a log router sink will be created for the billing account. The billing_account variable cannot be null." + type = bool + default = false +} + variable "logging_project_key" { description = "(Optional) The key of logging destination project if it is inside resources map. It is mandatory when resource_type = project and logging_target_type = logbucket." type = string diff --git a/2-environments/envs/development/main.tf b/2-environments/envs/development/main.tf index b291ddbda..c8502a9a6 100644 --- a/2-environments/envs/development/main.tf +++ b/2-environments/envs/development/main.tf @@ -17,9 +17,8 @@ module "env" { source = "../../modules/env_baseline" - env = "development" - environment_code = "d" - monitoring_workspace_users = var.monitoring_workspace_users - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = "development" + environment_code = "d" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name } diff --git a/2-environments/envs/non-production/main.tf b/2-environments/envs/non-production/main.tf index a0ebe6e3f..193e0fa9a 100644 --- a/2-environments/envs/non-production/main.tf +++ b/2-environments/envs/non-production/main.tf @@ -17,9 +17,8 @@ module "env" { source = "../../modules/env_baseline" - env = "non-production" - environment_code = "n" - monitoring_workspace_users = var.monitoring_workspace_users - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = "non-production" + environment_code = "n" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name } diff --git a/2-environments/envs/production/main.tf b/2-environments/envs/production/main.tf index f7eabffda..95ecdd724 100644 --- a/2-environments/envs/production/main.tf +++ b/2-environments/envs/production/main.tf @@ -17,11 +17,10 @@ module "env" { source = "../../modules/env_baseline" - env = "production" - environment_code = "p" - monitoring_workspace_users = var.monitoring_workspace_users - remote_state_bucket = var.remote_state_bucket - tfc_org_name = var.tfc_org_name + env = "production" + environment_code = "p" + remote_state_bucket = var.remote_state_bucket + tfc_org_name = var.tfc_org_name assured_workload_configuration = { enabled = false diff --git a/2-environments/modules/env_baseline/README.md b/2-environments/modules/env_baseline/README.md index eaea52116..87d087f81 100644 --- a/2-environments/modules/env_baseline/README.md +++ b/2-environments/modules/env_baseline/README.md @@ -6,7 +6,6 @@ | assured\_workload\_configuration | Assured Workload configuration. See https://cloud.google.com/assured-workloads ."
enabled: If the assured workload should be created.
location: The location where the workload will be created.
display\_name: User-assigned resource display name.
compliance\_regime: Supported Compliance Regimes. See https://cloud.google.com/assured-workloads/docs/reference/rest/Shared.Types/ComplianceRegime .
resource\_type: The type of resource. One of CONSUMER\_FOLDER, KEYRING, or ENCRYPTION\_KEYS\_PROJECT. |
object({
enabled = optional(bool, false)
location = optional(string, "us-central1")
display_name = optional(string, "FEDRAMP-MODERATE")
compliance_regime = optional(string, "FEDRAMP_MODERATE")
resource_type = optional(string, "CONSUMER_FOLDER")
})
| `{}` | no | | env | The environment to prepare (ex. development) | `string` | n/a | yes | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization (ex. d). | `string` | n/a | yes | -| monitoring\_workspace\_users | Google Workspace or Cloud Identity group that have access to Monitoring Workspaces. | `string` | n/a | yes | | project\_budget | Budget configuration for projects.
budget\_amount: The amount to use as the budget.
alert\_spent\_percents: A list of percentages of the budget to alert on when threshold is exceeded.
alert\_pubsub\_topic: The name of the Cloud Pub/Sub topic where budget related messages will be published, in the form of `projects/{project_id}/topics/{topic_id}`.
alert\_spend\_basis: The type of basis used to determine if spend has passed the threshold. Possible choices are `CURRENT_SPEND` or `FORECASTED_SPEND` (default). |
object({
base_network_budget_amount = optional(number, 1000)
base_network_alert_spent_percents = optional(list(number), [1.2])
base_network_alert_pubsub_topic = optional(string, null)
base_network_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
restricted_network_budget_amount = optional(number, 1000)
restricted_network_alert_spent_percents = optional(list(number), [1.2])
restricted_network_alert_pubsub_topic = optional(string, null)
restricted_network_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
monitoring_budget_amount = optional(number, 1000)
monitoring_alert_spent_percents = optional(list(number), [1.2])
monitoring_alert_pubsub_topic = optional(string, null)
monitoring_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
secret_budget_amount = optional(number, 1000)
secret_alert_spent_percents = optional(list(number), [1.2])
secret_alert_pubsub_topic = optional(string, null)
secret_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
kms_budget_amount = optional(number, 1000)
kms_alert_spent_percents = optional(list(number), [1.2])
kms_alert_pubsub_topic = optional(string, null)
kms_budget_alert_spend_basis = optional(string, "FORECASTED_SPEND")
})
| `{}` | no | | remote\_state\_bucket | Backend bucket to load Terraform Remote State Data from previous steps. | `string` | n/a | yes | | tfc\_org\_name | Name of the TFC organization | `string` | n/a | yes | diff --git a/2-environments/modules/env_baseline/iam.tf b/2-environments/modules/env_baseline/iam.tf index d602f4485..ffe9acbc3 100644 --- a/2-environments/modules/env_baseline/iam.tf +++ b/2-environments/modules/env_baseline/iam.tf @@ -18,8 +18,8 @@ Monitoring - IAM *****************************************/ -resource "google_project_iam_member" "monitoring_editor" { +resource "google_project_iam_member" "monitoring_viewer" { project = module.monitoring_project.project_id - role = "roles/monitoring.editor" - member = "group:${var.monitoring_workspace_users}" + role = "roles/monitoring.viewer" + member = "group:${local.required_groups["monitoring_workspace_users"]}" } diff --git a/2-environments/modules/env_baseline/remote.tf b/2-environments/modules/env_baseline/remote.tf index 9475329c7..521bab9cc 100644 --- a/2-environments/modules/env_baseline/remote.tf +++ b/2-environments/modules/env_baseline/remote.tf @@ -21,6 +21,7 @@ locals { project_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.project_prefix folder_prefix = data.terraform_remote_state.bootstrap.outputs.common_config.folder_prefix tags = data.terraform_remote_state.org.outputs.tags + required_groups = data.terraform_remote_state.bootstrap.outputs.required_groups } data "terraform_remote_state" "bootstrap" { diff --git a/2-environments/modules/env_baseline/variables.tf b/2-environments/modules/env_baseline/variables.tf index 7a93fb2a9..5c459a0cb 100644 --- a/2-environments/modules/env_baseline/variables.tf +++ b/2-environments/modules/env_baseline/variables.tf @@ -34,11 +34,6 @@ variable "tfc_org_name" { type = string } -variable "monitoring_workspace_users" { - description = "Google Workspace or Cloud Identity group that have access to Monitoring Workspaces." - type = string -} - variable "project_budget" { description = <