diff --git a/README.md b/README.md index 16a0a089d..9a7a6aa61 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -282,7 +283,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/autogen/main/README.md b/autogen/main/README.md index a91f352e4..c89e2e9ce 100644 --- a/autogen/main/README.md +++ b/autogen/main/README.md @@ -241,7 +241,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/autogen/main/sa.tf.tmpl b/autogen/main/sa.tf.tmpl index 6eddbfea1..287072dea 100644 --- a/autogen/main/sa.tf.tmpl +++ b/autogen/main/sa.tf.tmpl @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/autogen/main/variables.tf.tmpl b/autogen/main/variables.tf.tmpl index 1505c9e58..e84526f96 100644 --- a/autogen/main/variables.tf.tmpl +++ b/autogen/main/variables.tf.tmpl @@ -360,10 +360,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/autogen/safer-cluster/README.md b/autogen/safer-cluster/README.md index 74a86dc21..772dff12d 100644 --- a/autogen/safer-cluster/README.md +++ b/autogen/safer-cluster/README.md @@ -53,7 +53,8 @@ developers, which mostly just want to deploy and debug applications. own projects, so that they can be administered independently (e.g., dev cluster; production clusters; staging clusters should go in different projects.) -- *A shared GCR project (`registry_project_id`):* all clusters can share the same GCR project. +- *Shared GCR projects (`registry_project_ids`):* all clusters can share the same + GCR projects. - Easier to share images between environments. The same image could be progressively rolled-out in dev, staging, and then production. @@ -93,7 +94,7 @@ The Safer Cluster setup relies on several service accounts: ``` create_service_account = true -registry_project_id = +registry_project_ids = [] grant_registry_access = true ``` diff --git a/autogen/safer-cluster/main.tf.tmpl b/autogen/safer-cluster/main.tf.tmpl index f4e4146b1..7a871d372 100644 --- a/autogen/safer-cluster/main.tf.tmpl +++ b/autogen/safer-cluster/main.tf.tmpl @@ -101,6 +101,7 @@ module "gke" { create_service_account = var.compute_engine_service_account == "" ? true : false service_account = var.compute_engine_service_account registry_project_id = var.registry_project_id + registry_project_ids = var.registry_project_ids grant_registry_access = var.grant_registry_access // Basic Auth disabled diff --git a/autogen/safer-cluster/variables.tf.tmpl b/autogen/safer-cluster/variables.tf.tmpl index f085d9193..b64d63c7a 100644 --- a/autogen/safer-cluster/variables.tf.tmpl +++ b/autogen/safer-cluster/variables.tf.tmpl @@ -210,10 +210,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "cluster_resource_labels" { type = map(string) description = "The GCE resource labels (a map of key/value pairs) to be applied to the cluster" diff --git a/examples/workload_metadata_config/main.tf b/examples/workload_metadata_config/main.tf index 40601eace..27e05448c 100644 --- a/examples/workload_metadata_config/main.tf +++ b/examples/workload_metadata_config/main.tf @@ -51,7 +51,7 @@ module "gke" { ip_range_services = var.ip_range_services create_service_account = true grant_registry_access = true - registry_project_id = var.registry_project_id + registry_project_ids = var.registry_project_ids enable_private_endpoint = true enable_private_nodes = true master_ipv4_cidr_block = "172.16.0.0/28" diff --git a/examples/workload_metadata_config/outputs.tf b/examples/workload_metadata_config/outputs.tf index 01a13147c..520ce7836 100644 --- a/examples/workload_metadata_config/outputs.tf +++ b/examples/workload_metadata_config/outputs.tf @@ -25,7 +25,8 @@ output "client_token" { } output "ca_certificate" { - value = module.gke.ca_certificate + sensitive = true + value = module.gke.ca_certificate } output "service_account" { diff --git a/examples/workload_metadata_config/variables.tf b/examples/workload_metadata_config/variables.tf index 7f0750278..603dc4c4f 100644 --- a/examples/workload_metadata_config/variables.tf +++ b/examples/workload_metadata_config/variables.tf @@ -48,6 +48,7 @@ variable "ip_range_services" { description = "The secondary ip range to use for services" } -variable "registry_project_id" { - description = "Project name for the GCR registry" +variable "registry_project_ids" { + description = "Project names for GCR registries" + type = list(string) } diff --git a/modules/beta-private-cluster-update-variant/README.md b/modules/beta-private-cluster-update-variant/README.md index 5ea8ab5ed..d1a9a53c5 100644 --- a/modules/beta-private-cluster-update-variant/README.md +++ b/modules/beta-private-cluster-update-variant/README.md @@ -232,7 +232,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -352,7 +353,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/modules/beta-private-cluster-update-variant/sa.tf b/modules/beta-private-cluster-update-variant/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/modules/beta-private-cluster-update-variant/sa.tf +++ b/modules/beta-private-cluster-update-variant/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/modules/beta-private-cluster-update-variant/variables.tf b/modules/beta-private-cluster-update-variant/variables.tf index b338f1002..8dd21d53e 100644 --- a/modules/beta-private-cluster-update-variant/variables.tf +++ b/modules/beta-private-cluster-update-variant/variables.tf @@ -348,10 +348,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/modules/beta-private-cluster/README.md b/modules/beta-private-cluster/README.md index 1314d506d..c46bfce55 100644 --- a/modules/beta-private-cluster/README.md +++ b/modules/beta-private-cluster/README.md @@ -210,7 +210,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -330,7 +331,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/modules/beta-private-cluster/sa.tf b/modules/beta-private-cluster/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/modules/beta-private-cluster/sa.tf +++ b/modules/beta-private-cluster/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/modules/beta-private-cluster/variables.tf b/modules/beta-private-cluster/variables.tf index b338f1002..8dd21d53e 100644 --- a/modules/beta-private-cluster/variables.tf +++ b/modules/beta-private-cluster/variables.tf @@ -348,10 +348,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/modules/beta-public-cluster-update-variant/README.md b/modules/beta-public-cluster-update-variant/README.md index 491be6604..875967d82 100644 --- a/modules/beta-public-cluster-update-variant/README.md +++ b/modules/beta-public-cluster-update-variant/README.md @@ -221,7 +221,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -339,7 +340,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/modules/beta-public-cluster-update-variant/sa.tf b/modules/beta-public-cluster-update-variant/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/modules/beta-public-cluster-update-variant/sa.tf +++ b/modules/beta-public-cluster-update-variant/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/modules/beta-public-cluster-update-variant/variables.tf b/modules/beta-public-cluster-update-variant/variables.tf index 7b69bd1fd..2d4f27d88 100644 --- a/modules/beta-public-cluster-update-variant/variables.tf +++ b/modules/beta-public-cluster-update-variant/variables.tf @@ -348,10 +348,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/modules/beta-public-cluster/README.md b/modules/beta-public-cluster/README.md index b86a908fe..ca6f5fe25 100644 --- a/modules/beta-public-cluster/README.md +++ b/modules/beta-public-cluster/README.md @@ -199,7 +199,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -317,7 +318,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/modules/beta-public-cluster/sa.tf b/modules/beta-public-cluster/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/modules/beta-public-cluster/sa.tf +++ b/modules/beta-public-cluster/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/modules/beta-public-cluster/variables.tf b/modules/beta-public-cluster/variables.tf index 7b69bd1fd..2d4f27d88 100644 --- a/modules/beta-public-cluster/variables.tf +++ b/modules/beta-public-cluster/variables.tf @@ -348,10 +348,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/modules/private-cluster-update-variant/README.md b/modules/private-cluster-update-variant/README.md index 9eeb4867e..75a2a272c 100644 --- a/modules/private-cluster-update-variant/README.md +++ b/modules/private-cluster-update-variant/README.md @@ -209,7 +209,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -316,7 +317,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/modules/private-cluster-update-variant/sa.tf b/modules/private-cluster-update-variant/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/modules/private-cluster-update-variant/sa.tf +++ b/modules/private-cluster-update-variant/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/modules/private-cluster-update-variant/variables.tf b/modules/private-cluster-update-variant/variables.tf index 1645e5df6..51ad164f6 100644 --- a/modules/private-cluster-update-variant/variables.tf +++ b/modules/private-cluster-update-variant/variables.tf @@ -312,10 +312,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/modules/private-cluster/README.md b/modules/private-cluster/README.md index ed70afa9e..3630ba796 100644 --- a/modules/private-cluster/README.md +++ b/modules/private-cluster/README.md @@ -187,7 +187,8 @@ Then perform the following commands on the root folder: | project\_id | The project ID to host the cluster in (required) | `string` | n/a | yes | | region | The region to host the cluster in (optional if zonal cluster / required if regional) | `string` | `null` | no | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `UNSPECIFIED`. | `string` | `null` | no | | remove\_default\_node\_pool | Remove default node pool while setting up the cluster | `bool` | `false` | no | | resource\_usage\_export\_dataset\_id | The ID of a BigQuery Dataset for using BigQuery as the destination of resource usage export. | `string` | `""` | no | @@ -294,7 +295,7 @@ following project roles: - roles/iam.serviceAccountUser - roles/resourcemanager.projectIamAdmin (only required if `service_account` is set to `create`) -Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_id` project: +Additionally, if `service_account` is set to `create` and `grant_registry_access` is requested, the service account requires the following role on the `registry_project_ids` projects: - roles/resourcemanager.projectIamAdmin ### Enable APIs diff --git a/modules/private-cluster/sa.tf b/modules/private-cluster/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/modules/private-cluster/sa.tf +++ b/modules/private-cluster/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/modules/private-cluster/variables.tf b/modules/private-cluster/variables.tf index 1645e5df6..51ad164f6 100644 --- a/modules/private-cluster/variables.tf +++ b/modules/private-cluster/variables.tf @@ -312,10 +312,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created." diff --git a/modules/safer-cluster-update-variant/README.md b/modules/safer-cluster-update-variant/README.md index e9955a8de..f6b997eba 100644 --- a/modules/safer-cluster-update-variant/README.md +++ b/modules/safer-cluster-update-variant/README.md @@ -53,7 +53,8 @@ developers, which mostly just want to deploy and debug applications. own projects, so that they can be administered independently (e.g., dev cluster; production clusters; staging clusters should go in different projects.) -- *A shared GCR project (`registry_project_id`):* all clusters can share the same GCR project. +- *Shared GCR projects (`registry_project_ids`):* all clusters can share the same + GCR projects. - Easier to share images between environments. The same image could be progressively rolled-out in dev, staging, and then production. @@ -93,7 +94,7 @@ The Safer Cluster setup relies on several service accounts: ``` create_service_account = true -registry_project_id = +registry_project_ids = [] grant_registry_access = true ``` @@ -248,7 +249,8 @@ For simplicity, we suggest using `roles/container.admin` and | project\_id | The project ID to host the cluster in | `string` | n/a | yes | | region | The region to host the cluster in | `string` | n/a | yes | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | (Beta) The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `REGULAR`. | `string` | `"REGULAR"` | no | | resource\_usage\_export\_dataset\_id | The dataset id for which network egress metering for this cluster will be enabled. If enabled, a daemonset will be created in the cluster to meter network egress traffic. | `string` | `""` | no | | sandbox\_enabled | (Beta) Enable GKE Sandbox (Do not forget to set `image_type` = `COS_CONTAINERD` to use it). | `bool` | `false` | no | diff --git a/modules/safer-cluster-update-variant/main.tf b/modules/safer-cluster-update-variant/main.tf index 55f5d024e..1a9be43bc 100644 --- a/modules/safer-cluster-update-variant/main.tf +++ b/modules/safer-cluster-update-variant/main.tf @@ -97,6 +97,7 @@ module "gke" { create_service_account = var.compute_engine_service_account == "" ? true : false service_account = var.compute_engine_service_account registry_project_id = var.registry_project_id + registry_project_ids = var.registry_project_ids grant_registry_access = var.grant_registry_access // Basic Auth disabled diff --git a/modules/safer-cluster-update-variant/variables.tf b/modules/safer-cluster-update-variant/variables.tf index e1c100b72..6ac893416 100644 --- a/modules/safer-cluster-update-variant/variables.tf +++ b/modules/safer-cluster-update-variant/variables.tf @@ -210,10 +210,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "cluster_resource_labels" { type = map(string) description = "The GCE resource labels (a map of key/value pairs) to be applied to the cluster" diff --git a/modules/safer-cluster/README.md b/modules/safer-cluster/README.md index e9955a8de..f6b997eba 100644 --- a/modules/safer-cluster/README.md +++ b/modules/safer-cluster/README.md @@ -53,7 +53,8 @@ developers, which mostly just want to deploy and debug applications. own projects, so that they can be administered independently (e.g., dev cluster; production clusters; staging clusters should go in different projects.) -- *A shared GCR project (`registry_project_id`):* all clusters can share the same GCR project. +- *Shared GCR projects (`registry_project_ids`):* all clusters can share the same + GCR projects. - Easier to share images between environments. The same image could be progressively rolled-out in dev, staging, and then production. @@ -93,7 +94,7 @@ The Safer Cluster setup relies on several service accounts: ``` create_service_account = true -registry_project_id = +registry_project_ids = [] grant_registry_access = true ``` @@ -248,7 +249,8 @@ For simplicity, we suggest using `roles/container.admin` and | project\_id | The project ID to host the cluster in | `string` | n/a | yes | | region | The region to host the cluster in | `string` | n/a | yes | | regional | Whether is a regional cluster (zonal cluster if set false. WARNING: changing this after cluster creation is destructive!) | `bool` | `true` | no | -| registry\_project\_id | Project holding the Google Container Registry. If empty, we use the cluster project. If grant\_registry\_access is true, storage.objectViewer role is assigned on this project. | `string` | `""` | no | +| registry\_project\_id | Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version. | `string` | `""` | no | +| registry\_project\_ids | Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects. | `list(string)` | `[]` | no | | release\_channel | (Beta) The release channel of this cluster. Accepted values are `UNSPECIFIED`, `RAPID`, `REGULAR` and `STABLE`. Defaults to `REGULAR`. | `string` | `"REGULAR"` | no | | resource\_usage\_export\_dataset\_id | The dataset id for which network egress metering for this cluster will be enabled. If enabled, a daemonset will be created in the cluster to meter network egress traffic. | `string` | `""` | no | | sandbox\_enabled | (Beta) Enable GKE Sandbox (Do not forget to set `image_type` = `COS_CONTAINERD` to use it). | `bool` | `false` | no | diff --git a/modules/safer-cluster/main.tf b/modules/safer-cluster/main.tf index 6f96694fc..c61689b09 100644 --- a/modules/safer-cluster/main.tf +++ b/modules/safer-cluster/main.tf @@ -97,6 +97,7 @@ module "gke" { create_service_account = var.compute_engine_service_account == "" ? true : false service_account = var.compute_engine_service_account registry_project_id = var.registry_project_id + registry_project_ids = var.registry_project_ids grant_registry_access = var.grant_registry_access // Basic Auth disabled diff --git a/modules/safer-cluster/variables.tf b/modules/safer-cluster/variables.tf index e1c100b72..6ac893416 100644 --- a/modules/safer-cluster/variables.tf +++ b/modules/safer-cluster/variables.tf @@ -210,10 +210,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "cluster_resource_labels" { type = map(string) description = "The GCE resource labels (a map of key/value pairs) to be applied to the cluster" diff --git a/sa.tf b/sa.tf index 8c1e57860..7ea31ba05 100644 --- a/sa.tf +++ b/sa.tf @@ -25,6 +25,12 @@ locals { ) // if user set var.service_account it will be used even if var.create_service_account==true, so service account will be created but not used service_account = (var.service_account == "" || var.service_account == "create") && var.create_service_account ? local.service_account_list[0] : var.service_account + + registry_projects_list = compact( + length(var.registry_project_ids) == 0 && var.registry_project_id == "" + ? [var.project_id] + : concat([var.registry_project_id], var.registry_project_ids) + ) } resource "random_string" "cluster_service_account_suffix" { @@ -70,15 +76,15 @@ resource "google_project_iam_member" "cluster_service_account-resourceMetadata-w } resource "google_project_iam_member" "cluster_service_account-gcr" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/storage.objectViewer" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/storage.objectViewer" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } resource "google_project_iam_member" "cluster_service_account-artifact-registry" { - count = var.create_service_account && var.grant_registry_access ? 1 : 0 - project = var.registry_project_id == "" ? var.project_id : var.registry_project_id - role = "roles/artifactregistry.reader" - member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" + for_each = var.create_service_account && var.grant_registry_access ? toset(local.registry_projects_list) : [] + project = each.key + role = "roles/artifactregistry.reader" + member = "serviceAccount:${google_service_account.cluster_service_account[0].email}" } diff --git a/test/fixtures/shared/outputs.tf b/test/fixtures/shared/outputs.tf index dfc0d0239..0e3b32364 100644 --- a/test/fixtures/shared/outputs.tf +++ b/test/fixtures/shared/outputs.tf @@ -79,6 +79,6 @@ output "service_account" { value = module.example.service_account } -output "registry_project_id" { - value = var.registry_project_id +output "registry_project_ids" { + value = var.registry_project_ids } diff --git a/test/fixtures/shared/variables.tf b/test/fixtures/shared/variables.tf index 56e5b6344..446535154 100644 --- a/test/fixtures/shared/variables.tf +++ b/test/fixtures/shared/variables.tf @@ -35,6 +35,7 @@ variable "compute_engine_service_accounts" { description = "The email addresses of the service account to associate with the GKE cluster" } -variable "registry_project_id" { - description = "Project to use for granting access to the GCR registry, if requested" +variable "registry_project_ids" { + description = "Projects to use for granting access to GCR registries, if requested" + type = list(string) } diff --git a/test/fixtures/workload_identity/outputs.tf b/test/fixtures/workload_identity/outputs.tf index c83711862..7990ef101 100644 --- a/test/fixtures/workload_identity/outputs.tf +++ b/test/fixtures/workload_identity/outputs.tf @@ -65,8 +65,8 @@ output "service_account" { value = module.example.service_account } -output "registry_project_id" { - value = var.registry_project_id +output "registry_project_ids" { + value = var.registry_project_ids } output "cluster_name" { diff --git a/test/fixtures/workload_identity/variables.tf b/test/fixtures/workload_identity/variables.tf index 235e09644..79f26c793 100644 --- a/test/fixtures/workload_identity/variables.tf +++ b/test/fixtures/workload_identity/variables.tf @@ -35,7 +35,7 @@ variable "compute_engine_service_accounts" { description = "The email addresses of the service account to associate with the GKE cluster" } -variable "registry_project_id" { - description = "Project to use for granting access to the GCR registry, if requested" - default = "" +variable "registry_project_ids" { + type = list(string) + description = "Projects to use for granting access to GCR registries, if requested" } diff --git a/test/fixtures/workload_metadata_config/example.tf b/test/fixtures/workload_metadata_config/example.tf index ea9519579..ef9895c66 100644 --- a/test/fixtures/workload_metadata_config/example.tf +++ b/test/fixtures/workload_metadata_config/example.tf @@ -17,13 +17,13 @@ module "example" { source = "../../../examples/workload_metadata_config" - project_id = var.project_ids[1] - cluster_name_suffix = "-${random_string.suffix.result}" - region = var.region - zones = slice(var.zones, 0, 1) - network = google_compute_network.main.name - subnetwork = google_compute_subnetwork.main.name - ip_range_pods = google_compute_subnetwork.main.secondary_ip_range[0].range_name - ip_range_services = google_compute_subnetwork.main.secondary_ip_range[1].range_name - registry_project_id = var.registry_project_id + project_id = var.project_ids[1] + cluster_name_suffix = "-${random_string.suffix.result}" + region = var.region + zones = slice(var.zones, 0, 1) + network = google_compute_network.main.name + subnetwork = google_compute_subnetwork.main.name + ip_range_pods = google_compute_subnetwork.main.secondary_ip_range[0].range_name + ip_range_services = google_compute_subnetwork.main.secondary_ip_range[1].range_name + registry_project_ids = var.registry_project_ids } diff --git a/test/integration/workload_metadata_config/controls/gcloud.rb b/test/integration/workload_metadata_config/controls/gcloud.rb index ad642ff7c..a46d7f08d 100644 --- a/test/integration/workload_metadata_config/controls/gcloud.rb +++ b/test/integration/workload_metadata_config/controls/gcloud.rb @@ -13,7 +13,7 @@ # limitations under the License. project_id = attribute('project_id') -registry_project_id = attribute('registry_project_id') +registry_project_ids = attribute('registry_project_ids') location = attribute('location') cluster_name = attribute('cluster_name') service_account = attribute('service_account') @@ -58,19 +58,21 @@ end end - describe command("gcloud projects get-iam-policy #{registry_project_id} --format=json") do - its(:exit_status) { should eq 0 } - its(:stderr) { should eq '' } + registry_project_ids.each do |registry_project_id| + describe command("gcloud projects get-iam-policy #{registry_project_id} --format=json") do + its(:exit_status) { should eq 0 } + its(:stderr) { should eq '' } - let!(:iam) do - if subject.exit_status == 0 - JSON.parse(subject.stdout) - else - {} + let!(:iam) do + if subject.exit_status == 0 + JSON.parse(subject.stdout) + else + {} + end + end + it "has expected registry roles" do + expect(iam['bindings']).to include("members" => ["serviceAccount:#{service_account}"], "role" => "roles/storage.objectViewer") end - end - it "has expected registry roles" do - expect(iam['bindings']).to include("members" => ["serviceAccount:#{service_account}"], "role" => "roles/storage.objectViewer") end end end diff --git a/test/integration/workload_metadata_config/inspec.yml b/test/integration/workload_metadata_config/inspec.yml index 4f2b7d40d..30dbe7104 100644 --- a/test/integration/workload_metadata_config/inspec.yml +++ b/test/integration/workload_metadata_config/inspec.yml @@ -12,6 +12,6 @@ attributes: - name: service_account required: true type: string - - name: registry_project_id + - name: registry_project_ids required: false - type: string + type: array diff --git a/test/setup/outputs.tf b/test/setup/outputs.tf index 8b85098fc..b14d5daac 100644 --- a/test/setup/outputs.tf +++ b/test/setup/outputs.tf @@ -31,6 +31,6 @@ output "compute_engine_service_accounts" { value = [google_service_account.gke_sa_1.email, google_service_account.gke_sa_2.email, google_service_account.gke_sa_asm.email] } -output "registry_project_id" { - value = module.gke-project-1.project_id +output "registry_project_ids" { + value = [module.gke-project-1.project_id] } diff --git a/variables.tf b/variables.tf index 7f7dba4e6..1fe54753c 100644 --- a/variables.tf +++ b/variables.tf @@ -312,10 +312,16 @@ variable "grant_registry_access" { variable "registry_project_id" { type = string - description = "Project holding the Google Container Registry. If empty, we use the cluster project. If grant_registry_access is true, storage.objectViewer role is assigned on this project." + description = "Deprecated. Replaced by `registry_project_ids`. Still works for the purposes of backwards compatibility, but will be removed in a future version." default = "" } +variable "registry_project_ids" { + type = list(string) + description = "Projects holding Google Container Registries. If empty, we use the cluster project. If a service account is created and the `grant_registry_access` variable is set to `true`, the `storage.objectViewer` role is assigned on these projects." + default = [] +} + variable "service_account" { type = string description = "The service account to run nodes as if not overridden in `node_pools`. The create_service_account variable default value (true) will cause a cluster-specific service account to be created."