diff --git a/fast/stages/2-project-factory/schemas/project.schema.md b/fast/stages/2-project-factory/schemas/project.schema.md
index 2f7583511..8782dbddd 100644
--- a/fast/stages/2-project-factory/schemas/project.schema.md
+++ b/fast/stages/2-project-factory/schemas/project.schema.md
@@ -173,7 +173,7 @@
- **member**: *string*
*pattern: ^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|[a-z])*
- **role**: *string*
-
*pattern: ^[a-zA-Z0-9_/]+$*
+
*pattern: ^[a-zA-Z0-9_/.]+$*
- **condition**: *object*
*additional properties: false*
- ⁺**expression**: *string*
diff --git a/modules/cloud-run-v2/README.md b/modules/cloud-run-v2/README.md
index 5c577f528..86c4f1a68 100644
--- a/modules/cloud-run-v2/README.md
+++ b/modules/cloud-run-v2/README.md
@@ -14,6 +14,7 @@ Cloud Run Services and Jobs, with support for IAM roles and Eventarc trigger cre
- [Eventarc triggers](#eventarc-triggers)
- [PubSub](#pubsub)
- [Audit logs](#audit-logs)
+ - [GCS bucket](#gcs-bucket)
- [Using custom service accounts for triggers](#using-custom-service-accounts-for-triggers)
- [Cloud Run Invoker IAM Disable](#cloud-run-invoker-iam-disable)
- [Cloud Run Service Account](#cloud-run-service-account)
@@ -612,6 +613,34 @@ module "cloud_run" {
# tftest modules=1 resources=4 inventory=service-eventarc-auditlogs-sa-create.yaml
```
+### GCS bucket
+
+This deploys a Cloud Run service that will be triggered when files are uploaded to a GCS bucket.
+
+```hcl
+module "cloud_run" {
+ source = "./fabric/modules/cloud-run-v2"
+ project_id = var.project_id
+ region = var.region
+ name = "hello"
+ containers = {
+ hello = {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ }
+ }
+ eventarc_triggers = {
+ storage = {
+ bucket-upload = {
+ bucket = module.gcs.name
+ path = "/webhook" # optional: URL path for the Cloud Run service
+ }
+ }
+ }
+ deletion_protection = false
+}
+# tftest modules=2 resources=4 fixtures=fixtures/gcs.tf inventory=service-eventarc-storage.yaml e2e
+```
+
### Using custom service accounts for triggers
By default `Compute default service account` is used to trigger Cloud Run. If you want to use custom Service Accounts you can either provide your own in `eventarc_triggers.service_account_email` or set `eventarc_triggers.service_account_create` to true and service account named `tf-cr-trigger-${var.name}` will be created with `roles/run.invoker` granted on this Cloud Run service.
@@ -666,6 +695,33 @@ module "cloud_run" {
# tftest modules=2 resources=6 fixtures=fixtures/pubsub.tf inventory=service-eventarc-pubsub-sa-create.yaml e2e
```
+Example using automatically created service account for storage triggers:
+
+```hcl
+module "cloud_run" {
+ source = "./fabric/modules/cloud-run-v2"
+ project_id = var.project_id
+ region = var.region
+ name = "hello"
+ containers = {
+ hello = {
+ image = "us-docker.pkg.dev/cloudrun/container/hello"
+ }
+ }
+ eventarc_triggers = {
+ storage = {
+ bucket-upload = {
+ bucket = module.gcs.name
+ path = "/webhook" # optional: URL path for the Cloud Run service
+ }
+ }
+ service_account_create = true
+ }
+ deletion_protection = false
+}
+# tftest modules=2 resources=6 fixtures=fixtures/gcs.tf inventory=service-eventarc-storage-sa-create.yaml e2e
+```
+
## Cloud Run Invoker IAM Disable
To disables IAM permission check for `run.routes.invoke` for callers of this service set the `invoker_iam_disabled` variable of the module to `true` (default `false`). There should be no requirement to pass the `roles/run.invoker` to the IAM block to enable public access. This allows for the org policy `domain restricted sharing` org policy remain enabled.
@@ -842,28 +898,28 @@ module "cloud_run" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [name](variables.tf#L206) | Name used for Cloud Run service. | string | ✓ | |
-| [project_id](variables.tf#L221) | Project id used for all resources. | string | ✓ | |
-| [region](variables.tf#L226) | Region used for all resources. | string | ✓ | |
+| [name](variables.tf#L210) | Name used for Cloud Run service. | string | ✓ | |
+| [project_id](variables.tf#L225) | Project id used for all resources. | string | ✓ | |
+| [region](variables.tf#L230) | 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 |
| [deletion_protection](variables.tf#L92) | Deletion protection setting for this Cloud Run service. | string | | null |
| [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)) | | {} |
-| [iap_config](variables.tf#L128) | If present, turns on Identity-Aware Proxy (IAP) for the Cloud Run service. | object({…}) | | null |
-| [ingress](variables.tf#L153) | Ingress settings. | string | | null |
-| [invoker_iam_disabled](variables.tf#L170) | Disables IAM permission check for run.routes.invoke for callers of this service. | bool | | false |
-| [labels](variables.tf#L176) | Resource labels. | map(string) | | {} |
-| [launch_stage](variables.tf#L182) | The launch stage as defined by Google Cloud Platform Launch Stages. | string | | null |
-| [managed_revision](variables.tf#L199) | Whether the Terraform module should control the deployment of revisions. | bool | | true |
-| [prefix](variables.tf#L211) | Optional prefix used for resource names. | string | | null |
-| [revision](variables.tf#L231) | Revision template configurations. | object({…}) | | {} |
-| [service_account](variables.tf#L270) | Service account email. Unused if service account is auto-created. | string | | null |
-| [service_account_create](variables.tf#L276) | Auto-create service account. | bool | | false |
-| [tag_bindings](variables.tf#L282) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} |
-| [volumes](variables.tf#L289) | Named volumes in containers in name => attributes format. | map(object({…})) | | {} |
+| [eventarc_triggers](variables.tf#L104) | Event arc triggers for different sources. | object({…}) | | {} |
+| [iam](variables.tf#L126) | IAM bindings for Cloud Run service in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
+| [iap_config](variables.tf#L132) | If present, turns on Identity-Aware Proxy (IAP) for the Cloud Run service. | object({…}) | | null |
+| [ingress](variables.tf#L157) | Ingress settings. | string | | null |
+| [invoker_iam_disabled](variables.tf#L174) | Disables IAM permission check for run.routes.invoke for callers of this service. | bool | | false |
+| [labels](variables.tf#L180) | Resource labels. | map(string) | | {} |
+| [launch_stage](variables.tf#L186) | The launch stage as defined by Google Cloud Platform Launch Stages. | string | | null |
+| [managed_revision](variables.tf#L203) | Whether the Terraform module should control the deployment of revisions. | bool | | true |
+| [prefix](variables.tf#L215) | Optional prefix used for resource names. | string | | null |
+| [revision](variables.tf#L235) | Revision template configurations. | object({…}) | | {} |
+| [service_account](variables.tf#L274) | Service account email. Unused if service account is auto-created. | string | | null |
+| [service_account_create](variables.tf#L280) | Auto-create service account. | bool | | false |
+| [tag_bindings](variables.tf#L286) | Tag bindings for this service, in key => tag value id format. | map(string) | | {} |
+| [volumes](variables.tf#L293) | 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
@@ -884,6 +940,7 @@ module "cloud_run" {
## Fixtures
- [cloudsql-instance.tf](../../tests/fixtures/cloudsql-instance.tf)
+- [gcs.tf](../../tests/fixtures/gcs.tf)
- [iam-service-account.tf](../../tests/fixtures/iam-service-account.tf)
- [pubsub.tf](../../tests/fixtures/pubsub.tf)
- [secret-credentials.tf](../../tests/fixtures/secret-credentials.tf)
diff --git a/modules/cloud-run-v2/main.tf b/modules/cloud-run-v2/main.tf
index 84212ba07..d92a1697b 100644
--- a/modules/cloud-run-v2/main.tf
+++ b/modules/cloud-run-v2/main.tf
@@ -128,6 +128,29 @@ resource "google_eventarc_trigger" "pubsub_triggers" {
service_account = local.trigger_sa_email
}
+resource "google_eventarc_trigger" "storage_triggers" {
+ for_each = coalesce(var.eventarc_triggers.storage, tomap({}))
+ name = "${local.prefix}storage-${each.key}"
+ location = google_cloud_run_v2_service.service[0].location
+ project = google_cloud_run_v2_service.service[0].project
+ matching_criteria {
+ attribute = "type"
+ value = "google.cloud.storage.object.v1.finalized"
+ }
+ matching_criteria {
+ attribute = "bucket"
+ value = each.value.bucket
+ }
+ destination {
+ cloud_run_service {
+ service = google_cloud_run_v2_service.service[0].name
+ region = google_cloud_run_v2_service.service[0].location
+ path = try(each.value.path, null)
+ }
+ }
+ service_account = local.trigger_sa_email
+}
+
resource "google_service_account" "trigger_service_account" {
count = local.trigger_sa_create ? 1 : 0
project = var.project_id
diff --git a/modules/cloud-run-v2/variables.tf b/modules/cloud-run-v2/variables.tf
index 1de08de08..d270a6799 100644
--- a/modules/cloud-run-v2/variables.tf
+++ b/modules/cloud-run-v2/variables.tf
@@ -108,7 +108,11 @@ variable "eventarc_triggers" {
method = string
service = string
})))
- pubsub = optional(map(string))
+ pubsub = optional(map(string))
+ storage = optional(map(object({
+ bucket = string
+ path = optional(string)
+ })))
service_account_email = optional(string)
service_account_create = optional(bool, false)
})
diff --git a/modules/dataproc/README.md b/modules/dataproc/README.md
index 15bc157b6..17d124c05 100644
--- a/modules/dataproc/README.md
+++ b/modules/dataproc/README.md
@@ -242,7 +242,7 @@ module "processing-dp-cluster" {
}
}
}
-# tftest modules=5 resources=9 fixtures=fixtures/gke-cluster-standard.tf e2e
+# tftest modules=5 resources=9 fixtures=fixtures/gke-cluster-standard.tf
```
## IAM
diff --git a/modules/project-factory/schemas/project.schema.json b/modules/project-factory/schemas/project.schema.json
index a03c697d0..105620464 100644
--- a/modules/project-factory/schemas/project.schema.json
+++ b/modules/project-factory/schemas/project.schema.json
@@ -249,7 +249,7 @@
}
}
}
- },
+ },
"parent": {
"type": "string"
},
@@ -616,7 +616,7 @@
},
"role": {
"type": "string",
- "pattern": "^[a-zA-Z0-9_/]+$"
+ "pattern": "^[a-zA-Z0-9_/.]+$"
},
"condition": {
"type": "object",
diff --git a/tests/fixtures/gke-cluster-standard.tf b/tests/fixtures/gke-cluster-standard.tf
index 9de8a05a8..3b0425ca5 100644
--- a/tests/fixtures/gke-cluster-standard.tf
+++ b/tests/fixtures/gke-cluster-standard.tf
@@ -41,7 +41,8 @@ module "gke-cluster-standard" {
kubelet_readonly_port_enabled = false
}
node_pool_auto_config = {
- network_tags = ["foo"] # to avoid perma-diff
+ network_tags = ["foo"] # to avoid perma-diff
+ kubelet_readonly_port_enabled = false
}
}
diff --git a/tests/modules/cloud_run_v2/examples/service-eventarc-storage-sa-create.yaml b/tests/modules/cloud_run_v2/examples/service-eventarc-storage-sa-create.yaml
new file mode 100644
index 000000000..6b532fa56
--- /dev/null
+++ b/tests/modules/cloud_run_v2/examples/service-eventarc-storage-sa-create.yaml
@@ -0,0 +1,73 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ module.cloud_run.google_cloud_run_v2_service.service[0]:
+ location: europe-west8
+ name: hello
+ project: project-id
+ template:
+ - containers:
+ - args: null
+ command: null
+ depends_on: null
+ env: []
+ image: us-docker.pkg.dev/cloudrun/container/hello
+ name: hello
+ volume_mounts: []
+ working_dir: null
+ execution_environment: EXECUTION_ENVIRONMENT_GEN1
+ volumes: []
+ vpc_access: []
+
+ module.cloud_run.google_eventarc_trigger.storage_triggers["bucket-upload"]:
+ destination:
+ - cloud_run_service:
+ - path: /webhook
+ region: europe-west8
+ service: hello
+ location: europe-west8
+ matching_criteria:
+ - attribute: bucket
+ operator: ''
+ value: test-my-bucket
+ - attribute: type
+ operator: ''
+ value: google.cloud.storage.object.v1.finalized
+ name: storage-bucket-upload
+ project: project-id
+
+ module.cloud_run.google_service_account.trigger_service_account[0]:
+ account_id: tf-cr-trigger-hello
+ description: null
+ disabled: false
+ display_name: Terraform trigger for Cloud Run hello.
+ project: project-id
+ timeouts: null
+
+ module.cloud_run.google_cloud_run_v2_service_iam_member.default[0]:
+ condition: []
+ member: serviceAccount:tf-cr-trigger-hello@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/run.invoker
+
+counts:
+ google_cloud_run_v2_service: 1
+ google_cloud_run_v2_service_iam_member: 1
+ google_eventarc_trigger: 1
+ google_service_account: 1
+ modules: 2
+ resources: 6
+
+outputs: {}
diff --git a/tests/modules/cloud_run_v2/examples/service-eventarc-storage.yaml b/tests/modules/cloud_run_v2/examples/service-eventarc-storage.yaml
new file mode 100644
index 000000000..e3da8d02d
--- /dev/null
+++ b/tests/modules/cloud_run_v2/examples/service-eventarc-storage.yaml
@@ -0,0 +1,58 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+values:
+ module.cloud_run.google_cloud_run_v2_service.service[0]:
+ location: europe-west8
+ name: hello
+ project: project-id
+ template:
+ - containers:
+ - args: null
+ command: null
+ depends_on: null
+ env: []
+ image: us-docker.pkg.dev/cloudrun/container/hello
+ name: hello
+ volume_mounts: []
+ working_dir: null
+ execution_environment: EXECUTION_ENVIRONMENT_GEN1
+ volumes: []
+ vpc_access: []
+
+ module.cloud_run.google_eventarc_trigger.storage_triggers["bucket-upload"]:
+ destination:
+ - cloud_run_service:
+ - path: /webhook
+ region: europe-west8
+ service: hello
+ location: europe-west8
+ matching_criteria:
+ - attribute: bucket
+ operator: ''
+ value: test-my-bucket
+ - attribute: type
+ operator: ''
+ value: google.cloud.storage.object.v1.finalized
+ name: storage-bucket-upload
+ project: project-id
+ service_account: null
+
+counts:
+ google_cloud_run_v2_service: 1
+ google_eventarc_trigger: 1
+ modules: 2
+ resources: 4
+
+outputs: {}