diff --git a/CHANGELOG.md b/CHANGELOG.md
index 099ed16dc..0f57791cb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ All notable changes to this project will be documented in this file.
### BREAKING CHANGES
+- `modules/cloud-function-v1`: service_account and service_account_create were moved to service_account_config. By default, module now creates a service accounts and grants `roles/logging.logWriter` and `roles/monitoring.metricWriter` on project level
+`modules/cloud-function-v2`: service_account and service_account_create were moved to service_account_config. By default, module now creates a service accounts and grants `roles/logging.logWriter` and `roles/monitoring.metricWriter` on project level [[#3443](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3443)]
- `terraform-provider-google`: Bump provider to 7.6.0, to allow use of `google_vertex_ai_reasoning_engine` in modules/agent-engine [[#3429](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3429)]
- `modules/project-factory`: the `factories_config` attribute has been removed from project defaults and overrides. [[#3440](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3440)]
- `modules/gke-hub`: Unified cluster configuration. The module now uses a single `clusters` variable to configure both cluster registration and feature enablement. [[#3332](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3332)]
@@ -14,11 +16,15 @@ All notable changes to this project will be documented in this file.
- `all modules`: Minimum supported OpenTofu version bumped 1.10.0 [[#3332](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3332)]
- `modules/project-factory`: the format for automation service account names has changed. [[#3345](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3345)]
+- [[#3456](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3456)] More knowledge - ([wiktorn](https://github.com/wiktorn))
+- [[#3455](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3455)] Service agents cursed knowledge ([juliocc](https://github.com/juliocc))
+- [[#3446](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3446)] Cursed knowledge of CFF ([wiktorn](https://github.com/wiktorn))
- [[#3428](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3428)] gitignore update ([juliocc](https://github.com/juliocc))
- [[#3361](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3361)] Use pre-commit managed Python environment for pre-commit checks ([wiktorn](https://github.com/wiktorn))
### FAST
+- [[#3443](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3443)] Cloud Function v1, v2 - contexts and service_account_config ([wiktorn](https://github.com/wiktorn))
- [[#3429](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3429)] Add Agent Engine module. ([LucaPrete](https://github.com/LucaPrete))
- [[#3440](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3440)] Support resource-level factories config in project factory module and FAST stages ([ludoo](https://github.com/ludoo))
- [[#3439](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3439)] compatiblity fix: Github CICD templates Terraform version bump to 1.12.2 ([ysolt](https://github.com/ysolt))
@@ -43,6 +49,8 @@ All notable changes to this project will be documented in this file.
### MODULES
+- [[#3443](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3443)] Cloud Function v1, v2 - contexts and service_account_config ([wiktorn](https://github.com/wiktorn))
+- [[#3448](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3448)] Skip IAM grants for service agents that are not created on API activation ([juliocc](https://github.com/juliocc))
- [[#3445](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3445)] Remove Netsec Authz Service Agent ([juliocc](https://github.com/juliocc))
- [[#3429](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3429)] Add Agent Engine module. ([LucaPrete](https://github.com/LucaPrete))
- [[#3438](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3438)] Add PAM support ([juliocc](https://github.com/juliocc))
@@ -82,6 +90,8 @@ All notable changes to this project will be documented in this file.
### TOOLS
+- [[#3443](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3443)] Cloud Function v1, v2 - contexts and service_account_config ([wiktorn](https://github.com/wiktorn))
+- [[#3448](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3448)] Skip IAM grants for service agents that are not created on API activation ([juliocc](https://github.com/juliocc))
- [[#3445](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3445)] Remove Netsec Authz Service Agent ([juliocc](https://github.com/juliocc))
- [[#3429](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3429)] Add Agent Engine module. ([LucaPrete](https://github.com/LucaPrete))
- [[#3444](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/3444)] fix Terraform version linter ([wiktorn](https://github.com/wiktorn))
diff --git a/CURSED_KNOWLEDGE.md b/CURSED_KNOWLEDGE.md
index d33083556..a228231a0 100644
--- a/CURSED_KNOWLEDGE.md
+++ b/CURSED_KNOWLEDGE.md
@@ -2,7 +2,24 @@
+* 2025-10-23 Some [service agents](https://cloud.google.com/iam/docs/service-agents) are not created upon API activation nor calling `google_project_service_identity`. Since we have no way of knowing if they exist, we avoid automatically granting their respective roles in the project module. The list of agents for which we do not perform automatic grants can be found in the [tools/build_service_agents.py](./tools/build_service_agents.py) script.
+* 2025-10-23 Use `terraform plan` after `terraform apply` to confirm that the plan is empty after applying the changes. Non-empty plan is a sign of potential bug in either Terraform code or provider and suggests, that configuration might not have been applied as expected or potential problems when implementing future changes
+* 2025-10-23 Do not use `data` resource. Even if you must, then still it might be [a bad idea](#avoid-data-resources)
+* 2025-10-23 when referring other resource prefer using `.id` attribute over names. `.id` is computed field, and will force update when referred resource is replaced. Sometimes this requires explicit `depends_on` - for example for Cloud Run IAM, so it is recreated when parent resource is replaced
+* 2025-10-23 Maps are the best drivers for `for_each` on the resource level. When using lists, and adding something in the middle of list means that all resources following insertion needs to be replaced
* 2025-10-21 Type checking in ternaries requires both sides to have identical types. For objects, it means that they need to define the same fields. And sometimes `null` and `tonumber(null)` don't converge to a common type (citation needed)
-* 2025-10-21 You can't call a provider function within brackets [opentofu#3401](https://github.com/opentofu/opentofu/issues/3401)
-* 2025-10-21 Terraform dependency graph considers a variable or a local as one node in the graph [adrs/20251013-context-locals.md], you may resolve your dependency cycles by just rearranging your variables / locals.
+* 2025-10-21 Terraform dependency graph considers a variable or a local as one node in the graph [adrs/20251013-context-locals.md], you may resolve your dependency cycles by just rearranging your variables / locals. But for resources - the dependency is tracked on attribute level and plan may differ depending on which attribute you depend
* 2025-10-21 `create_before_destory` meta-argument is [contagious](https://github.com/hashicorp/terraform/blob/main/docs/destroying.md#forced-create-before-destroy), which means - any resource that any resource depending on CBD resource will also be marked as CBD. This hits hard, when affected resource is silently accepting creation with the same name, even if the object exists (`google_storage_bucket_object`, I'm looking at you)
+
+
+## Detailed explanations
+### Avoid `data` resources
+There are two problems, when using `data` resources:
+* when reading is deferred to during apply, any values it returns are also `known after apply`, which may result in unnecessary resource replacement
+* when deploying more complex infrastructure with `data` resources, and your deployment fails in the middle, it might be not possible to recover without manual intervention in what is configured, so `data` resource can read its values
+
+What is considered a safe use case for `data` resource:
+* using it for validating invariants (resource is guaranteed to exist across full lifecycle of the state)
+* using `data` resource outputs in attributes without `ForceNew` flag - so even if `data` will be read during apply, it will result in spurious update-in-place instead of replacement
+
+In Fabric FAST modules `data` resources are used only by request to simplify calling, but then the above caveats apply to the whole module.
diff --git a/fast/project-templates/secops-anonymization-pipeline/README.md b/fast/project-templates/secops-anonymization-pipeline/README.md
index e92978f19..cad6e9dbf 100644
--- a/fast/project-templates/secops-anonymization-pipeline/README.md
+++ b/fast/project-templates/secops-anonymization-pipeline/README.md
@@ -92,7 +92,7 @@ terraform apply
#### Step 5: Test solution
-Test the solution triggering an export from the Cloud Scheduler page, after few hours (accoding to the size of the export) logs should be available on secops-export bucket. Please check for any issue during export using the corresponding APIs and the export ID.
+Test the solution triggering an export from the Cloud Scheduler page, after few hours (according to the size of the export) logs should be available on secops-export bucket. Please check for any issue during export using the corresponding APIs and the export ID.
## Variables
@@ -144,5 +144,5 @@ module "test" {
secondary = "europe-west1"
}
}
-# tftest modules=7 resources=49
+# tftest modules=7 resources=51
```
diff --git a/fast/project-templates/secops-anonymization-pipeline/main.tf b/fast/project-templates/secops-anonymization-pipeline/main.tf
index fc39cc27f..046d49a19 100644
--- a/fast/project-templates/secops-anonymization-pipeline/main.tf
+++ b/fast/project-templates/secops-anonymization-pipeline/main.tf
@@ -123,16 +123,18 @@ module "anonymized-bucket" {
}
module "function" {
- source = "../../../modules/cloud-function-v2"
- project_id = module.project.project_id
- region = var.regions.primary
- prefix = var.prefix
- name = "secops-anonymization"
- bucket_name = "${var.project_id}-anonymization"
- service_account_create = true
- ingress_settings = "ALLOW_INTERNAL_AND_GCLB"
- build_worker_pool = var.cloud_function_config.build_worker_pool_id
- build_service_account = var.cloud_function_config.build_sa != null ? var.cloud_function_config.build_sa : module.cloudbuild-sa.0.id
+ source = "../../../modules/cloud-function-v2"
+ project_id = module.project.project_id
+ region = var.regions.primary
+ prefix = var.prefix
+ name = "secops-anonymization"
+ bucket_name = "${var.project_id}-anonymization"
+ service_account_config = {
+ create = true
+ }
+ ingress_settings = "ALLOW_INTERNAL_AND_GCLB"
+ build_worker_pool = var.cloud_function_config.build_worker_pool_id
+ build_service_account = var.cloud_function_config.build_sa != null ? var.cloud_function_config.build_sa : module.cloudbuild-sa.0.id
bucket_config = {
lifecycle_delete_age_days = 1
}
diff --git a/modules/api-gateway/recipe-multi-region/README.md b/modules/api-gateway/recipe-multi-region/README.md
index 152bb1bf8..e2811f081 100644
--- a/modules/api-gateway/recipe-multi-region/README.md
+++ b/modules/api-gateway/recipe-multi-region/README.md
@@ -43,5 +43,5 @@ module "test" {
number = 1234567890
}
}
-# tftest modules=8 resources=43
+# tftest modules=8 resources=47
```
diff --git a/modules/api-gateway/recipe-multi-region/main.tf b/modules/api-gateway/recipe-multi-region/main.tf
index e48dd364e..25a813b32 100644
--- a/modules/api-gateway/recipe-multi-region/main.tf
+++ b/modules/api-gateway/recipe-multi-region/main.tf
@@ -95,7 +95,9 @@ module "functions" {
entry_point = "helloGET"
runtime = "nodejs22"
}
- service_account_create = true
+ service_account_config = {
+ create = true
+ }
iam = {
"roles/run.invoker" = [module.sa.iam_email]
}
diff --git a/modules/cloud-function-v1/README.md b/modules/cloud-function-v1/README.md
index 551c798a6..158522750 100644
--- a/modules/cloud-function-v1/README.md
+++ b/modules/cloud-function-v1/README.md
@@ -15,6 +15,7 @@ Cloud Function management, with support for IAM roles, optional bucket creation
- [Multiple Cloud Functions within project](#multiple-cloud-functions-within-project)
- [Mounting secrets from Secret Manager](#mounting-secrets-from-secret-manager)
- [Using CMEK to encrypt function resources](#using-cmek-to-encrypt-function-resources)
+- [VPC Access Connector](#vpc-access-connector)
- [Variables](#variables)
- [Outputs](#outputs)
- [Fixtures](#fixtures)
@@ -28,7 +29,7 @@ Cloud Function management, with support for IAM roles, optional bucket creation
### HTTP trigger
-This deploys a Cloud Function with an HTTP endpoint, using a pre-existing GCS bucket for deployment, setting the service account to the Cloud Function default one, and delegating access control to the containing project.
+This deploys a Cloud Function with an HTTP endpoint, using a pre-existing GCS bucket for deployment, creating service account dedicated for this function, granting it `roles/logging.logWriter` and `roles/monitoring.metricWriter` roles, and delegating access control to the containing project.
```hcl
module "cf-http" {
@@ -44,7 +45,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=5 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=http-trigger.yaml e2e
```
### PubSub and non-HTTP triggers
@@ -70,7 +71,7 @@ module "cf-http" {
]
}
-# tftest modules=2 resources=7 fixtures=fixtures/pubsub.tf,fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=pubsub-non-http-trigger.yaml fixtures=fixtures/pubsub.tf,fixtures/functions-default-sa-iam-grants.tf e2e
```
### Controlling HTTP access
@@ -125,7 +126,7 @@ module "cf-http" {
### Service account management
-To use a custom service account managed by the module, set `service_account_create` to `true` and leave `service_account` set to `null` value (default).
+To use a custom service account managed by the module, set `service_account_config.create` to `true`.
```hcl
module "cf-http" {
@@ -137,15 +138,17 @@ module "cf-http" {
bundle_config = {
path = "assets/sample-function/"
}
- service_account_create = true
+ service_account_config = {
+ create = true
+ }
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=6 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=service-account.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
```
-To use an externally managed service account, pass its email in `service_account` and leave `service_account_create` to `false` (the default).
+To use an externally managed service account, pass its email in `service_account_config.email` and set `service_account_config.create` to `false`.
```hcl
module "cf-http" {
@@ -157,7 +160,10 @@ module "cf-http" {
bundle_config = {
path = "assets/sample-function/"
}
- service_account = var.service_account.email
+ service_account_config = {
+ create = false
+ email = var.service_account.email
+ }
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
@@ -193,7 +199,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=5 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=custom-bundle.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
```
### Private Cloud Build Pool
@@ -215,7 +221,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=6 fixtures=fixtures/cloudbuild-custom-pool.tf,fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=private-build-pool.yaml fixtures=fixtures/cloudbuild-custom-pool.tf,fixtures/functions-default-sa-iam-grants.tf e2e
```
### Multiple Cloud Functions within project
@@ -256,6 +262,8 @@ This provides the latest value of the secret `var_secret` as `VARIABLE_SECRET` e
- `/app/secret/ver1` contains version referenced by `module.secret-manager.version_versions["credentials:v1"]`
+Remember to grant access to secrets to the service account running Cloud Function.
+
```hcl
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
@@ -288,7 +296,22 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest fixtures=fixtures/secret-credentials.tf,fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
+
+module "secret-manager" {
+ source = "./fabric/modules/secret-manager"
+ project_id = var.project_id
+ secrets = {
+ credentials = {
+ iam = {
+ "roles/secretmanager.secretAccessor" = [module.cf-http.service_account_iam_email]
+ }
+ versions = {
+ v1 = { data = "manual foo bar spam" }
+ }
+ }
+ }
+}
+# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
```
### Using CMEK to encrypt function resources
@@ -312,7 +335,7 @@ module "project" {
]
iam = {
# grant compute default service account that is used by Cloud Founction
- # permission to read from the buckets so it can function sources
+ # permission to read from the buckets so it can read function sources
"roles/storage.objectViewer" = [
"serviceAccount:${module.project.default_service_accounts.compute}"
]
@@ -369,7 +392,86 @@ module "cf-http" {
repository = module.artifact-registry.id
}
}
-# tftest modules=4 resources=25
+# tftest inventory=cmek.yaml
+```
+
+## VPC Access Connector
+
+You can use an existing [VPC Access Connector](https://cloud.google.com/vpc/docs/serverless-vpc-access) to connect to a VPC from Cloud Run.
+
+```hcl
+module "cf_http" {
+ source = "./fabric/modules/cloud-function-v1"
+ project_id = var.project_id
+ region = var.region
+ name = "test-cf-http"
+ bucket_name = var.bucket
+ bundle_config = {
+ path = "assets/sample-function/"
+ }
+ vpc_connector = {
+ name = google_vpc_access_connector.connector.id
+ egress_setting = "ALL_TRAFFIC"
+ }
+}
+# tftest fixtures=fixtures/vpc-connector.tf inventory=service-vpc-access-connector.yaml e2e
+```
+
+If creation of the VPC Access Connector is required, use the `vpc_connector.create` and `vpc_connector_create` variable which also supports optional attributes like number of instances, machine type, or throughput.
+
+```hcl
+module "cf_http" {
+ source = "./fabric/modules/cloud-function-v1"
+ project_id = var.project_id
+ region = var.region
+ name = "test-cf-http"
+ bucket_name = var.bucket
+ bundle_config = {
+ path = "assets/sample-function/"
+ }
+ vpc_connector = {
+ create = true
+ }
+ vpc_connector_create = {
+ ip_cidr_range = "10.10.10.0/28"
+ network = var.vpc.self_link
+ instances = {
+ max = 10
+ min = 3
+ }
+ }
+}
+# tftest inventory=service-vpc-access-connector-create.yaml e2e
+```
+
+Note that if you are using a Shared VPC for the connector, you need to specify a subnet and the host project if this is not where the Cloud Run service is deployed.
+
+```hcl
+module "cf_http" {
+ source = "./fabric/modules/cloud-function-v1"
+ project_id = var.project_id
+ region = var.region
+ name = "test-cf-http"
+ bucket_name = var.bucket
+ bundle_config = {
+ path = "assets/sample-function/"
+ }
+ vpc_connector = {
+ create = true
+ }
+ vpc_connector_create = {
+ machine_type = "e2-standard-4"
+ subnet = {
+ name = module.net-vpc-host.subnets["${var.region}/fixture-subnet-28"].name
+ project_id = module.project-host.project_id
+ }
+ throughput = {
+ max = 300
+ min = 200
+ }
+ }
+}
+# tftest fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e
```
## Variables
@@ -378,28 +480,28 @@ module "cf-http" {
|---|---|:---:|:---:|:---:|
| [bucket_name](variables.tf#L27) | Name of the bucket that will be used for the function code. It will be created with prefix prepended if bucket_config is not null. | string | ✓ | |
| [bundle_config](variables.tf#L45) | Cloud function source. Path can point to a GCS object URI, or a local path. A local path to a zip archive will generate a GCS object using its basename, a folder will be zipped and the GCS object name inferred when not specified. | object({…}) | ✓ | |
-| [name](variables.tf#L140) | Name used for cloud function and associated resources. | string | ✓ | |
-| [project_id](variables.tf#L155) | Project id used for all resources. | string | ✓ | |
-| [region](variables.tf#L160) | Region used for all resources. | string | ✓ | |
+| [name](variables.tf#L158) | Name used for cloud function and associated resources. | string | ✓ | |
+| [project_id](variables.tf#L173) | Project id used for all resources. | string | ✓ | |
+| [region](variables.tf#L178) | Region used for all resources. | string | ✓ | |
| [bucket_config](variables.tf#L17) | Enable and configure auto-created bucket. Set fields to null to use defaults. | object({…}) | | null |
| [build_environment_variables](variables.tf#L33) | A set of key/value environment variable pairs available during build time. | map(string) | | {} |
| [build_worker_pool](variables.tf#L39) | Build worker pool, in projects//locations//workerPools/ format. | string | | null |
-| [description](variables.tf#L78) | Optional description. | string | | "Terraform managed." |
-| [environment_variables](variables.tf#L84) | Cloud function environment variables. | map(string) | | {} |
-| [function_config](variables.tf#L90) | Cloud function configuration. Defaults to using main as entrypoint, 1 instance with 256MiB of memory, and 180 second timeout. | object({…}) | | {…} |
-| [https_security_level](variables.tf#L110) | The security level for the function: Allowed values are SECURE_ALWAYS, SECURE_OPTIONAL. | string | | null |
-| [iam](variables.tf#L116) | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
-| [ingress_settings](variables.tf#L122) | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL, ALLOW_INTERNAL_AND_GCLB and ALLOW_INTERNAL_ONLY . | string | | null |
-| [kms_key](variables.tf#L128) | Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources in key id format. If specified, you must also provide an artifact registry repository using the docker_repository field that was created with the same KMS crypto key. | string | | null |
-| [labels](variables.tf#L134) | Resource labels. | map(string) | | {} |
-| [prefix](variables.tf#L145) | Optional prefix used for resource names. | string | | null |
-| [repository_settings](variables.tf#L165) | Docker Registry to use for storing the function's Docker images and specific repository. If kms_key is provided, the repository must have already been encrypted with the key. | object({…}) | | {…} |
-| [secrets](variables.tf#L176) | Secret Manager secrets. Key is the variable name or mountpoint, volume versions are in version:path format. | map(object({…})) | | {} |
-| [service_account](variables.tf#L188) | Service account email. Unused if service account is auto-created. | string | | null |
-| [service_account_create](variables.tf#L194) | Auto-create service account. | bool | | false |
-| [trigger_config](variables.tf#L200) | Function trigger configuration. Leave null for HTTP trigger. | object({…}) | | null |
-| [vpc_connector](variables.tf#L210) | VPC connector configuration. Set create to 'true' if a new connector needs to be created. | object({…}) | | null |
-| [vpc_connector_config](variables.tf#L220) | VPC connector network configuration. Must be provided if new VPC connector is being created. | object({…}) | | null |
+| [context](variables.tf#L78) | Context-specific interpolations. | object({…}) | | {} |
+| [description](variables.tf#L96) | Optional description. | string | | "Terraform managed." |
+| [environment_variables](variables.tf#L102) | Cloud function environment variables. | map(string) | | {} |
+| [function_config](variables.tf#L108) | Cloud function configuration. Defaults to using main as entrypoint, 1 instance with 256MiB of memory, and 180 second timeout. | object({…}) | | {…} |
+| [https_security_level](variables.tf#L128) | The security level for the function: Allowed values are SECURE_ALWAYS, SECURE_OPTIONAL. | string | | null |
+| [iam](variables.tf#L134) | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
+| [ingress_settings](variables.tf#L140) | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL, ALLOW_INTERNAL_AND_GCLB and ALLOW_INTERNAL_ONLY . | string | | null |
+| [kms_key](variables.tf#L146) | Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources in key id format. If specified, you must also provide an artifact registry repository using the docker_repository field that was created with the same KMS crypto key. | string | | null |
+| [labels](variables.tf#L152) | Resource labels. | map(string) | | {} |
+| [prefix](variables.tf#L163) | Optional prefix used for resource names. | string | | null |
+| [repository_settings](variables.tf#L183) | Docker Registry to use for storing the function's Docker images and specific repository. If kms_key is provided, the repository must have already been encrypted with the key. | object({…}) | | {…} |
+| [secrets](variables.tf#L194) | Secret Manager secrets. Key is the variable name or mountpoint, volume versions are in version:path format. | map(object({…})) | | {} |
+| [service_account_config](variables-serviceaccount.tf#L17) | Service account configurations. | object({…}) | | {} |
+| [trigger_config](variables.tf#L206) | Function trigger configuration. Leave null for HTTP trigger. | object({…}) | | null |
+| [vpc_connector](variables-vpcconnector.tf#L17) | VPC connector configuration. Set create to 'true' if a new connector needs to be created. | object({…}) | | {} |
+| [vpc_connector_create](variables-vpcconnector.tf#L28) | VPC connector network configuration. Must be provided if new VPC connector is being created. | object({…}) | | null |
## Outputs
@@ -421,5 +523,6 @@ module "cf-http" {
- [cloudbuild-custom-pool.tf](../../tests/fixtures/cloudbuild-custom-pool.tf)
- [functions-default-sa-iam-grants.tf](../../tests/fixtures/functions-default-sa-iam-grants.tf)
- [pubsub.tf](../../tests/fixtures/pubsub.tf)
-- [secret-credentials.tf](../../tests/fixtures/secret-credentials.tf)
+- [shared-vpc.tf](../../tests/fixtures/shared-vpc.tf)
+- [vpc-connector.tf](../../tests/fixtures/vpc-connector.tf)
diff --git a/modules/cloud-function-v1/bundle.tf b/modules/cloud-function-v1/bundle.tf
index 661ece842..97e9927e7 100644
--- a/modules/cloud-function-v1/bundle.tf
+++ b/modules/cloud-function-v1/bundle.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2024 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,13 +29,13 @@ locals {
resource "google_storage_bucket" "bucket" {
count = var.bucket_config == null ? 0 : 1
- project = var.project_id
+ project = local.project_id
name = "${local.prefix}${var.bucket_name}"
uniform_bucket_level_access = true
location = (
var.bucket_config.location == null
- ? var.region
- : var.bucket_config.location
+ ? local.location
+ : lookup(local.ctx.locations, var.bucket_config.location, var.bucket_config.location)
)
labels = var.labels
dynamic "lifecycle_rule" {
@@ -66,7 +66,7 @@ data "archive_file" "bundle" {
output_path = (
var.bundle_config.folder_options.archive_path != null
? pathexpand(var.bundle_config.folder_options.archive_path)
- : "/tmp/bundle-${var.project_id}-${var.name}.zip"
+ : "/tmp/bundle-${local.project_id}-${var.name}.zip"
)
output_file_mode = "0644"
excludes = var.bundle_config.folder_options.excludes
@@ -76,13 +76,20 @@ data "archive_file" "bundle" {
resource "google_storage_bucket_object" "bundle" {
count = local.bundle_type != "gcs" ? 1 : 0
- name = try(
- "bundle-${data.archive_file.bundle[0].output_md5}.zip",
- basename(var.bundle_config.path)
+ name = (
+ local.bundle_type == "local-folder"
+ ? "bundle-${data.archive_file.bundle[0].output_md5}.zip"
+ : basename(var.bundle_config.path)
)
bucket = local.bucket
- source = try(
- data.archive_file.bundle[0].output_path,
- pathexpand(var.bundle_config.path)
+ source = (
+ local.bundle_type == "local-folder"
+ ? data.archive_file.bundle[0].output_path
+ : pathexpand(var.bundle_config.path)
+ )
+ source_md5hash = (
+ local.bundle_type == "local-folder"
+ ? data.archive_file.bundle[0].output_md5
+ : filemd5(pathexpand(var.bundle_config.path))
)
}
diff --git a/modules/cloud-function-v1/main.tf b/modules/cloud-function-v1/main.tf
index e51ded541..4c6cc0f07 100644
--- a/modules/cloud-function-v1/main.tf
+++ b/modules/cloud-function-v1/main.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2022 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,12 @@
*/
locals {
+ _ctx_p = "$"
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local._ctx_p}${k}:${kk}" => vv
+ } if k != "condition_vars"
+ }
bucket = (
var.bucket_config == null
? var.bucket_name
@@ -24,39 +30,15 @@ locals {
: null
)
)
- prefix = var.prefix == null ? "" : "${var.prefix}-"
- service_account_email = (
- var.service_account_create
- ? google_service_account.service_account[0].email
- : var.service_account
- )
- vpc_connector = (
- var.vpc_connector == null
- ? null
- : (
- try(var.vpc_connector.create, false) == false
- ? var.vpc_connector.name
- : google_vpc_access_connector.connector[0].id
- )
- )
-}
-
-resource "google_vpc_access_connector" "connector" {
- count = try(var.vpc_connector.create, false) == true ? 1 : 0
- project = var.project_id
- name = var.vpc_connector.name
- region = var.region
- ip_cidr_range = var.vpc_connector_config.ip_cidr_range
- network = var.vpc_connector_config.network
- max_instances = try(var.vpc_connector_config.instances.max, null)
- min_instances = try(var.vpc_connector_config.instances.min, null)
- max_throughput = try(var.vpc_connector_config.throughput.max, null)
- min_throughput = try(var.vpc_connector_config.throughput.min, null)
+ location = lookup(local.ctx.locations, var.region, var.region)
+ prefix = var.prefix == null ? "" : "${var.prefix}-"
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
+ vpc_connector = var.vpc_connector.create ? google_vpc_access_connector.connector[0].id : var.vpc_connector.name
}
resource "google_cloudfunctions_function" "function" {
- project = var.project_id
- region = var.region
+ project = local.project_id
+ region = local.location
name = "${local.prefix}${var.name}"
description = var.description
runtime = var.function_config.runtime
@@ -72,19 +54,17 @@ resource "google_cloudfunctions_function" "function" {
? replace(var.bundle_config.path, "/^gs:\\/\\/[^\\/]+\\//", "")
: google_storage_bucket_object.bundle[0].name
)
- labels = var.labels
- trigger_http = var.trigger_config == null ? true : null
- https_trigger_security_level = var.https_security_level == null ? "SECURE_ALWAYS" : var.https_security_level
- ingress_settings = var.ingress_settings
- build_worker_pool = var.build_worker_pool
- build_environment_variables = var.build_environment_variables
- kms_key_name = var.kms_key
- docker_registry = try(var.repository_settings.registry, "ARTIFACT_REGISTRY")
- docker_repository = try(var.repository_settings.repository, null)
- vpc_connector = local.vpc_connector
- vpc_connector_egress_settings = try(
- var.vpc_connector.egress_settings, null
- )
+ labels = var.labels
+ trigger_http = var.trigger_config == null ? true : null
+ https_trigger_security_level = var.https_security_level == null ? "SECURE_ALWAYS" : var.https_security_level
+ ingress_settings = var.ingress_settings
+ build_worker_pool = var.build_worker_pool
+ build_environment_variables = var.build_environment_variables
+ kms_key_name = var.kms_key == null ? null : lookup(local.ctx.kms_keys, var.kms_key, var.kms_key)
+ docker_registry = try(var.repository_settings.registry, "ARTIFACT_REGISTRY")
+ docker_repository = try(var.repository_settings.repository, null)
+ vpc_connector = local.vpc_connector
+ vpc_connector_egress_settings = var.vpc_connector.egress_settings
dynamic "event_trigger" {
for_each = var.trigger_config == null ? [] : [""]
@@ -132,16 +112,12 @@ resource "google_cloudfunctions_function" "function" {
resource "google_cloudfunctions_function_iam_binding" "default" {
for_each = var.iam
- project = var.project_id
- region = var.region
+ project = local.project_id
+ region = local.location
cloud_function = google_cloudfunctions_function.function.id
- role = each.key
- members = each.value
-}
-
-resource "google_service_account" "service_account" {
- count = var.service_account_create ? 1 : 0
- project = var.project_id
- account_id = "tf-cf-${var.name}"
- display_name = "Terraform Cloud Function ${var.name}."
+ role = lookup(local.ctx.custom_roles, each.key, each.key)
+ members = [for member in each.value : lookup(local.ctx.iam_principals, member, member)]
+ lifecycle {
+ replace_triggered_by = [google_cloudfunctions_function.function]
+ }
}
diff --git a/modules/cloud-function-v1/serviceaccount.tf b/modules/cloud-function-v1/serviceaccount.tf
new file mode 100644
index 000000000..f1fdec866
--- /dev/null
+++ b/modules/cloud-function-v1/serviceaccount.tf
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2025 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.
+ */
+
+locals {
+ service_account_email = (
+ var.service_account_config.create
+ ? google_service_account.service_account[0].email
+ : lookup(
+ local.ctx.iam_principals,
+ var.service_account_config.email,
+ var.service_account_config.email
+ )
+ )
+ service_account_roles = [
+ for role in var.service_account_config.roles
+ : lookup(local.ctx.custom_roles, role, role)
+ ]
+}
+
+resource "google_service_account" "service_account" {
+ count = var.service_account_config.create ? 1 : 0
+ project = local.project_id
+ account_id = coalesce(var.service_account_config.name, var.name)
+ display_name = coalesce(
+ var.service_account_config.display_name,
+ var.service_account_config.name,
+ var.name
+ )
+}
+
+resource "google_project_iam_member" "default" {
+ for_each = (
+ var.service_account_config.create
+ ? toset(local.service_account_roles)
+ : toset([])
+ )
+ role = each.key
+ project = local.project_id
+ member = google_service_account.service_account[0].member
+}
diff --git a/modules/cloud-function-v1/variables-serviceaccount.tf b/modules/cloud-function-v1/variables-serviceaccount.tf
new file mode 100644
index 000000000..878feed9f
--- /dev/null
+++ b/modules/cloud-function-v1/variables-serviceaccount.tf
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+
+variable "service_account_config" {
+ description = "Service account configurations."
+ type = object({
+ create = optional(bool, true)
+ display_name = optional(string)
+ email = optional(string)
+ name = optional(string)
+ roles = optional(list(string), [
+ "roles/logging.logWriter",
+ "roles/monitoring.metricWriter"
+ ])
+ })
+ nullable = false
+ default = {}
+}
diff --git a/modules/cloud-function-v1/variables-vpcconnector.tf b/modules/cloud-function-v1/variables-vpcconnector.tf
new file mode 100644
index 000000000..967ce6983
--- /dev/null
+++ b/modules/cloud-function-v1/variables-vpcconnector.tf
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2025 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.
+ */
+
+variable "vpc_connector" {
+ description = "VPC connector configuration. Set create to 'true' if a new connector needs to be created."
+ type = object({
+ create = optional(bool, false)
+ name = optional(string)
+ egress_settings = optional(string)
+ })
+ nullable = false
+ default = {}
+}
+
+variable "vpc_connector_create" {
+ description = "VPC connector network configuration. Must be provided if new VPC connector is being created."
+ type = object({
+ ip_cidr_range = optional(string)
+ machine_type = optional(string)
+ name = optional(string)
+ network = optional(string)
+ instances = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }), {}
+ )
+ throughput = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }), {}
+ )
+ subnet = optional(object({
+ name = optional(string)
+ project_id = optional(string)
+ }), {})
+ })
+ default = null
+ validation {
+ condition = (
+ var.vpc_connector.create == false ||
+ try(var.vpc_connector_create.instances, null) != null ||
+ try(var.vpc_connector_create.throughput, null) != null
+ )
+ error_message = "VPC connector must specify either instances or throughput."
+ }
+}
diff --git a/modules/cloud-function-v1/variables.tf b/modules/cloud-function-v1/variables.tf
index 98f1f10af..c089bbb07 100644
--- a/modules/cloud-function-v1/variables.tf
+++ b/modules/cloud-function-v1/variables.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2022 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,6 +75,24 @@ variable "bundle_config" {
}
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ condition_vars = optional(map(map(string)), {}) # not needed here?
+ cidr_ranges = optional(map(string), {})
+ custom_roles = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ kms_keys = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ subnets = optional(map(string), {})
+ tag_values = optional(map(string), {}) # not needed here?
+ })
+ nullable = false
+ default = {}
+}
+
variable "description" {
description = "Optional description."
type = string
@@ -185,18 +203,6 @@ variable "secrets" {
default = {}
}
-variable "service_account" {
- description = "Service account email. Unused if service account is auto-created."
- type = string
- default = null
-}
-
-variable "service_account_create" {
- description = "Auto-create service account."
- type = bool
- default = false
-}
-
variable "trigger_config" {
description = "Function trigger configuration. Leave null for HTTP trigger."
type = object({
@@ -206,38 +212,3 @@ variable "trigger_config" {
})
default = null
}
-
-variable "vpc_connector" {
- description = "VPC connector configuration. Set create to 'true' if a new connector needs to be created."
- type = object({
- create = bool
- name = string
- egress_settings = string
- })
- default = null
-}
-
-variable "vpc_connector_config" {
- description = "VPC connector network configuration. Must be provided if new VPC connector is being created."
- type = object({
- ip_cidr_range = string
- network = string
- instances = optional(object({
- max = optional(number)
- min = optional(number, 2)
- }))
- throughput = optional(object({
- max = optional(number, 300)
- min = optional(number, 200)
- }))
- })
- default = null
- validation {
- condition = (
- var.vpc_connector_config == null ||
- try(var.vpc_connector_config.instances, null) != null ||
- try(var.vpc_connector_config.throughput, null) != null
- )
- error_message = "VPC connector must specify either instances or throughput."
- }
-}
diff --git a/modules/cloud-function-v1/vpcconnector.tf b/modules/cloud-function-v1/vpcconnector.tf
new file mode 100644
index 000000000..2c449132a
--- /dev/null
+++ b/modules/cloud-function-v1/vpcconnector.tf
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2025 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.
+ */
+
+locals {
+ _connector_subnet_name_ctx = (
+ try(var.vpc_connector_create.subnet.name, null) == null ? false :
+ contains(keys(local.ctx.subnets), var.vpc_connector_create.subnet.name)
+ )
+ # if you pass the subnet, you must pass only the name, not the whole id
+ _connector_subnet_name = (
+ local._connector_subnet_name_ctx
+ ? reverse(split("/", local.ctx.subnets[var.vpc_connector_create.subnet.name]))[0]
+ : try(var.vpc_connector_create.subnet.name, null)
+ )
+ # if project is not provided, but subnet is coming from context, use project from subnet id in context
+ # and avoid lookups using null project
+ _connector_subnet_project_input = try(var.vpc_connector_create.subnet.project_id, null)
+ _connector_subnet_project = (
+ local._connector_subnet_project_input == null
+ ? (
+ local._connector_subnet_name_ctx
+ ? split("/", local.ctx.subnets[var.vpc_connector_create.subnet.name])[1]
+ : null
+ )
+ : lookup(
+ local.ctx.project_ids, local._connector_subnet_project_input,
+ local._connector_subnet_project_input
+ )
+ )
+}
+
+resource "google_vpc_access_connector" "connector" {
+ count = var.vpc_connector_create != null ? 1 : 0
+ project = local.project_id
+ name = (
+ var.vpc_connector_create.name != null
+ ? var.vpc_connector_create.name
+ : var.name
+ )
+ region = local.location
+ ip_cidr_range = var.vpc_connector_create.ip_cidr_range == null ? null : lookup(
+ local.ctx.cidr_ranges, var.vpc_connector_create.ip_cidr_range,
+ var.vpc_connector_create.ip_cidr_range
+ )
+ network = var.vpc_connector_create.network == null ? null : lookup(
+ local.ctx.networks, var.vpc_connector_create.network,
+ var.vpc_connector_create.network
+ )
+ machine_type = var.vpc_connector_create.machine_type
+ max_instances = var.vpc_connector_create.instances.max
+ max_throughput = var.vpc_connector_create.throughput.max
+ min_instances = var.vpc_connector_create.instances.min
+ min_throughput = var.vpc_connector_create.throughput.min
+ dynamic "subnet" {
+ for_each = var.vpc_connector_create.subnet.name == null ? [] : [""]
+ content {
+ name = local._connector_subnet_name
+ project_id = local._connector_subnet_project
+ }
+ }
+}
diff --git a/modules/cloud-function-v2/README.md b/modules/cloud-function-v2/README.md
index 0c0fa2c44..9f60b645f 100644
--- a/modules/cloud-function-v2/README.md
+++ b/modules/cloud-function-v2/README.md
@@ -14,6 +14,7 @@ Cloud Function management, with support for IAM roles, optional bucket creation
- [Private Cloud Build Pool](#private-cloud-build-pool)
- [Multiple Cloud Functions within project](#multiple-cloud-functions-within-project)
- [Mounting secrets from Secret Manager](#mounting-secrets-from-secret-manager)
+- [VPC Access Connector](#vpc-access-connector)
- [Variables](#variables)
- [Outputs](#outputs)
- [Fixtures](#fixtures)
@@ -43,7 +44,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=5 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=http-trigger.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
```
### PubSub and non-HTTP triggers
@@ -80,7 +81,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=3 resources=9 fixtures=fixtures/pubsub.tf,fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=pubsub-non-http-trigger.yaml fixtures=fixtures/pubsub.tf,fixtures/functions-default-sa-iam-grants.tf e2e
```
Ensure that pubsub service identity (`service-[project number]@gcp-sa-pubsub.iam.gserviceaccount.com` has `roles/iam.serviceAccountTokenCreator`
@@ -150,12 +151,14 @@ module "cf-http" {
bundle_config = {
path = "assets/sample-function/"
}
- service_account_create = true
+ service_account_config = {
+ create = true
+ }
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=6 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=service-account-1.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
```
To use an externally managed service account, pass its email in `service_account` and leave `service_account_create` to `false` (the default).
@@ -170,12 +173,15 @@ module "cf-http" {
bundle_config = {
path = "assets/sample-function/"
}
- service_account = var.service_account.email
+ service_account_config = {
+ create = false
+ email = var.service_account.email
+ }
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=5 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=service-account-2.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
```
### Custom bundle config
@@ -206,7 +212,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=5 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
+# tftest inventory=custom-bundle.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
```
### Private Cloud Build Pool
@@ -228,7 +234,7 @@ module "cf-http" {
google_project_iam_member.bucket_default_compute_account_grant,
]
}
-# tftest modules=1 resources=6 fixtures=fixtures/functions-default-sa-iam-grants.tf,fixtures/cloudbuild-custom-pool.tf e2e
+# tftest inventory=private-build-pool.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf,fixtures/cloudbuild-custom-pool.tf e2e
```
### Multiple Cloud Functions within project
@@ -269,6 +275,8 @@ This provides the latest value of the secret `var_secret` as `VARIABLE_SECRET` e
- `/app/secret/ver1` contains version referenced by `module.secret-manager.version_versions["credentials:v1"]`
+Remember to grant access to secrets to the service account running Cloud Function.
+
```hcl
module "cf-http" {
source = "./fabric/modules/cloud-function-v2"
@@ -302,7 +310,100 @@ module "cf-http" {
]
}
-# tftest fixtures=fixtures/secret-credentials.tf,fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
+module "secret-manager" {
+ source = "./fabric/modules/secret-manager"
+ project_id = var.project_id
+ secrets = {
+ credentials = {
+ iam = {
+ "roles/secretmanager.secretAccessor" = [module.cf-http.service_account_iam_email]
+ }
+ versions = {
+ v1 = { data = "manual foo bar spam" }
+ }
+ }
+ }
+}
+# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
+```
+
+## VPC Access Connector
+
+You can use an existing [VPC Access Connector](https://cloud.google.com/vpc/docs/serverless-vpc-access) to connect to a VPC from Cloud Run.
+
+```hcl
+module "cf_http" {
+ source = "./fabric/modules/cloud-function-v2"
+ project_id = var.project_id
+ region = var.region
+ name = "test-cf-http"
+ bucket_name = var.bucket
+ bundle_config = {
+ path = "assets/sample-function/"
+ }
+ vpc_connector = {
+ name = google_vpc_access_connector.connector.id
+ egress_setting = "ALL_TRAFFIC"
+ }
+}
+# tftest fixtures=fixtures/vpc-connector.tf inventory=service-vpc-access-connector.yaml e2e
+```
+
+If creation of the VPC Access Connector is required, use the `vpc_connector.create` and `vpc_connector_create` variable which also supports optional attributes like number of instances, machine type, or throughput.
+
+```hcl
+module "cf_http" {
+ source = "./fabric/modules/cloud-function-v2"
+ project_id = var.project_id
+ region = var.region
+ name = "test-cf-http"
+ bucket_name = var.bucket
+ bundle_config = {
+ path = "assets/sample-function/"
+ }
+ vpc_connector = {
+ create = true
+ }
+ vpc_connector_create = {
+ ip_cidr_range = "10.10.10.0/28"
+ network = var.vpc.self_link
+ instances = {
+ max = 10
+ min = 3
+ }
+ }
+}
+# tftest inventory=service-vpc-access-connector-create.yaml e2e
+```
+
+Note that if you are using a Shared VPC for the connector, you need to specify a subnet and the host project if this is not where the Cloud Run service is deployed.
+
+```hcl
+module "cf_http" {
+ source = "./fabric/modules/cloud-function-v2"
+ project_id = var.project_id
+ region = var.region
+ name = "test-cf-http"
+ bucket_name = var.bucket
+ bundle_config = {
+ path = "assets/sample-function/"
+ }
+ vpc_connector = {
+ create = true
+ }
+ vpc_connector_create = {
+ machine_type = "e2-standard-4"
+ subnet = {
+ name = module.net-vpc-host.subnets["${var.region}/fixture-subnet-28"].name
+ project_id = module.project-host.project_id
+ }
+ throughput = {
+ max = 300
+ min = 200
+ }
+ }
+}
+# tftest fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e
```
## Variables
@@ -311,28 +412,28 @@ module "cf-http" {
|---|---|:---:|:---:|:---:|
| [bucket_name](variables.tf#L27) | Name of the bucket that will be used for the function code. It will be created with prefix prepended if bucket_config is not null. | string | ✓ | |
| [bundle_config](variables.tf#L51) | Cloud function source. Path can point to a GCS object URI, or a local path. A local path to a zip archive will generate a GCS object using its basename, a folder will be zipped and the GCS object name inferred when not specified. | object({…}) | ✓ | |
-| [name](variables.tf#L149) | Name used for cloud function and associated resources. | string | ✓ | |
-| [project_id](variables.tf#L164) | Project id used for all resources. | string | ✓ | |
-| [region](variables.tf#L169) | Region used for all resources. | string | ✓ | |
+| [name](variables.tf#L167) | Name used for cloud function and associated resources. | string | ✓ | |
+| [project_id](variables.tf#L182) | Project id used for all resources. | string | ✓ | |
+| [region](variables.tf#L187) | Region used for all resources. | string | ✓ | |
| [bucket_config](variables.tf#L17) | Enable and configure auto-created bucket. Set fields to null to use defaults. | object({…}) | | null |
| [build_environment_variables](variables.tf#L33) | A set of key/value environment variable pairs available during build time. | map(string) | | {} |
| [build_service_account](variables.tf#L39) | Build service account email. | string | | null |
| [build_worker_pool](variables.tf#L45) | Build worker pool, in projects//locations//workerPools/ format. | string | | null |
-| [description](variables.tf#L84) | Optional description. | string | | "Terraform managed." |
-| [docker_repository_id](variables.tf#L90) | User managed repository created in Artifact Registry. | string | | null |
-| [environment_variables](variables.tf#L96) | Cloud function environment variables. | map(string) | | {…} |
-| [function_config](variables.tf#L104) | Cloud function configuration. Defaults to using main as entrypoint, 1 instance with 256MiB of memory, and 180 second timeout. | object({…}) | | {…} |
-| [iam](variables.tf#L125) | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
-| [ingress_settings](variables.tf#L131) | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL, ALLOW_INTERNAL_AND_GCLB and ALLOW_INTERNAL_ONLY . | string | | null |
-| [kms_key](variables.tf#L137) | Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources in key id format. If specified, you must also provide an artifact registry repository using the docker_repository_id field that was created with the same KMS crypto key. | string | | null |
-| [labels](variables.tf#L143) | Resource labels. | map(string) | | {} |
-| [prefix](variables.tf#L154) | Optional prefix used for resource names. | string | | null |
-| [secrets](variables.tf#L174) | Secret Manager secrets. Key is the variable name or mountpoint, volume versions are in version:path format. | map(object({…})) | | {} |
-| [service_account](variables.tf#L186) | Service account email. Unused if service account is auto-created. | string | | null |
-| [service_account_create](variables.tf#L192) | Auto-create service account. | bool | | false |
-| [trigger_config](variables.tf#L198) | Function trigger configuration. Leave null for HTTP trigger. | object({…}) | | null |
-| [vpc_connector](variables.tf#L216) | VPC connector configuration. Set create to 'true' if a new connector needs to be created. | object({…}) | | {} |
-| [vpc_connector_config](variables.tf#L227) | VPC connector network configuration. Must be provided if new VPC connector is being created. | object({…}) | | null |
+| [context](variables.tf#L84) | Context-specific interpolations. | object({…}) | | {} |
+| [description](variables.tf#L102) | Optional description. | string | | "Terraform managed." |
+| [docker_repository_id](variables.tf#L108) | User managed repository created in Artifact Registry. | string | | null |
+| [environment_variables](variables.tf#L114) | Cloud function environment variables. | map(string) | | {…} |
+| [function_config](variables.tf#L122) | Cloud function configuration. Defaults to using main as entrypoint, 1 instance with 256MiB of memory, and 180 second timeout. | object({…}) | | {…} |
+| [iam](variables.tf#L143) | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} |
+| [ingress_settings](variables.tf#L149) | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL, ALLOW_INTERNAL_AND_GCLB and ALLOW_INTERNAL_ONLY . | string | | null |
+| [kms_key](variables.tf#L155) | Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources in key id format. If specified, you must also provide an artifact registry repository using the docker_repository_id field that was created with the same KMS crypto key. | string | | null |
+| [labels](variables.tf#L161) | Resource labels. | map(string) | | {} |
+| [prefix](variables.tf#L172) | Optional prefix used for resource names. | string | | null |
+| [secrets](variables.tf#L192) | Secret Manager secrets. Key is the variable name or mountpoint, volume versions are in version:path format. | map(object({…})) | | {} |
+| [service_account_config](variables-serviceaccount.tf#L17) | Service account configurations. | object({…}) | | {} |
+| [trigger_config](variables.tf#L204) | Function trigger configuration. Leave null for HTTP trigger. | object({…}) | | null |
+| [vpc_connector](variables-vpcconnector.tf#L17) | VPC connector configuration. Set create to 'true' if a new connector needs to be created. | object({…}) | | {} |
+| [vpc_connector_create](variables-vpcconnector.tf#L28) | VPC connector network configuration. Must be provided if new VPC connector is being created. | object({…}) | | null |
## Outputs
@@ -358,5 +459,6 @@ module "cf-http" {
- [cloudbuild-custom-pool.tf](../../tests/fixtures/cloudbuild-custom-pool.tf)
- [functions-default-sa-iam-grants.tf](../../tests/fixtures/functions-default-sa-iam-grants.tf)
- [pubsub.tf](../../tests/fixtures/pubsub.tf)
-- [secret-credentials.tf](../../tests/fixtures/secret-credentials.tf)
+- [shared-vpc.tf](../../tests/fixtures/shared-vpc.tf)
+- [vpc-connector.tf](../../tests/fixtures/vpc-connector.tf)
diff --git a/modules/cloud-function-v2/bundle.tf b/modules/cloud-function-v2/bundle.tf
index 661ece842..97e9927e7 100644
--- a/modules/cloud-function-v2/bundle.tf
+++ b/modules/cloud-function-v2/bundle.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2024 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,13 +29,13 @@ locals {
resource "google_storage_bucket" "bucket" {
count = var.bucket_config == null ? 0 : 1
- project = var.project_id
+ project = local.project_id
name = "${local.prefix}${var.bucket_name}"
uniform_bucket_level_access = true
location = (
var.bucket_config.location == null
- ? var.region
- : var.bucket_config.location
+ ? local.location
+ : lookup(local.ctx.locations, var.bucket_config.location, var.bucket_config.location)
)
labels = var.labels
dynamic "lifecycle_rule" {
@@ -66,7 +66,7 @@ data "archive_file" "bundle" {
output_path = (
var.bundle_config.folder_options.archive_path != null
? pathexpand(var.bundle_config.folder_options.archive_path)
- : "/tmp/bundle-${var.project_id}-${var.name}.zip"
+ : "/tmp/bundle-${local.project_id}-${var.name}.zip"
)
output_file_mode = "0644"
excludes = var.bundle_config.folder_options.excludes
@@ -76,13 +76,20 @@ data "archive_file" "bundle" {
resource "google_storage_bucket_object" "bundle" {
count = local.bundle_type != "gcs" ? 1 : 0
- name = try(
- "bundle-${data.archive_file.bundle[0].output_md5}.zip",
- basename(var.bundle_config.path)
+ name = (
+ local.bundle_type == "local-folder"
+ ? "bundle-${data.archive_file.bundle[0].output_md5}.zip"
+ : basename(var.bundle_config.path)
)
bucket = local.bucket
- source = try(
- data.archive_file.bundle[0].output_path,
- pathexpand(var.bundle_config.path)
+ source = (
+ local.bundle_type == "local-folder"
+ ? data.archive_file.bundle[0].output_path
+ : pathexpand(var.bundle_config.path)
+ )
+ source_md5hash = (
+ local.bundle_type == "local-folder"
+ ? data.archive_file.bundle[0].output_md5
+ : filemd5(pathexpand(var.bundle_config.path))
)
}
diff --git a/modules/cloud-function-v2/main.tf b/modules/cloud-function-v2/main.tf
index c42f81bf1..7905158ca 100644
--- a/modules/cloud-function-v2/main.tf
+++ b/modules/cloud-function-v2/main.tf
@@ -1,5 +1,5 @@
/**
- * Copyright 2024 Google LLC
+ * Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,12 @@
*/
locals {
+ _ctx_p = "$"
+ ctx = {
+ for k, v in var.context : k => {
+ for kk, vv in v : "${local._ctx_p}${k}:${kk}" => vv
+ } if k != "condition_vars"
+ }
bucket = (
var.bucket_config == null
? var.bucket_name
@@ -24,12 +30,9 @@ locals {
: null
)
)
- prefix = var.prefix == null ? "" : "${var.prefix}-"
- service_account_email = (
- var.service_account_create
- ? google_service_account.service_account[0].email
- : var.service_account
- )
+ location = lookup(local.ctx.locations, var.region, var.region)
+ prefix = var.prefix == null ? "" : "${var.prefix}-"
+ project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
trigger_sa_create = (
try(var.trigger_config.service_account_create, false) == true
)
@@ -38,37 +41,16 @@ locals {
var.trigger_config.service_account_email,
null
)
- vpc_connector = (
- var.vpc_connector.name == null
- ? null
- : (
- var.vpc_connector.create == false
- ? var.vpc_connector.name
- : google_vpc_access_connector.connector[0].id
- )
- )
-}
-
-resource "google_vpc_access_connector" "connector" {
- count = var.vpc_connector.create == true ? 1 : 0
- project = var.project_id
- name = var.vpc_connector.name
- region = var.region
- ip_cidr_range = var.vpc_connector_config.ip_cidr_range
- network = var.vpc_connector_config.network
- max_instances = try(var.vpc_connector_config.instances.max, null)
- min_instances = try(var.vpc_connector_config.instances.min, null)
- max_throughput = try(var.vpc_connector_config.throughput.max, null)
- min_throughput = try(var.vpc_connector_config.throughput.min, null)
+ vpc_connector = var.vpc_connector.create ? google_vpc_access_connector.connector[0].id : var.vpc_connector.name
}
resource "google_cloudfunctions2_function" "function" {
provider = google-beta
- project = var.project_id
- location = var.region
+ project = local.project_id
+ location = local.location
name = "${local.prefix}${var.name}"
description = var.description
- kms_key_name = var.kms_key
+ kms_key_name = var.kms_key == null ? null : lookup(local.ctx.kms_keys, var.kms_key, var.kms_key)
build_config {
service_account = var.build_service_account
worker_pool = var.build_worker_pool
@@ -160,33 +142,35 @@ resource "google_cloudfunctions2_function_iam_binding" "binding" {
for_each = {
for k, v in var.iam : k => v if k != "roles/run.invoker"
}
- project = var.project_id
- location = google_cloudfunctions2_function.function.location
+ project = local.project_id
+ location = local.location
cloud_function = google_cloudfunctions2_function.function.name
- role = each.key
- members = each.value
+ role = lookup(local.ctx.custom_roles, each.key, each.key)
+ members = [for member in each.value : lookup(local.ctx.iam_principals, member, member)]
lifecycle {
replace_triggered_by = [google_cloudfunctions2_function.function]
}
}
+locals {
+ run_invoker_members = distinct(compact(concat(
+ !local.trigger_sa_create
+ ? []
+ : ["serviceAccount:${local.trigger_sa_email}"],
+ lookup(var.iam, "roles/run.invoker", []),
+ )))
+}
+
resource "google_cloud_run_service_iam_binding" "invoker" {
# cloud run resources are needed for invoker role to the underlying service
count = (
lookup(var.iam, "roles/run.invoker", null) != null
) ? 1 : 0
- project = var.project_id
- location = google_cloudfunctions2_function.function.location
+ project = local.project_id
+ location = local.location
service = google_cloudfunctions2_function.function.name
role = "roles/run.invoker"
- members = distinct(compact(concat(
- lookup(var.iam, "roles/run.invoker", []),
- (
- !local.trigger_sa_create
- ? []
- : ["serviceAccount:${local.trigger_sa_email}"]
- )
- )))
+ members = [for member in local.run_invoker_members : lookup(local.ctx.iam_principals, member, member)]
lifecycle {
replace_triggered_by = [google_cloudfunctions2_function.function]
}
@@ -199,8 +183,8 @@ resource "google_cloud_run_service_iam_member" "invoker" {
lookup(var.iam, "roles/run.invoker", null) == null &&
local.trigger_sa_create
) ? 1 : 0
- project = var.project_id
- location = google_cloudfunctions2_function.function.location
+ project = local.project_id
+ location = local.location
service = google_cloudfunctions2_function.function.name
role = "roles/run.invoker"
member = "serviceAccount:${local.trigger_sa_email}"
@@ -209,13 +193,6 @@ resource "google_cloud_run_service_iam_member" "invoker" {
}
}
-resource "google_service_account" "service_account" {
- count = var.service_account_create ? 1 : 0
- project = var.project_id
- account_id = "tf-cf-${var.name}"
- display_name = "Terraform Cloud Function ${var.name}."
-}
-
resource "google_service_account" "trigger_service_account" {
count = local.trigger_sa_create ? 1 : 0
project = var.project_id
diff --git a/modules/cloud-function-v2/serviceaccount.tf b/modules/cloud-function-v2/serviceaccount.tf
new file mode 100644
index 000000000..f1fdec866
--- /dev/null
+++ b/modules/cloud-function-v2/serviceaccount.tf
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2025 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.
+ */
+
+locals {
+ service_account_email = (
+ var.service_account_config.create
+ ? google_service_account.service_account[0].email
+ : lookup(
+ local.ctx.iam_principals,
+ var.service_account_config.email,
+ var.service_account_config.email
+ )
+ )
+ service_account_roles = [
+ for role in var.service_account_config.roles
+ : lookup(local.ctx.custom_roles, role, role)
+ ]
+}
+
+resource "google_service_account" "service_account" {
+ count = var.service_account_config.create ? 1 : 0
+ project = local.project_id
+ account_id = coalesce(var.service_account_config.name, var.name)
+ display_name = coalesce(
+ var.service_account_config.display_name,
+ var.service_account_config.name,
+ var.name
+ )
+}
+
+resource "google_project_iam_member" "default" {
+ for_each = (
+ var.service_account_config.create
+ ? toset(local.service_account_roles)
+ : toset([])
+ )
+ role = each.key
+ project = local.project_id
+ member = google_service_account.service_account[0].member
+}
diff --git a/modules/cloud-function-v2/variables-serviceaccount.tf b/modules/cloud-function-v2/variables-serviceaccount.tf
new file mode 100644
index 000000000..878feed9f
--- /dev/null
+++ b/modules/cloud-function-v2/variables-serviceaccount.tf
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+
+variable "service_account_config" {
+ description = "Service account configurations."
+ type = object({
+ create = optional(bool, true)
+ display_name = optional(string)
+ email = optional(string)
+ name = optional(string)
+ roles = optional(list(string), [
+ "roles/logging.logWriter",
+ "roles/monitoring.metricWriter"
+ ])
+ })
+ nullable = false
+ default = {}
+}
diff --git a/modules/cloud-function-v2/variables-vpcconnector.tf b/modules/cloud-function-v2/variables-vpcconnector.tf
new file mode 100644
index 000000000..967ce6983
--- /dev/null
+++ b/modules/cloud-function-v2/variables-vpcconnector.tf
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2025 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.
+ */
+
+variable "vpc_connector" {
+ description = "VPC connector configuration. Set create to 'true' if a new connector needs to be created."
+ type = object({
+ create = optional(bool, false)
+ name = optional(string)
+ egress_settings = optional(string)
+ })
+ nullable = false
+ default = {}
+}
+
+variable "vpc_connector_create" {
+ description = "VPC connector network configuration. Must be provided if new VPC connector is being created."
+ type = object({
+ ip_cidr_range = optional(string)
+ machine_type = optional(string)
+ name = optional(string)
+ network = optional(string)
+ instances = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }), {}
+ )
+ throughput = optional(object({
+ max = optional(number)
+ min = optional(number)
+ }), {}
+ )
+ subnet = optional(object({
+ name = optional(string)
+ project_id = optional(string)
+ }), {})
+ })
+ default = null
+ validation {
+ condition = (
+ var.vpc_connector.create == false ||
+ try(var.vpc_connector_create.instances, null) != null ||
+ try(var.vpc_connector_create.throughput, null) != null
+ )
+ error_message = "VPC connector must specify either instances or throughput."
+ }
+}
diff --git a/modules/cloud-function-v2/variables.tf b/modules/cloud-function-v2/variables.tf
index ebaf3c7eb..0926f8fd5 100644
--- a/modules/cloud-function-v2/variables.tf
+++ b/modules/cloud-function-v2/variables.tf
@@ -81,6 +81,24 @@ variable "bundle_config" {
}
}
+variable "context" {
+ description = "Context-specific interpolations."
+ type = object({
+ condition_vars = optional(map(map(string)), {}) # not needed here?
+ cidr_ranges = optional(map(string), {})
+ custom_roles = optional(map(string), {})
+ iam_principals = optional(map(string), {})
+ kms_keys = optional(map(string), {})
+ locations = optional(map(string), {})
+ networks = optional(map(string), {})
+ project_ids = optional(map(string), {})
+ subnets = optional(map(string), {})
+ tag_values = optional(map(string), {}) # not needed here?
+ })
+ nullable = false
+ default = {}
+}
+
variable "description" {
description = "Optional description."
type = string
@@ -183,18 +201,6 @@ variable "secrets" {
default = {}
}
-variable "service_account" {
- description = "Service account email. Unused if service account is auto-created."
- type = string
- default = null
-}
-
-variable "service_account_create" {
- description = "Auto-create service account."
- type = bool
- default = false
-}
-
variable "trigger_config" {
description = "Function trigger configuration. Leave null for HTTP trigger."
type = object({
@@ -212,39 +218,3 @@ variable "trigger_config" {
})
default = null
}
-
-variable "vpc_connector" {
- description = "VPC connector configuration. Set create to 'true' if a new connector needs to be created."
- type = object({
- create = optional(bool, false)
- name = optional(string)
- egress_settings = optional(string)
- })
- nullable = false
- default = {}
-}
-
-variable "vpc_connector_config" {
- description = "VPC connector network configuration. Must be provided if new VPC connector is being created."
- type = object({
- ip_cidr_range = string
- network = string
- instances = optional(object({
- max = optional(number)
- min = optional(number, 2)
- }))
- throughput = optional(object({
- max = optional(number, 300)
- min = optional(number, 200)
- }))
- })
- default = null
- validation {
- condition = (
- var.vpc_connector_config == null ||
- try(var.vpc_connector_config.instances, null) != null ||
- try(var.vpc_connector_config.throughput, null) != null
- )
- error_message = "VPC connector must specify either instances or throughput."
- }
-}
diff --git a/modules/cloud-function-v2/vpcconnector.tf b/modules/cloud-function-v2/vpcconnector.tf
new file mode 100644
index 000000000..2c449132a
--- /dev/null
+++ b/modules/cloud-function-v2/vpcconnector.tf
@@ -0,0 +1,74 @@
+/**
+ * Copyright 2025 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.
+ */
+
+locals {
+ _connector_subnet_name_ctx = (
+ try(var.vpc_connector_create.subnet.name, null) == null ? false :
+ contains(keys(local.ctx.subnets), var.vpc_connector_create.subnet.name)
+ )
+ # if you pass the subnet, you must pass only the name, not the whole id
+ _connector_subnet_name = (
+ local._connector_subnet_name_ctx
+ ? reverse(split("/", local.ctx.subnets[var.vpc_connector_create.subnet.name]))[0]
+ : try(var.vpc_connector_create.subnet.name, null)
+ )
+ # if project is not provided, but subnet is coming from context, use project from subnet id in context
+ # and avoid lookups using null project
+ _connector_subnet_project_input = try(var.vpc_connector_create.subnet.project_id, null)
+ _connector_subnet_project = (
+ local._connector_subnet_project_input == null
+ ? (
+ local._connector_subnet_name_ctx
+ ? split("/", local.ctx.subnets[var.vpc_connector_create.subnet.name])[1]
+ : null
+ )
+ : lookup(
+ local.ctx.project_ids, local._connector_subnet_project_input,
+ local._connector_subnet_project_input
+ )
+ )
+}
+
+resource "google_vpc_access_connector" "connector" {
+ count = var.vpc_connector_create != null ? 1 : 0
+ project = local.project_id
+ name = (
+ var.vpc_connector_create.name != null
+ ? var.vpc_connector_create.name
+ : var.name
+ )
+ region = local.location
+ ip_cidr_range = var.vpc_connector_create.ip_cidr_range == null ? null : lookup(
+ local.ctx.cidr_ranges, var.vpc_connector_create.ip_cidr_range,
+ var.vpc_connector_create.ip_cidr_range
+ )
+ network = var.vpc_connector_create.network == null ? null : lookup(
+ local.ctx.networks, var.vpc_connector_create.network,
+ var.vpc_connector_create.network
+ )
+ machine_type = var.vpc_connector_create.machine_type
+ max_instances = var.vpc_connector_create.instances.max
+ max_throughput = var.vpc_connector_create.throughput.max
+ min_instances = var.vpc_connector_create.instances.min
+ min_throughput = var.vpc_connector_create.throughput.min
+ dynamic "subnet" {
+ for_each = var.vpc_connector_create.subnet.name == null ? [] : [""]
+ content {
+ name = local._connector_subnet_name
+ project_id = local._connector_subnet_project
+ }
+ }
+}
diff --git a/modules/cloud-run-v2/README.md b/modules/cloud-run-v2/README.md
index 96141de7b..f33a3a13f 100644
--- a/modules/cloud-run-v2/README.md
+++ b/modules/cloud-run-v2/README.md
@@ -189,7 +189,7 @@ You can use an existing [VPC Access Connector](https://cloud.google.com/vpc/docs
module "cloud_run" {
source = "./fabric/modules/cloud-run-v2"
project_id = var.project_id
- region = var.region
+ region = var.regions.secondary
name = "hello"
containers = {
hello = {
diff --git a/tests/fixtures/vpc-connector.tf b/tests/fixtures/vpc-connector.tf
index 483cf64d0..fda673012 100644
--- a/tests/fixtures/vpc-connector.tf
+++ b/tests/fixtures/vpc-connector.tf
@@ -17,7 +17,7 @@
resource "google_vpc_access_connector" "connector" {
project = var.project_id
name = "vpc-connector"
- region = var.region
+ region = var.regions.secondary
min_instances = 2
max_instances = 3
ip_cidr_range = "192.168.0.0/28"
diff --git a/tests/modules/cloud_function_v1/context-subnet-project.tfvars b/tests/modules/cloud_function_v1/context-subnet-project.tfvars
new file mode 100644
index 000000000..6428c97e1
--- /dev/null
+++ b/tests/modules/cloud_function_v1/context-subnet-project.tfvars
@@ -0,0 +1,58 @@
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+context = {
+ cidr_ranges = {
+ test = "10.10.20.0/28"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+kms_key = "$kms_keys:test"
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+service_account_config = {
+ roles = [
+ "$custom_roles:myrole_one"
+ ]
+}
+vpc_connector = {
+ create = true
+ name = "connector_name"
+}
+vpc_connector_create = {
+ instances = {
+ max = 10
+ min = 3
+ }
+ subnet = {
+ name = "$subnets:test"
+ project_id = "$project_ids:test"
+ }
+}
diff --git a/tests/modules/cloud_function_v1/context-subnet-project.yaml b/tests/modules/cloud_function_v1/context-subnet-project.yaml
new file mode 100644
index 000000000..6ada43545
--- /dev/null
+++ b/tests/modules/cloud_function_v1/context-subnet-project.yaml
@@ -0,0 +1,61 @@
+# Copyright 2025 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:
+ google_cloudfunctions_function.function:
+ kms_key_name: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ project: foo-test-0
+ region: europe-west8
+ service_account_email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ google_cloudfunctions_function_iam_binding.default["$custom_roles:myrole_one"]:
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ region: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ google_project_iam_member.default["organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_service_account.service_account[0]:
+ account_id: test-cf-kms
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-kms
+ email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ timeouts: null
+ google_vpc_access_connector.connector[0]:
+ ip_cidr_range: null
+ machine_type: e2-micro
+ max_instances: 10
+ min_instances: 3
+ name: test-cf-kms
+ project: foo-test-0
+ region: europe-west8
+ subnet:
+ - name: gce
+ project_id: foo-test-0
+ timeouts: null
+
+counts:
+ google_cloudfunctions_function: 1
+ google_cloudfunctions_function_iam_binding: 1
+ google_project_iam_member: 1
+ google_service_account: 1
+ google_vpc_access_connector: 1
diff --git a/tests/modules/cloud_function_v1/context-subnet.tfvars b/tests/modules/cloud_function_v1/context-subnet.tfvars
new file mode 100644
index 000000000..7512e3ab3
--- /dev/null
+++ b/tests/modules/cloud_function_v1/context-subnet.tfvars
@@ -0,0 +1,57 @@
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+context = {
+ cidr_ranges = {
+ test = "10.10.20.0/28"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+kms_key = "$kms_keys:test"
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+service_account_config = {
+ roles = [
+ "$custom_roles:myrole_one"
+ ]
+}
+vpc_connector = {
+ create = true
+ name = "connector_name"
+}
+vpc_connector_create = {
+ instances = {
+ max = 10
+ min = 3
+ }
+ subnet = {
+ name = "$subnets:test"
+ }
+}
diff --git a/tests/modules/cloud_function_v1/context-subnet.yaml b/tests/modules/cloud_function_v1/context-subnet.yaml
new file mode 100644
index 000000000..de5cf036a
--- /dev/null
+++ b/tests/modules/cloud_function_v1/context-subnet.yaml
@@ -0,0 +1,60 @@
+# Copyright 2025 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:
+ google_cloudfunctions_function.function:
+ kms_key_name: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ project: foo-test-0
+ region: europe-west8
+ service_account_email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ google_cloudfunctions_function_iam_binding.default["$custom_roles:myrole_one"]:
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ region: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ google_project_iam_member.default["organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_service_account.service_account[0]:
+ account_id: test-cf-kms
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-kms
+ email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ timeouts: null
+ google_vpc_access_connector.connector[0]:
+ ip_cidr_range: null
+ machine_type: e2-micro
+ max_instances: 10
+ min_instances: 3
+ name: test-cf-kms
+ project: foo-test-0
+ region: europe-west8
+ subnet:
+ - name: gce
+ project_id: foo-dev-net-spoke-0
+
+counts:
+ google_cloudfunctions_function: 1
+ google_cloudfunctions_function_iam_binding: 1
+ google_project_iam_member: 1
+ google_service_account: 1
+ google_vpc_access_connector: 1
diff --git a/tests/modules/cloud_function_v1/context.tfvars b/tests/modules/cloud_function_v1/context.tfvars
new file mode 100644
index 000000000..f6eb5e3e3
--- /dev/null
+++ b/tests/modules/cloud_function_v1/context.tfvars
@@ -0,0 +1,56 @@
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+context = {
+ cidr_ranges = {
+ test = "10.10.20.0/28"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+kms_key = "$kms_keys:test"
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+service_account_config = {
+ roles = [
+ "$custom_roles:myrole_one"
+ ]
+}
+vpc_connector = {
+ create = true
+ name = "connector_name"
+}
+vpc_connector_create = {
+ ip_cidr_range = "$cidr_ranges:test"
+ network = "$networks:test"
+ instances = {
+ max = 10
+ min = 3
+ }
+}
diff --git a/tests/modules/cloud_function_v1/context.yaml b/tests/modules/cloud_function_v1/context.yaml
new file mode 100644
index 000000000..da60e5571
--- /dev/null
+++ b/tests/modules/cloud_function_v1/context.yaml
@@ -0,0 +1,60 @@
+# Copyright 2025 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:
+ google_cloudfunctions_function.function:
+ kms_key_name: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ project: foo-test-0
+ region: europe-west8
+ service_account_email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ google_cloudfunctions_function_iam_binding.default["$custom_roles:myrole_one"]:
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ region: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ google_project_iam_member.default["organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_service_account.service_account[0]:
+ account_id: test-cf-kms
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-kms
+ email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ timeouts: null
+ google_vpc_access_connector.connector[0]:
+ ip_cidr_range: 10.10.20.0/28
+ machine_type: e2-micro
+ max_instances: 10
+ min_instances: 3
+ name: test-cf-kms
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ project: foo-test-0
+ region: europe-west8
+ subnet: []
+ timeouts: null
+
+counts:
+ google_cloudfunctions_function: 1
+ google_cloudfunctions_function_iam_binding: 1
+ google_project_iam_member: 1
+ google_service_account: 1
+ google_vpc_access_connector: 1
diff --git a/tests/modules/cloud_function_v1/examples/bucket-creation.yaml b/tests/modules/cloud_function_v1/examples/bucket-creation.yaml
index 964682337..8d8b8bda2 100644
--- a/tests/modules/cloud_function_v1/examples/bucket-creation.yaml
+++ b/tests/modules/cloud_function_v1/examples/bucket-creation.yaml
@@ -27,10 +27,9 @@ values:
name: test-bucket
counts:
+ archive_file: 1
google_cloudfunctions_function: 1
google_storage_bucket: 1
google_storage_bucket_object: 1
- modules: 1
- resources: 6
outputs: {}
diff --git a/tests/modules/cloud_function_v1/examples/cmek.yaml b/tests/modules/cloud_function_v1/examples/cmek.yaml
new file mode 100644
index 000000000..cc697041e
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/cmek.yaml
@@ -0,0 +1,73 @@
+# Copyright 2025 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.artifact-registry.google_artifact_registry_repository.registry:
+ cleanup_policies: []
+ cleanup_policy_dry_run: null
+ description: Terraform-managed registry
+ docker_config: []
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ format: DOCKER
+ labels: null
+ location: europe-west9
+ maven_config: []
+ mode: STANDARD_REPOSITORY
+ project: test-cf-v1
+ remote_repository_config: []
+ repository_id: registry
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ virtual_repository_config: []
+ vulnerability_scanning_config:
+ - enablement_config: null
+ module.artifact-registry.google_artifact_registry_repository_iam_binding.authoritative["roles/artifactregistry.createOnPushWriter"]:
+ condition: []
+ location: europe-west9
+ project: test-cf-v1
+ role: roles/artifactregistry.createOnPushWriter
+ module.kms.google_kms_crypto_key.default["key-regional"]:
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ labels: null
+ name: key-regional
+ purpose: ENCRYPT_DECRYPT
+ rotation_period: null
+ skip_initial_version_creation: false
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.kms.google_kms_key_ring.default[0]:
+ location: europe-west9
+ name: test-keyring
+ project: test-cf-v1
+ timeouts: null
+ module.kms.google_kms_key_ring_iam_binding.authoritative["roles/cloudkms.cryptoKeyEncrypterDecrypter"]:
+ condition: []
+ role: roles/cloudkms.cryptoKeyEncrypterDecrypter
+ module.project.google_project_iam_binding.authoritative["roles/storage.objectViewer"]:
+ condition: []
+ project: test-cf-v1
+ role: roles/storage.objectViewer
+
+counts:
+ google_artifact_registry_repository: 1
+ google_artifact_registry_repository_iam_binding: 1
+ google_cloudfunctions_function: 1
+ google_kms_crypto_key: 1
+ google_kms_key_ring: 1
+ google_kms_key_ring_iam_binding: 1
diff --git a/tests/modules/cloud_function_v1/examples/custom-bundle.yaml b/tests/modules/cloud_function_v1/examples/custom-bundle.yaml
new file mode 100644
index 000000000..ff8866600
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/custom-bundle.yaml
@@ -0,0 +1,113 @@
+# Copyright 2025 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:
+ google_project_iam_member.artifact_writer:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/artifactregistry.createOnPushWriter
+ google_project_iam_member.bucket_default_compute_account_grant:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/storage.objectViewer
+ module.cf-http.data.archive_file.bundle[0]:
+ exclude_symlink_directories: null
+ excludes:
+ - __pycache__
+ output_file_mode: '0644'
+ output_path: bundle.zip
+ source: []
+ source_content: null
+ source_content_filename: null
+ source_dir: assets/sample-function/
+ source_file: null
+ type: zip
+ module.cf-http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west9
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+ vpc_connector: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.cf-http.google_storage_bucket_object.bundle[0]:
+ bucket: bucket
+ cache_control: null
+ content_disposition: null
+ content_encoding: null
+ content_language: null
+ customer_encryption: []
+ deletion_policy: null
+ detect_md5hash: null
+ event_based_hold: null
+ force_empty_content_type: null
+ metadata: null
+ retention: []
+ source: bundle.zip
+ temporary_hold: null
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
diff --git a/tests/modules/cloud_function_v1/examples/http-trigger.yaml b/tests/modules/cloud_function_v1/examples/http-trigger.yaml
new file mode 100644
index 000000000..ce1d501a1
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/http-trigger.yaml
@@ -0,0 +1,115 @@
+# Copyright 2025 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:
+ google_project_iam_member.artifact_writer:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/artifactregistry.createOnPushWriter
+ google_project_iam_member.bucket_default_compute_account_grant:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/storage.objectViewer
+ module.cf-http.data.archive_file.bundle[0]:
+ exclude_symlink_directories: null
+ excludes: null
+ output_file_mode: '0644'
+ output_path: /tmp/bundle-project-id-test-cf-http.zip
+ source: []
+ source_content: null
+ source_content_filename: null
+ source_dir: assets/sample-function/
+ source_file: null
+ type: zip
+ module.cf-http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west9
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+ vpc_connector: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.cf-http.google_storage_bucket_object.bundle[0]:
+ bucket: bucket
+ cache_control: null
+ content_disposition: null
+ content_encoding: null
+ content_language: null
+ customer_encryption: []
+ deletion_policy: null
+ detect_md5hash: null
+ event_based_hold: null
+ force_empty_content_type: null
+ metadata: null
+ retention: []
+ source: /tmp/bundle-project-id-test-cf-http.zip
+ temporary_hold: null
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v1/examples/iam.yaml b/tests/modules/cloud_function_v1/examples/iam.yaml
index 8473e6f3d..31e5b30cc 100644
--- a/tests/modules/cloud_function_v1/examples/iam.yaml
+++ b/tests/modules/cloud_function_v1/examples/iam.yaml
@@ -22,7 +22,9 @@ values:
role: roles/cloudfunctions.invoker
counts:
+ archive_file: 1
google_cloudfunctions_function: 1
+ google_cloudfunctions_function_iam_binding: 1
+ google_project_iam_member: 4
+ google_service_account: 1
google_storage_bucket_object: 1
- modules: 1
- resources: 6
diff --git a/tests/modules/cloud_function_v1/examples/multiple_functions.yaml b/tests/modules/cloud_function_v1/examples/multiple_functions.yaml
index f550b2c75..2cc3fecb1 100644
--- a/tests/modules/cloud_function_v1/examples/multiple_functions.yaml
+++ b/tests/modules/cloud_function_v1/examples/multiple_functions.yaml
@@ -19,7 +19,8 @@ values:
source: /tmp/bundle-project-id-test-cf-http-two.zip
counts:
+ archive_file: 1
google_cloudfunctions_function: 2
+ google_project_iam_member: 6
+ google_service_account: 2
google_storage_bucket_object: 2
- modules: 2
- resources: 7
diff --git a/tests/modules/cloud_function_v1/examples/private-build-pool.yaml b/tests/modules/cloud_function_v1/examples/private-build-pool.yaml
new file mode 100644
index 000000000..b8db1e2c7
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/private-build-pool.yaml
@@ -0,0 +1,125 @@
+# Copyright 2025 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:
+ google_cloudbuild_worker_pool.pool:
+ annotations: null
+ display_name: null
+ location: europe-west9
+ name: custom-pool
+ network_config: []
+ private_service_connect: []
+ project: project-id
+ timeouts: null
+ worker_config:
+ - disk_size_gb: 100
+ machine_type: e2-standard-4
+ no_external_ip: false
+ google_project_iam_member.artifact_writer:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/artifactregistry.createOnPushWriter
+ google_project_iam_member.bucket_default_compute_account_grant:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/storage.objectViewer
+ module.cf-http.data.archive_file.bundle[0]:
+ exclude_symlink_directories: null
+ excludes: null
+ output_file_mode: '0644'
+ output_path: /tmp/bundle-project-id-test-cf-http.zip
+ source: []
+ source_content: null
+ source_content_filename: null
+ source_dir: assets/sample-function/
+ source_file: null
+ type: zip
+ module.cf-http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west9
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+ vpc_connector: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.cf-http.google_storage_bucket_object.bundle[0]:
+ bucket: bucket
+ cache_control: null
+ content_disposition: null
+ content_encoding: null
+ content_language: null
+ customer_encryption: []
+ deletion_policy: null
+ detect_md5hash: null
+ event_based_hold: null
+ force_empty_content_type: null
+ metadata: null
+ retention: []
+ source: /tmp/bundle-project-id-test-cf-http.zip
+ temporary_hold: null
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudbuild_worker_pool: 1
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
diff --git a/tests/modules/cloud_function_v1/examples/pubsub-non-http-trigger.yaml b/tests/modules/cloud_function_v1/examples/pubsub-non-http-trigger.yaml
new file mode 100644
index 000000000..4e3b1c777
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/pubsub-non-http-trigger.yaml
@@ -0,0 +1,84 @@
+# Copyright 2025 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.cf-http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ event_trigger:
+ - event_type: google.pubsub.topic.publish
+ resource: topic
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west9
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: null
+ vpc_connector: null
+ module.pubsub.google_pubsub_topic.default:
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ ingestion_data_source_settings: []
+ kms_key_name: null
+ labels: null
+ message_retention_duration: null
+ message_transforms: []
+ name: topic
+ project: project-id
+ schema_settings: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.pubsub.google_pubsub_topic_iam_binding.authoritative["roles/pubsub.subscriber"]:
+ condition: []
+ members:
+ - serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/pubsub.subscriber
+ topic: topic
+
+counts:
+ archive_file: 1
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 4
+ google_pubsub_topic: 1
+ google_pubsub_topic_iam_binding: 1
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v1/examples/secrets.yaml b/tests/modules/cloud_function_v1/examples/secrets.yaml
index dc5d432bd..01cfaa646 100644
--- a/tests/modules/cloud_function_v1/examples/secrets.yaml
+++ b/tests/modules/cloud_function_v1/examples/secrets.yaml
@@ -30,5 +30,3 @@ values:
counts:
google_cloudfunctions_function: 1
google_storage_bucket_object: 1
- modules: 2
- resources: 8
diff --git a/tests/modules/cloud_function_v1/examples/service-account.yaml b/tests/modules/cloud_function_v1/examples/service-account.yaml
new file mode 100644
index 000000000..767d86a40
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/service-account.yaml
@@ -0,0 +1,75 @@
+# Copyright 2025 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.cf-http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west9
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+ vpc_connector: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
diff --git a/tests/modules/cloud_function_v1/examples/service-vpc-access-connector-create-sharedvpc.yaml b/tests/modules/cloud_function_v1/examples/service-vpc-access-connector-create-sharedvpc.yaml
new file mode 100644
index 000000000..1ae759ba3
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/service-vpc-access-connector-create-sharedvpc.yaml
@@ -0,0 +1,68 @@
+# Copyright 2025 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.cf_http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west8
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_archive_object: bundle-95c1b0e5b92dae8333539b1e0ad5173b.zip
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+ module.cf_http.google_vpc_access_connector.connector[0]:
+ ip_cidr_range: null
+ machine_type: e2-standard-4
+ max_throughput: 300
+ min_throughput: 200
+ name: test-cf-http
+ project: project-id
+ region: europe-west8
+ subnet:
+ - name: fixture-subnet-28
+ project_id: test-host
+ timeouts: null
+
+counts:
+ google_cloudfunctions_function: 1
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v1/examples/service-vpc-access-connector-create.yaml b/tests/modules/cloud_function_v1/examples/service-vpc-access-connector-create.yaml
new file mode 100644
index 000000000..e1c215525
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/service-vpc-access-connector-create.yaml
@@ -0,0 +1,56 @@
+# Copyright 2025 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.cf_http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west8
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+
+counts:
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 2
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v1/examples/service-vpc-access-connector.yaml b/tests/modules/cloud_function_v1/examples/service-vpc-access-connector.yaml
new file mode 100644
index 000000000..e1c215525
--- /dev/null
+++ b/tests/modules/cloud_function_v1/examples/service-vpc-access-connector.yaml
@@ -0,0 +1,56 @@
+# Copyright 2025 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.cf_http.google_cloudfunctions_function.function:
+ available_memory_mb: 256
+ build_environment_variables: null
+ build_worker_pool: null
+ description: Terraform managed.
+ docker_registry: ARTIFACT_REGISTRY
+ docker_repository: null
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ entry_point: main
+ environment_variables: null
+ https_trigger_security_level: SECURE_ALWAYS
+ ingress_settings: ALLOW_ALL
+ kms_key_name: null
+ labels: null
+ max_instances: 1
+ min_instances: null
+ name: test-cf-http
+ on_deploy_update_policy: []
+ project: project-id
+ region: europe-west8
+ runtime: python310
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_repository: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeout: 180
+ timeouts: null
+ trigger_http: true
+
+counts:
+ google_cloudfunctions_function: 1
+ google_project_iam_member: 2
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v1/kms.tfvars b/tests/modules/cloud_function_v1/kms.tfvars
new file mode 100644
index 000000000..81f3b5190
--- /dev/null
+++ b/tests/modules/cloud_function_v1/kms.tfvars
@@ -0,0 +1,11 @@
+project_id = "project"
+region = "region"
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+kms_key = "kms_key_id"
+repository_settings = {
+ repository = "artifact_registry_id"
+}
diff --git a/tests/modules/cloud_function_v1/kms.yaml b/tests/modules/cloud_function_v1/kms.yaml
new file mode 100644
index 000000000..813f42f7c
--- /dev/null
+++ b/tests/modules/cloud_function_v1/kms.yaml
@@ -0,0 +1,39 @@
+# Copyright 2025 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:
+ google_cloudfunctions_function.function:
+ docker_repository: artifact_registry_id
+ kms_key_name: kms_key_id
+ name: test-cf-kms
+ project: project
+ region: region
+ service_account_email: test-cf-kms@project.iam.gserviceaccount.com
+ source_archive_bucket: bucket
+ source_archive_object: sample-function.zip
+
+counts:
+ google_cloudfunctions_function: 1
+
+outputs:
+ bucket: null
+ bucket_name: bucket
+ function: __missing__
+ function_name: test-cf-kms
+ id: __missing__
+ invoke_command: __missing__
+ service_account: __missing__
+ service_account_email: test-cf-kms@project.iam.gserviceaccount.com
+ service_account_iam_email: serviceAccount:test-cf-kms@project.iam.gserviceaccount.com
+ vpc_connector: null
diff --git a/tests/modules/cloud_function_v1/tftest.yaml b/tests/modules/cloud_function_v1/tftest.yaml
new file mode 100644
index 000000000..f763f77be
--- /dev/null
+++ b/tests/modules/cloud_function_v1/tftest.yaml
@@ -0,0 +1,20 @@
+# Copyright 2025 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.
+
+module: modules/cloud-function-v1
+tests:
+ context:
+ context-subnet:
+ context-subnet-project:
+ kms:
diff --git a/tests/modules/cloud_function_v2/context-subnet-project.tfvars b/tests/modules/cloud_function_v2/context-subnet-project.tfvars
new file mode 100644
index 000000000..6428c97e1
--- /dev/null
+++ b/tests/modules/cloud_function_v2/context-subnet-project.tfvars
@@ -0,0 +1,58 @@
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+context = {
+ cidr_ranges = {
+ test = "10.10.20.0/28"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+kms_key = "$kms_keys:test"
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+service_account_config = {
+ roles = [
+ "$custom_roles:myrole_one"
+ ]
+}
+vpc_connector = {
+ create = true
+ name = "connector_name"
+}
+vpc_connector_create = {
+ instances = {
+ max = 10
+ min = 3
+ }
+ subnet = {
+ name = "$subnets:test"
+ project_id = "$project_ids:test"
+ }
+}
diff --git a/tests/modules/cloud_function_v2/context-subnet-project.yaml b/tests/modules/cloud_function_v2/context-subnet-project.yaml
new file mode 100644
index 000000000..1c0947fb9
--- /dev/null
+++ b/tests/modules/cloud_function_v2/context-subnet-project.yaml
@@ -0,0 +1,64 @@
+# Copyright 2025 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:
+ google_cloudfunctions2_function.function:
+ kms_key_name: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ location: europe-west8
+ name: test-cf-kms
+ project: foo-test-0
+ service_config:
+ - service_account_email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ google_cloudfunctions2_function_iam_binding.binding["$custom_roles:myrole_one"]:
+ cloud_function: test-cf-kms
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ location: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ google_project_iam_member.default["organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_service_account.service_account[0]:
+ account_id: test-cf-kms
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-kms
+ email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ timeouts: null
+ google_vpc_access_connector.connector[0]:
+ ip_cidr_range: null
+ machine_type: e2-micro
+ max_instances: 10
+ min_instances: 3
+ name: test-cf-kms
+ project: foo-test-0
+ region: europe-west8
+ subnet:
+ - name: gce
+ project_id: foo-test-0
+ timeouts: null
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_cloudfunctions2_function_iam_binding: 1
+ google_project_iam_member: 1
+ google_service_account: 1
+ google_vpc_access_connector: 1
diff --git a/tests/modules/cloud_function_v2/context-subnet.tfvars b/tests/modules/cloud_function_v2/context-subnet.tfvars
new file mode 100644
index 000000000..7512e3ab3
--- /dev/null
+++ b/tests/modules/cloud_function_v2/context-subnet.tfvars
@@ -0,0 +1,57 @@
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+context = {
+ cidr_ranges = {
+ test = "10.10.20.0/28"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+kms_key = "$kms_keys:test"
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+service_account_config = {
+ roles = [
+ "$custom_roles:myrole_one"
+ ]
+}
+vpc_connector = {
+ create = true
+ name = "connector_name"
+}
+vpc_connector_create = {
+ instances = {
+ max = 10
+ min = 3
+ }
+ subnet = {
+ name = "$subnets:test"
+ }
+}
diff --git a/tests/modules/cloud_function_v2/context-subnet.yaml b/tests/modules/cloud_function_v2/context-subnet.yaml
new file mode 100644
index 000000000..de83bf86d
--- /dev/null
+++ b/tests/modules/cloud_function_v2/context-subnet.yaml
@@ -0,0 +1,62 @@
+# Copyright 2025 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:
+ google_cloudfunctions2_function.function:
+ kms_key_name: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ location: europe-west8
+ project: foo-test-0
+ service_config:
+ - service_account_email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ google_cloudfunctions2_function_iam_binding.binding["$custom_roles:myrole_one"]:
+ cloud_function: test-cf-kms
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ location: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ google_project_iam_member.default["organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_service_account.service_account[0]:
+ account_id: test-cf-kms
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-kms
+ email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ timeouts: null
+ google_vpc_access_connector.connector[0]:
+ ip_cidr_range: null
+ machine_type: e2-micro
+ max_instances: 10
+ min_instances: 3
+ name: test-cf-kms
+ project: foo-test-0
+ region: europe-west8
+ subnet:
+ - name: gce
+ project_id: foo-dev-net-spoke-0
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_cloudfunctions2_function_iam_binding: 1
+ google_project_iam_member: 1
+ google_service_account: 1
+ google_vpc_access_connector: 1
diff --git a/tests/modules/cloud_function_v2/context.tfvars b/tests/modules/cloud_function_v2/context.tfvars
new file mode 100644
index 000000000..f6eb5e3e3
--- /dev/null
+++ b/tests/modules/cloud_function_v2/context.tfvars
@@ -0,0 +1,56 @@
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+context = {
+ cidr_ranges = {
+ test = "10.10.20.0/28"
+ }
+ custom_roles = {
+ myrole_one = "organizations/366118655033/roles/myRoleOne"
+ }
+ iam_principals = {
+ mygroup = "group:test-group@example.com"
+ }
+ kms_keys = {
+ test = "projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute"
+ }
+ locations = {
+ ew8 = "europe-west8"
+ }
+ networks = {
+ test = "projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0"
+ }
+ project_ids = {
+ test = "foo-test-0"
+ }
+ subnets = {
+ test = "projects/foo-dev-net-spoke-0/regions/europe-west1/subnetworks/gce"
+ }
+}
+kms_key = "$kms_keys:test"
+iam = {
+ "$custom_roles:myrole_one" = [
+ "$iam_principals:mygroup"
+ ]
+}
+project_id = "$project_ids:test"
+region = "$locations:ew8"
+service_account_config = {
+ roles = [
+ "$custom_roles:myrole_one"
+ ]
+}
+vpc_connector = {
+ create = true
+ name = "connector_name"
+}
+vpc_connector_create = {
+ ip_cidr_range = "$cidr_ranges:test"
+ network = "$networks:test"
+ instances = {
+ max = 10
+ min = 3
+ }
+}
diff --git a/tests/modules/cloud_function_v2/context.yaml b/tests/modules/cloud_function_v2/context.yaml
new file mode 100644
index 000000000..4f9a08057
--- /dev/null
+++ b/tests/modules/cloud_function_v2/context.yaml
@@ -0,0 +1,61 @@
+# Copyright 2025 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:
+ google_cloudfunctions2_function.function:
+ kms_key_name: projects/foo-prod-sec-core/locations/global/keyRings/prod-global-default/cryptoKeys/compute
+ location: europe-west8
+ project: foo-test-0
+ service_config:
+ - service_account_email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ google_cloudfunctions2_function_iam_binding.binding["$custom_roles:myrole_one"]:
+ condition: []
+ members:
+ - group:test-group@example.com
+ project: foo-test-0
+ location: europe-west8
+ role: organizations/366118655033/roles/myRoleOne
+ google_project_iam_member.default["organizations/366118655033/roles/myRoleOne"]:
+ condition: []
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ role: organizations/366118655033/roles/myRoleOne
+ google_service_account.service_account[0]:
+ account_id: test-cf-kms
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-kms
+ email: test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-kms@foo-test-0.iam.gserviceaccount.com
+ project: foo-test-0
+ timeouts: null
+ google_vpc_access_connector.connector[0]:
+ ip_cidr_range: 10.10.20.0/28
+ machine_type: e2-micro
+ max_instances: 10
+ min_instances: 3
+ name: test-cf-kms
+ network: projects/foo-dev-net-spoke-0/global/networks/dev-spoke-0
+ project: foo-test-0
+ region: europe-west8
+ subnet: []
+ timeouts: null
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_cloudfunctions2_function_iam_binding: 1
+ google_project_iam_member: 1
+ google_service_account: 1
+ google_vpc_access_connector: 1
diff --git a/tests/modules/cloud_function_v2/examples/bucket-creation.yaml b/tests/modules/cloud_function_v2/examples/bucket-creation.yaml
index 99cb104e3..e92953ea5 100644
--- a/tests/modules/cloud_function_v2/examples/bucket-creation.yaml
+++ b/tests/modules/cloud_function_v2/examples/bucket-creation.yaml
@@ -35,7 +35,6 @@ counts:
google_cloudfunctions2_function: 1
google_storage_bucket: 1
google_storage_bucket_object: 1
- modules: 1
- resources: 6
+
outputs: {}
diff --git a/tests/modules/cloud_function_v2/examples/custom-bundle.yaml b/tests/modules/cloud_function_v2/examples/custom-bundle.yaml
new file mode 100644
index 000000000..e938995c6
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/custom-bundle.yaml
@@ -0,0 +1,118 @@
+# Copyright 2025 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:
+ google_project_iam_member.artifact_writer:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/artifactregistry.createOnPushWriter
+ google_project_iam_member.bucket_default_compute_account_grant:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/storage.objectViewer
+ module.cf-http.data.archive_file.bundle[0]:
+ exclude_symlink_directories: null
+ excludes:
+ - __pycache__
+ output_file_mode: '0644'
+ output_path: bundle.zip
+ source: []
+ source_content: null
+ source_content_filename: null
+ source_dir: assets/sample-function/
+ source_file: null
+ type: zip
+ module.cf-http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector: null
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.cf-http.google_storage_bucket_object.bundle[0]:
+ bucket: bucket
+ cache_control: null
+ content_disposition: null
+ content_encoding: null
+ content_language: null
+ customer_encryption: []
+ deletion_policy: null
+ detect_md5hash: null
+ event_based_hold: null
+ force_empty_content_type: null
+ metadata: null
+ retention: []
+ source: bundle.zip
+ temporary_hold: null
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
diff --git a/tests/modules/cloud_function_v2/examples/http-trigger.yaml b/tests/modules/cloud_function_v2/examples/http-trigger.yaml
new file mode 100644
index 000000000..7cbb5901c
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/http-trigger.yaml
@@ -0,0 +1,120 @@
+# Copyright 2025 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:
+ google_project_iam_member.artifact_writer:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/artifactregistry.createOnPushWriter
+ google_project_iam_member.bucket_default_compute_account_grant:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/storage.objectViewer
+ module.cf-http.data.archive_file.bundle[0]:
+ exclude_symlink_directories: null
+ excludes: null
+ output_file_mode: '0644'
+ output_path: /tmp/bundle-project-id-test-cf-http.zip
+ source: []
+ source_content: null
+ source_content_filename: null
+ source_dir: assets/sample-function/
+ source_file: null
+ type: zip
+ module.cf-http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector: null
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.cf-http.google_storage_bucket_object.bundle[0]:
+ bucket: bucket
+ cache_control: null
+ content_disposition: null
+ content_encoding: null
+ content_language: null
+ customer_encryption: []
+ deletion_policy: null
+ detect_md5hash: null
+ event_based_hold: null
+ force_empty_content_type: null
+ metadata: null
+ retention: []
+ source: /tmp/bundle-project-id-test-cf-http.zip
+ temporary_hold: null
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v2/examples/iam.yaml b/tests/modules/cloud_function_v2/examples/iam.yaml
index c96dbdb34..427d09e4f 100644
--- a/tests/modules/cloud_function_v2/examples/iam.yaml
+++ b/tests/modules/cloud_function_v2/examples/iam.yaml
@@ -25,12 +25,9 @@ values:
module.cf-http.google_storage_bucket_object.bundle[0]:
bucket: bucket
customer_encryption: []
- detect_md5hash: different hash
source: /tmp/bundle-project-id-test-cf-http.zip
counts:
google_cloud_run_service_iam_binding: 1
google_cloudfunctions2_function: 1
google_storage_bucket_object: 1
- modules: 1
- resources: 6
diff --git a/tests/modules/cloud_function_v2/examples/multiple_functions.yaml b/tests/modules/cloud_function_v2/examples/multiple_functions.yaml
index 65b7a18b9..6de069911 100644
--- a/tests/modules/cloud_function_v2/examples/multiple_functions.yaml
+++ b/tests/modules/cloud_function_v2/examples/multiple_functions.yaml
@@ -22,4 +22,4 @@ counts:
google_cloudfunctions2_function: 2
google_storage_bucket_object: 2
modules: 2
- resources: 7
+ resources: 13
diff --git a/tests/modules/cloud_function_v2/examples/private-build-pool.yaml b/tests/modules/cloud_function_v2/examples/private-build-pool.yaml
new file mode 100644
index 000000000..6ee70850a
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/private-build-pool.yaml
@@ -0,0 +1,130 @@
+# Copyright 2025 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:
+ google_cloudbuild_worker_pool.pool:
+ annotations: null
+ display_name: null
+ location: europe-west9
+ name: custom-pool
+ network_config: []
+ private_service_connect: []
+ project: project-id
+ timeouts: null
+ worker_config:
+ - disk_size_gb: 100
+ machine_type: e2-standard-4
+ no_external_ip: false
+ google_project_iam_member.artifact_writer:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/artifactregistry.createOnPushWriter
+ google_project_iam_member.bucket_default_compute_account_grant:
+ condition: []
+ member: serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/storage.objectViewer
+ module.cf-http.data.archive_file.bundle[0]:
+ exclude_symlink_directories: null
+ excludes: null
+ output_file_mode: '0644'
+ output_path: /tmp/bundle-project-id-test-cf-http.zip
+ source: []
+ source_content: null
+ source_content_filename: null
+ source_dir: assets/sample-function/
+ source_file: null
+ type: zip
+ module.cf-http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west9
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector: null
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.cf-http.google_storage_bucket_object.bundle[0]:
+ bucket: bucket
+ cache_control: null
+ content_disposition: null
+ content_encoding: null
+ content_language: null
+ customer_encryption: []
+ deletion_policy: null
+ detect_md5hash: null
+ event_based_hold: null
+ force_empty_content_type: null
+ metadata: null
+ retention: []
+ source: /tmp/bundle-project-id-test-cf-http.zip
+ temporary_hold: null
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudbuild_worker_pool: 1
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
diff --git a/tests/modules/cloud_function_v2/examples/pubsub-non-http-trigger.yaml b/tests/modules/cloud_function_v2/examples/pubsub-non-http-trigger.yaml
new file mode 100644
index 000000000..430a89a3c
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/pubsub-non-http-trigger.yaml
@@ -0,0 +1,91 @@
+# Copyright 2025 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.cf-http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger:
+ - event_filters: []
+ event_type: google.cloud.pubsub.topic.v1.messagePublished
+ retry_policy: RETRY_POLICY_DO_NOT_RETRY
+ service_account_email: sa-cloudfunction@project-id.iam.gserviceaccount.com
+ trigger_region: europe-west8
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector: null
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.pubsub.google_pubsub_topic.default:
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ ingestion_data_source_settings: []
+ kms_key_name: null
+ labels: null
+ message_retention_duration: null
+ message_transforms: []
+ name: topic
+ project: project-id
+ schema_settings: []
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.pubsub.google_pubsub_topic_iam_binding.authoritative["roles/pubsub.subscriber"]:
+ condition: []
+ members:
+ - serviceAccount:123-compute@developer.gserviceaccount.com
+ project: project-id
+ role: roles/pubsub.subscriber
+ topic: topic
+
+counts:
+ archive_file: 1
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 5
+ google_pubsub_topic: 1
+ google_pubsub_topic_iam_binding: 1
+ google_service_account: 2
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v2/examples/secrets.yaml b/tests/modules/cloud_function_v2/examples/secrets.yaml
index 77a36c8c6..04bf0be8f 100644
--- a/tests/modules/cloud_function_v2/examples/secrets.yaml
+++ b/tests/modules/cloud_function_v2/examples/secrets.yaml
@@ -1,4 +1,4 @@
-# Copyright 2023 Google LLC
+# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -27,9 +27,55 @@ values:
# secret: var_secret # known after apply
versions:
- {}
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+ module.secret-manager.google_secret_manager_secret.default["credentials"]:
+ annotations: null
+ deletion_protection: false
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ labels: null
+ project: project-id
+ replication:
+ - auto:
+ - customer_managed_encryption: []
+ user_managed: []
+ rotation: []
+ secret_id: credentials
+ tags: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ topics: []
+ ttl: null
+ version_aliases: null
+ version_destroy_ttl: null
+ module.secret-manager.google_secret_manager_secret_iam_binding.authoritative["credentials.roles/secretmanager.secretAccessor"]:
+ condition: []
+ members:
+ - serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ role: roles/secretmanager.secretAccessor
+ module.secret-manager.google_secret_manager_secret_version.default["credentials/v1"]:
+ deletion_policy: DELETE
+ enabled: true
+ is_secret_data_base64: false
+ secret_data: manual foo bar spam
+ secret_data_wo: null
+ secret_data_wo_version: 0
+ timeouts: null
counts:
google_cloudfunctions2_function: 1
- google_storage_bucket_object: 1
- modules: 2
- resources: 8
+ google_secret_manager_secret: 1
+ google_secret_manager_secret_iam_binding: 1
+ google_secret_manager_secret_version: 1
+ google_service_account: 1
diff --git a/tests/modules/cloud_function_v2/examples/service-account-1.yaml b/tests/modules/cloud_function_v2/examples/service-account-1.yaml
new file mode 100644
index 000000000..ba3c7d8b6
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/service-account-1.yaml
@@ -0,0 +1,80 @@
+# Copyright 2025 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.cf-http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector: null
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.cf-http.google_project_iam_member.default["roles/logging.logWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/logging.logWriter
+ module.cf-http.google_project_iam_member.default["roles/monitoring.metricWriter"]:
+ condition: []
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ role: roles/monitoring.metricWriter
+ module.cf-http.google_service_account.service_account[0]:
+ account_id: test-cf-http
+ create_ignore_already_exists: null
+ description: null
+ disabled: false
+ display_name: test-cf-http
+ email: test-cf-http@project-id.iam.gserviceaccount.com
+ member: serviceAccount:test-cf-http@project-id.iam.gserviceaccount.com
+ project: project-id
+ timeouts: null
+
+counts:
+ archive_file: 1
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 4
+ google_service_account: 1
+ google_storage_bucket_object: 1
diff --git a/tests/modules/cloud_function_v2/examples/service-account-2.yaml b/tests/modules/cloud_function_v2/examples/service-account-2.yaml
new file mode 100644
index 000000000..2a95f05c0
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/service-account-2.yaml
@@ -0,0 +1,58 @@
+# Copyright 2025 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.cf-http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: sa1@sa.example
+ timeout_seconds: 180
+ vpc_connector: null
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 2
+ google_service_account: 0
diff --git a/tests/modules/cloud_function_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml b/tests/modules/cloud_function_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
new file mode 100644
index 000000000..e3cef2cec
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
@@ -0,0 +1,73 @@
+# Copyright 2025 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.cf_http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ object: bundle-95c1b0e5b92dae8333539b1e0ad5173b.zip
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+ module.cf_http.google_vpc_access_connector.connector[0]:
+ ip_cidr_range: null
+ machine_type: e2-standard-4
+ max_throughput: 300
+ min_throughput: 200
+ name: test-cf-http
+ project: project-id
+ region: europe-west8
+ subnet:
+ - name: fixture-subnet-28
+ project_id: test-host
+ timeouts: null
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v2/examples/service-vpc-access-connector-create.yaml b/tests/modules/cloud_function_v2/examples/service-vpc-access-connector-create.yaml
new file mode 100644
index 000000000..ba62ca04a
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/service-vpc-access-connector-create.yaml
@@ -0,0 +1,62 @@
+# Copyright 2025 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.cf_http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ object: bundle-95c1b0e5b92dae8333539b1e0ad5173b.zip
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 2
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v2/examples/service-vpc-access-connector.yaml b/tests/modules/cloud_function_v2/examples/service-vpc-access-connector.yaml
new file mode 100644
index 000000000..ba62ca04a
--- /dev/null
+++ b/tests/modules/cloud_function_v2/examples/service-vpc-access-connector.yaml
@@ -0,0 +1,62 @@
+# Copyright 2025 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.cf_http.google_cloudfunctions2_function.function:
+ build_config:
+ - entry_point: main
+ on_deploy_update_policy: []
+ runtime: python310
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ object: bundle-95c1b0e5b92dae8333539b1e0ad5173b.zip
+ worker_pool: null
+ description: Terraform managed.
+ effective_labels:
+ goog-terraform-provisioned: 'true'
+ event_trigger: []
+ kms_key_name: null
+ labels: null
+ location: europe-west8
+ name: test-cf-http
+ project: project-id
+ service_config:
+ - all_traffic_on_latest_revision: true
+ available_cpu: '0.166'
+ available_memory: 256M
+ binary_authorization_policy: null
+ environment_variables:
+ LOG_EXECUTION_ID: 'true'
+ ingress_settings: ALLOW_ALL
+ max_instance_count: 1
+ min_instance_count: 0
+ secret_environment_variables: []
+ secret_volumes: []
+ service_account_email: test-cf-http@project-id.iam.gserviceaccount.com
+ timeout_seconds: 180
+ vpc_connector_egress_settings: null
+ terraform_labels:
+ goog-terraform-provisioned: 'true'
+ timeouts: null
+
+counts:
+ google_cloudfunctions2_function: 1
+ google_project_iam_member: 2
+ google_service_account: 1
+ google_storage_bucket_object: 1
+
+
+outputs: {}
diff --git a/tests/modules/cloud_function_v2/kms.tfvars b/tests/modules/cloud_function_v2/kms.tfvars
new file mode 100644
index 000000000..2fbed27c2
--- /dev/null
+++ b/tests/modules/cloud_function_v2/kms.tfvars
@@ -0,0 +1,9 @@
+project_id = "project"
+region = "region"
+name = "test-cf-kms"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+kms_key = "kms_key_id"
+docker_repository_id = "artifact_registry_id"
diff --git a/tests/modules/cloud_function_v2/kms.yaml b/tests/modules/cloud_function_v2/kms.yaml
new file mode 100644
index 000000000..882ac992a
--- /dev/null
+++ b/tests/modules/cloud_function_v2/kms.yaml
@@ -0,0 +1,44 @@
+# Copyright 2025 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:
+ google_cloudfunctions2_function.function:
+ build_config:
+ - docker_repository: artifact_registry_id
+ source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ object: sample-function.zip
+ kms_key_name: kms_key_id
+ name: test-cf-kms
+ project: project
+ location: region
+ service_config:
+ - service_account_email: test-cf-kms@project.iam.gserviceaccount.com
+
+counts:
+ google_cloudfunctions2_function: 1
+
+outputs:
+ bucket: null
+ bucket_name: bucket
+ function: __missing__
+ function_name: test-cf-kms
+ id: __missing__
+ invoke_command: __missing__
+ service_account: __missing__
+ service_account_email: test-cf-kms@project.iam.gserviceaccount.com
+ service_account_iam_email: serviceAccount:test-cf-kms@project.iam.gserviceaccount.com
+ vpc_connector: null
diff --git a/tests/modules/cloud_function_v2/tftest.yaml b/tests/modules/cloud_function_v2/tftest.yaml
new file mode 100644
index 000000000..1ed90ec39
--- /dev/null
+++ b/tests/modules/cloud_function_v2/tftest.yaml
@@ -0,0 +1,21 @@
+# Copyright 2025 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.
+
+module: modules/cloud-function-v2
+tests:
+ context:
+ context-subnet:
+ context-subnet-project:
+ kms:
+ vpcconnector:
diff --git a/tests/modules/cloud_function_v2/vpcconnector.tfvars b/tests/modules/cloud_function_v2/vpcconnector.tfvars
new file mode 100644
index 000000000..2df2acf7b
--- /dev/null
+++ b/tests/modules/cloud_function_v2/vpcconnector.tfvars
@@ -0,0 +1,11 @@
+project_id = "test-project"
+region = "region"
+name = "test-cf-vpc"
+bucket_name = "bucket"
+bundle_config = {
+ path = "gs://assets/sample-function.zip"
+}
+vpc_connector = {
+ name = "projects/test-project/locations/region/connectors/vpc-connector"
+ egress_settings = "ALL_TRAFFIC"
+}
diff --git a/tests/modules/cloud_function_v2/vpcconnector.yaml b/tests/modules/cloud_function_v2/vpcconnector.yaml
new file mode 100644
index 000000000..d66ace823
--- /dev/null
+++ b/tests/modules/cloud_function_v2/vpcconnector.yaml
@@ -0,0 +1,44 @@
+# Copyright 2025 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:
+ google_cloudfunctions2_function.function:
+ build_config:
+ - source:
+ - repo_source: []
+ storage_source:
+ - bucket: bucket
+ object: sample-function.zip
+ name: test-cf-vpc
+ project: test-project
+ location: region
+ service_config:
+ - service_account_email: test-cf-vpc@test-project.iam.gserviceaccount.com
+ vpc_connector: projects/test-project/locations/region/connectors/vpc-connector
+ vpc_connector_egress_settings: ALL_TRAFFIC
+
+counts:
+ google_cloudfunctions2_function: 1
+
+outputs:
+ bucket: null
+ bucket_name: bucket
+ function: __missing__
+ function_name: test-cf-vpc
+ id: __missing__
+ invoke_command: __missing__
+ service_account: __missing__
+ service_account_email: test-cf-vpc@test-project.iam.gserviceaccount.com
+ service_account_iam_email: serviceAccount:test-cf-vpc@test-project.iam.gserviceaccount.com
+ vpc_connector: null
diff --git a/tests/modules/cloud_run_v2/examples/service-vpc-access-connector.yaml b/tests/modules/cloud_run_v2/examples/service-vpc-access-connector.yaml
index 4c94468c7..bee8ea643 100644
--- a/tests/modules/cloud_run_v2/examples/service-vpc-access-connector.yaml
+++ b/tests/modules/cloud_run_v2/examples/service-vpc-access-connector.yaml
@@ -14,7 +14,7 @@
values:
module.cloud_run.google_cloud_run_v2_service.service[0]:
- location: europe-west8
+ location: europe-west9
name: hello
project: project-id
template:
@@ -42,4 +42,3 @@ counts:
resources: 2
outputs: {}
-
diff --git a/tools/duplicate-diff.py b/tools/duplicate-diff.py
index b5b8c3915..c19af5b1b 100755
--- a/tools/duplicate-diff.py
+++ b/tools/duplicate-diff.py
@@ -139,6 +139,31 @@ duplicates = [
"modules/project/schemas/tags.schema.json",
"modules/organization/schemas/tags.schema.json",
],
+ [
+ "fast/stages/2-networking-legacy-a-simple/data/cidrs.yaml",
+ "fast/stages/2-networking-legacy-b-nva/data/cidrs.yaml",
+ "fast/stages/2-networking-legacy-c-separate-envs/data/cidrs.yaml",
+ ],
+ [
+ "modules/cloud-function-v1/bundle.tf",
+ "modules/cloud-function-v2/bundle.tf",
+ ],
+ [
+ "modules/cloud-function-v1/serviceaccount.tf",
+ "modules/cloud-function-v2/serviceaccount.tf",
+ ],
+ [
+ "modules/cloud-function-v1/variables-serviceaccount.tf",
+ "modules/cloud-function-v2/variables-serviceaccount.tf",
+ ],
+ [
+ "modules/cloud-function-v1/variables-vpcconnector.tf",
+ "modules/cloud-function-v2/variables-vpcconnector.tf",
+ ],
+ [
+ "modules/cloud-function-v1/vpcconnector.tf",
+ "modules/cloud-function-v2/vpcconnector.tf",
+ ],
]
@@ -167,6 +192,7 @@ def check_dir_diff(dcmp):
has_diff = False
+
for group in duplicates:
first = group[0]
if not os.path.exists(first):