From c0bac5acc0a383b52383d24c83c9f08593ee7aef Mon Sep 17 00:00:00 2001 From: Maciej Sikora Date: Mon, 16 Jun 2025 10:42:21 +0200 Subject: [PATCH] feat: adds support for direct cloud run iap --- modules/cloud-run-v2/README.md | 30 +++++++++++----------- modules/cloud-run-v2/service.tf | 38 ++++++++++++++++++++++++++++ modules/cloud-run-v2/variables.tf | 42 +++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 14 deletions(-) diff --git a/modules/cloud-run-v2/README.md b/modules/cloud-run-v2/README.md index b237175b3..91ef4887e 100644 --- a/modules/cloud-run-v2/README.md +++ b/modules/cloud-run-v2/README.md @@ -813,9 +813,9 @@ module "cloud_run" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L181) | Name used for Cloud Run service. | string | ✓ | | -| [project_id](variables.tf#L196) | Project id used for all resources. | string | ✓ | | -| [region](variables.tf#L201) | Region used for all resources. | string | ✓ | | +| [name](variables.tf#L223) | Name used for Cloud Run service. | string | ✓ | | +| [project_id](variables.tf#L238) | Project id used for all resources. | string | ✓ | | +| [region](variables.tf#L243) | Region used for all resources. | string | ✓ | | | [containers](variables.tf#L17) | Containers in name => attributes format. | map(object({…})) | | {} | | [create_job](variables.tf#L80) | Create Cloud Run Job instead of Service. | bool | | false | | [custom_audiences](variables.tf#L86) | Custom audiences for service. | list(string) | | null | @@ -823,17 +823,19 @@ module "cloud_run" { | [encryption_key](variables.tf#L98) | The full resource name of the Cloud KMS CryptoKey. | string | | null | | [eventarc_triggers](variables.tf#L104) | Event arc triggers for different sources. | object({…}) | | {} | | [iam](variables.tf#L122) | IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | -| [ingress](variables.tf#L128) | Ingress settings. | string | | null | -| [invoker_iam_disabled](variables.tf#L145) | Disables IAM permission check for run.routes.invoke for callers of this service. | bool | | false | -| [labels](variables.tf#L151) | Resource labels. | map(string) | | {} | -| [launch_stage](variables.tf#L157) | The launch stage as defined by Google Cloud Platform Launch Stages. | string | | null | -| [managed_revision](variables.tf#L174) | Whether the Terraform module should control the deployment of revisions. | bool | | true | -| [prefix](variables.tf#L186) | Optional prefix used for resource names. | string | | null | -| [revision](variables.tf#L206) | Revision template configurations. | object({…}) | | {} | -| [service_account](variables.tf#L245) | Service account email. Unused if service account is auto-created. | string | | null | -| [service_account_create](variables.tf#L251) | Auto-create service account. | bool | | false | -| [tag_bindings](variables.tf#L257) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | -| [volumes](variables.tf#L264) | Named volumes in containers in name => attributes format. | map(object({…})) | | {} | +| [iap_enabled](variables.tf#L128) | <<-EOT Enables Identity-Aware Proxy (IAP) for this service. IAP can only be enabled for Cloud Run services (create_job = false). EOT | bool | | false | +| [iap_http_resource_accessors_config](variables.tf#L142) | <<-EOT IAP HTTP resource accessors configuration. When authoritative_mode is true, the google_iap_web_cloud_run_service_iam_binding resource is used which replaces any existing IAM policy attached to the IAP web service. When authoritative_mode is false (default), the google_iap_web_cloud_run_service_iam_member resource is used which adds the IAM policies to the service. EOT | object({…}) | | null | +| [ingress](variables.tf#L166) | Ingress settings. | string | | null | +| [invoker_iam_disabled](variables.tf#L183) | Disables IAM permission check for run.routes.invoke for callers of this service. | bool | | false | +| [labels](variables.tf#L189) | Resource labels. | map(string) | | {} | +| [launch_stage](variables.tf#L195) | The launch stage as defined by Google Cloud Platform Launch Stages. | string | | null | +| [managed_revision](variables.tf#L216) | Whether the Terraform module should control the deployment of revisions. | bool | | true | +| [prefix](variables.tf#L228) | Optional prefix used for resource names. | string | | null | +| [revision](variables.tf#L248) | Revision template configurations. | object({…}) | | {} | +| [service_account](variables.tf#L287) | Service account email. Unused if service account is auto-created. | string | | null | +| [service_account_create](variables.tf#L293) | Auto-create service account. | bool | | false | +| [tag_bindings](variables.tf#L299) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} | +| [volumes](variables.tf#L306) | Named volumes in containers in name => attributes format. | map(object({…})) | | {} | | [vpc_connector_create](variables-vpcconnector.tf#L17) | Populate this to create a Serverless VPC Access connector. | object({…}) | | null | ## Outputs diff --git a/modules/cloud-run-v2/service.tf b/modules/cloud-run-v2/service.tf index a9b1e0fea..808b9ecd2 100644 --- a/modules/cloud-run-v2/service.tf +++ b/modules/cloud-run-v2/service.tf @@ -30,6 +30,7 @@ resource "google_cloud_run_v2_service" "service" { launch_stage = var.launch_stage custom_audiences = var.custom_audiences deletion_protection = var.deletion_protection + iap_enabled = var.iap_enabled template { labels = var.revision.labels @@ -274,6 +275,7 @@ resource "google_cloud_run_v2_service" "service_unmanaged" { launch_stage = var.launch_stage custom_audiences = var.custom_audiences deletion_protection = var.deletion_protection + iap_enabled = var.iap_enabled template { labels = var.revision.labels @@ -522,3 +524,39 @@ resource "google_cloud_run_v2_service_iam_binding" "binding" { ) ) } + +locals { + use_iap_member = ( + !var.create_job && + var.iap_http_resource_accessors_config != null && + !var.iap_http_resource_accessors_config.authoritative_mode + ) + + iap_member_list = local.use_iap_member ? toset(var.iap_http_resource_accessors_config.iam_emails) : [] + + use_iap_iam_binding = ( + !var.create_job && + var.iap_http_resource_accessors_config != null && + var.iap_http_resource_accessors_config.authoritative_mode + ) + iap_binding_dict = local.use_iap_iam_binding ? { "iap" = var.iap_http_resource_accessors_config.iam_emails } : {} + +} + +resource "google_iap_web_cloud_run_service_iam_member" "member" { + for_each = local.iap_member_list + project = local.service.project + location = local.service.location + cloud_run_service_name = local.service.name + role = "roles/iap.httpsResourceAccessor" + member = each.key +} + +resource "google_iap_web_cloud_run_service_iam_binding" "binding" { + for_each = local.iap_binding_dict + project = local.service.project + location = local.service.location + cloud_run_service_name = local.service.name + role = "roles/iap.httpsResourceAccessor" + members = each.value +} \ No newline at end of file diff --git a/modules/cloud-run-v2/variables.tf b/modules/cloud-run-v2/variables.tf index 61a9ab95c..15649e55f 100644 --- a/modules/cloud-run-v2/variables.tf +++ b/modules/cloud-run-v2/variables.tf @@ -125,6 +125,44 @@ variable "iam" { default = {} } +variable "iap_enabled" { + description = <<-EOT + Enables Identity-Aware Proxy (IAP) for this service. + IAP can only be enabled for Cloud Run services (create_job = false). + EOT + type = bool + default = false + + validation { + condition = !var.iap_enabled || (var.iap_enabled && !var.create_job) + error_message = "IAP can only be enabled for Cloud Run services (create_job = false), not for jobs." + } +} + +variable "iap_http_resource_accessors_config" { + description = <<-EOT + IAP HTTP resource accessors configuration. + When authoritative_mode is true, the google_iap_web_cloud_run_service_iam_binding resource is used + which replaces any existing IAM policy attached to the IAP web service. + When authoritative_mode is false (default), the google_iap_web_cloud_run_service_iam_member resource is used + which adds the IAM policies to the service. + EOT + type = object({ + iam_emails = list(string) + authoritative_mode = optional(bool, false) + }) + default = null + + validation { + condition = var.iap_http_resource_accessors_config == null || var.iap_enabled + error_message = "iap_http_resource_accessors_config can only be set when iap_enabled = true." + } + validation { + condition = var.iap_http_resource_accessors_config == null || (var.iap_http_resource_accessors_config != null && length(var.iap_http_resource_accessors_config.iam_emails) > 0) + error_message = "When iap_http_resource_accessors_config is set, iam_emails must not be empty." + } +} + variable "ingress" { description = "Ingress settings." type = string @@ -169,6 +207,10 @@ variable "launch_stage" { BETA, GA, DEPRECATED. EOF } + validation { + condition = !var.iap_enabled || (var.iap_enabled && var.launch_stage == "BETA") + error_message = "When IAP is enabled (iap_enabled = true), launch_stage must be set to 'BETA'." + } } variable "managed_revision" {