From 97cfe1c53f6d9fa12953e975a7db5a71502197f5 Mon Sep 17 00:00:00 2001 From: Yisheng Cai Date: Sun, 28 Jul 2024 06:41:27 +0800 Subject: [PATCH] feat: Support GCP impersonation (#94) Introduce new parameter `streamnative_org_id` which is required to grant permissions of GCP project for impersonation --- modules/gcp/vendor-access/README.md | 7 +- modules/gcp/vendor-access/common.tf | 10 +++ modules/gcp/vendor-access/docs/main.tf | 3 +- modules/gcp/vendor-access/main.tf | 88 +++++++++++++++++++++----- 4 files changed, 88 insertions(+), 20 deletions(-) diff --git a/modules/gcp/vendor-access/README.md b/modules/gcp/vendor-access/README.md index 42eb5a7..cb08e5c 100644 --- a/modules/gcp/vendor-access/README.md +++ b/modules/gcp/vendor-access/README.md @@ -34,8 +34,9 @@ provider "google" { } module "sn_managed_cloud" { - source = "github.com/streamnative/terraform-managed-cloud//modules/gcp/vendor-access?ref=v3.7.0" + source = "github.com/streamnative/terraform-managed-cloud//modules/gcp/vendor-access?ref=v3.15.0" project = "" + streamnative_org_id = "" } ``` @@ -523,7 +524,8 @@ No modules. |------|-------------|------|---------|:--------:| | [extra\_google\_services](#input\_extra\_google\_services) | Extra google API services need to be enabled. | `list(string)` | `[]` | no | | [project](#input\_project) | The project id of the target project | `string` | n/a | yes | -| [roles](#input\_roles) | The role list will be associated with StreamNative GSA. | `list(string)` |
[
"roles/editor",
"roles/compute.admin",
"roles/compute.loadBalancerAdmin",
"roles/compute.networkAdmin",
"roles/container.admin",
"roles/dns.admin",
"roles/storage.admin",
"roles/iam.serviceAccountAdmin",
"roles/iam.workloadIdentityPoolAdmin",
"roles/resourcemanager.projectIamAdmin"
]
| no | +| [roles](#input\_roles) | The role list will be associated with StreamNative GSA. | `list(string)` |
[
"roles/editor",
"roles/cloudkms.admin",
"roles/compute.admin",
"roles/compute.loadBalancerAdmin",
"roles/compute.networkAdmin",
"roles/container.admin",
"roles/dns.admin",
"roles/storage.admin",
"roles/iam.serviceAccountAdmin",
"roles/iam.workloadIdentityPoolAdmin",
"roles/resourcemanager.projectIamAdmin"
]
| no | +| [streamnative\_org\_id](#input\_streamnative\_org\_id) | Your Organization ID within StreamNative Cloud, used as name of impersonation GSA in your project. This will be the organization ID in the StreamNative console, e.g. "o-xhopj". | `string` | `""` | no | | [streamnative\_support\_access\_gsa](#input\_streamnative\_support\_access\_gsa) | The GSA will be used by StreamnNative support team. | `list(string)` |
[
"cloud-support-general@sncloud-production.iam.gserviceaccount.com"
]
| no | | [streamnative\_vendor\_access\_gsa](#input\_streamnative\_vendor\_access\_gsa) | The GSA will be used by StreamnNative cloud. | `list(string)` |
[
"cloud-manager@sncloud-production.iam.gserviceaccount.com",
"pool-automation@sncloud-production.iam.gserviceaccount.com"
]
| no | @@ -533,3 +535,4 @@ No modules. |------|-------------| | [google\_services](#output\_google\_services) | Enabled google services. | | [iam\_bindings](#output\_iam\_bindings) | Configured iam policies. | +| [impersonation\_iam\_bindings](#output\_impersonation\_iam\_bindings) | Configured iam policies for impersonation. | diff --git a/modules/gcp/vendor-access/common.tf b/modules/gcp/vendor-access/common.tf index d3d6ac9..18d5e54 100644 --- a/modules/gcp/vendor-access/common.tf +++ b/modules/gcp/vendor-access/common.tf @@ -31,6 +31,16 @@ variable "streamnative_vendor_access_gsa" { description = "The GSA will be used by StreamnNative cloud." } +variable "streamnative_org_id" { + default = "" + type = string + description = "Your Organization ID within StreamNative Cloud, used as name of impersonation GSA in your project. This will be the organization ID in the StreamNative console, e.g. \"o-xhopj\"." + validation { + condition = length(var.streamnative_org_id) <= 18 + error_message = "The organization ID must not exceed 18 characters. If you reach this limit, please contact StreamNative support." + } +} + variable "streamnative_support_access_gsa" { default = ["cloud-support-general@sncloud-production.iam.gserviceaccount.com"] type = list(string) diff --git a/modules/gcp/vendor-access/docs/main.tf b/modules/gcp/vendor-access/docs/main.tf index 95c67bf..467ac10 100644 --- a/modules/gcp/vendor-access/docs/main.tf +++ b/modules/gcp/vendor-access/docs/main.tf @@ -3,6 +3,7 @@ provider "google" { } module "sn_managed_cloud" { - source = "github.com/streamnative/terraform-managed-cloud//modules/gcp/vendor-access?ref=v3.7.0" + source = "github.com/streamnative/terraform-managed-cloud//modules/gcp/vendor-access?ref=v3.15.0" project = "" + streamnative_org_id = "" } \ No newline at end of file diff --git a/modules/gcp/vendor-access/main.tf b/modules/gcp/vendor-access/main.tf index 7a64a15..ae77910 100644 --- a/modules/gcp/vendor-access/main.tf +++ b/modules/gcp/vendor-access/main.tf @@ -1,13 +1,4 @@ locals { - streamnative_gsa = concat(var.streamnative_vendor_access_gsa, var.streamnative_support_access_gsa) - iam_bindings = flatten([ - for role in var.roles : [ - for gsa in local.streamnative_gsa : { - role : role, - member : format("serviceAccount:%s", gsa), - } - ] - ]) google_services = concat([ "autoscaling.googleapis.com", "cloudresourcemanager.googleapis.com", @@ -31,23 +22,86 @@ resource "google_project_service" "gcp_apis" { service = local.google_services[count.index] } +locals { + is_impersonation_enabled = var.streamnative_org_id != "" + streamnative_gsa = concat(var.streamnative_vendor_access_gsa, var.streamnative_support_access_gsa) +} + +# Grant permissions directly to the StreamNative Cloud service account +locals { + iam_bindings = local.is_impersonation_enabled ? [] : flatten([ + for role in var.roles : [ + for gsa in local.streamnative_gsa : { + role : role, + member : format("serviceAccount:%s", gsa), + } + ] + ]) +} + resource "google_project_iam_member" "sn_access" { - for_each = { - for index, binding in local.iam_bindings : - index => binding - } + count = length(local.iam_bindings) project = var.project - role = each.value.role - member = each.value.member + role = local.iam_bindings[count.index].role + member = local.iam_bindings[count.index].member depends_on = [google_project_service.gcp_apis] } +# Grant permissions to the project service account that will be impersonated by StreamNative Cloud service account +locals { + streamnative_bootstrap_gsa_name = format("snbootstrap-%s", var.streamnative_org_id) + streamnative_bootstrap_roles = local.is_impersonation_enabled ? var.roles : [] + impersonation_roles = [ + "roles/iam.serviceAccountUser", + "roles/iam.serviceAccountAdmin", + "roles/iam.serviceAccountTokenCreator", + ] + impersonation_iam_bindings = local.is_impersonation_enabled ? flatten([ + for role in local.impersonation_roles : [ + for gsa in local.streamnative_gsa : { + role : role, + member : format("serviceAccount:%s", gsa), + } + ] + ]) : [] + +} +resource "google_service_account" "sn_bootstrap" { + count = local.is_impersonation_enabled ? 1 : 0 + account_id = local.streamnative_bootstrap_gsa_name + project = var.project + display_name = "StreamNative Bootstrap GSA that will be impersonated by StreamNative Cloud Control Plane." + depends_on = [google_project_service.gcp_apis] +} + +resource "google_project_iam_member" "sn_bootstrap" { + count = length(local.streamnative_bootstrap_roles) + project = var.project + role = local.streamnative_bootstrap_roles[count.index] + member = format("serviceAccount:%s", google_service_account.sn_bootstrap[0].email) + depends_on = [google_service_account.sn_bootstrap] +} + +resource "google_service_account_iam_member" "sn_bootstrap_impersonation" { + count = length(local.impersonation_iam_bindings) + service_account_id = google_service_account.sn_bootstrap[0].id + role = local.impersonation_iam_bindings[count.index].role + member = local.impersonation_iam_bindings[count.index].member + depends_on = [google_service_account.sn_bootstrap] +} + + output "google_services" { - value = local.google_services + value = local.google_services description = "Enabled google services." } output "iam_bindings" { - value = local.iam_bindings + value = local.iam_bindings description = "Configured iam policies." } + +output "impersonation_iam_bindings" { + value = local.impersonation_iam_bindings + description = "Configured iam policies for impersonation." +}