├── test ├── .gitignore ├── setup │ ├── .gitignore │ ├── versions.tf │ ├── variables.tf │ ├── outputs.tf │ ├── main.tf │ └── iam.tf ├── install_build_dependencies.sh └── integration │ ├── discover_test.go │ ├── cloud_function2_gcs_source │ └── cloud_function2_gcs_source_test.go │ ├── cloud_function2_pubsub_trigger │ └── cloud_function2_pubsub_trigger_test.go │ ├── go.mod │ └── secure_cloud_function_internal_server │ └── cloud_function_internal_server_test.go ├── helpers ├── sample_function_py.zip └── generate_swp_certificate.sh ├── examples ├── secure_cloud_function_internal_server │ ├── function │ │ ├── go.mod │ │ └── main.go │ ├── providers.tf │ ├── terraform.tfvars.example │ ├── web_server │ │ └── internal_server_setup.sh │ ├── outputs.tf │ ├── internal_server.tf │ └── variables.tf ├── secure_cloud_function_bigquery_trigger │ ├── functions │ │ └── bq-to-cf │ │ │ ├── go.mod │ │ │ └── main.go │ ├── providers.tf │ ├── terraform.tfvars.example │ ├── templates │ │ └── bigquery_schema.template │ ├── outputs.tf │ └── variables.tf ├── secure_cloud_function_with_sql │ ├── providers.tf │ ├── terraform.tfvars.example │ ├── helpers │ │ ├── destroy_db_user.sh │ │ └── create_db_user.sh │ ├── functions │ │ └── cf-to-sql │ │ │ ├── go.mod │ │ │ └── main.go │ ├── assets │ │ └── sample-db-data.sql │ ├── variables.tf │ └── outputs.tf ├── cloud_function2_gcs_source │ ├── variables.tf │ ├── README.md │ ├── outputs.tf │ └── main.tf └── cloud_function2_pubsub_trigger │ ├── variables.tf │ ├── README.md │ ├── outputs.tf │ └── main.tf ├── .github ├── renovate.json ├── release-please.yml ├── trusted-contribution.yml └── workflows │ ├── stale.yml │ └── lint.yaml ├── .dockerignore ├── SECURITY.md ├── CODEOWNERS ├── modules ├── secure-web-proxy │ ├── outputs.tf │ ├── versions.tf │ ├── variables.tf │ └── main.tf ├── secure-cloud-function-security │ ├── outputs.tf │ ├── versions.tf │ ├── kms.tf │ ├── iam.tf │ ├── org_policies.tf │ ├── variables.tf │ └── README.md ├── secure-cloud-function-core │ ├── versions.tf │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md └── secure-cloud-function │ ├── versions.tf │ ├── outputs.tf │ └── main.tf ├── .kitchen.yml ├── outputs.tf ├── .gitignore ├── versions.tf ├── ERRATA.md ├── docs └── secure-web-proxy.md ├── function_describe.json ├── Makefile ├── CONTRIBUTING.md ├── variables.tf ├── metadata.yaml ├── main.tf └── README.md /test/.gitignore: -------------------------------------------------------------------------------- 1 | source.sh 2 | -------------------------------------------------------------------------------- /test/setup/.gitignore: -------------------------------------------------------------------------------- 1 | terraform.tfvars 2 | source.sh 3 | -------------------------------------------------------------------------------- /helpers/sample_function_py.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/terraform-google-cloud-functions/HEAD/helpers/sample_function_py.zip -------------------------------------------------------------------------------- /examples/secure_cloud_function_internal_server/function/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/module/helloworld 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/GoogleCloudPlatform/functions-framework-go v1.6.1 7 | ) 8 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>GoogleCloudPlatform/cloud-foundation-toolkit//infra/terraform/test-org/github/resources/renovate"] 4 | } 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .terraform 3 | .terraform.d 4 | .kitchen 5 | terraform.tfstate.d 6 | test/fixtures/*/.terraform 7 | test/fixtures/*/terraform.tfstate.d 8 | examples/.kitchen 9 | examples/*/.terraform 10 | examples/*/terraform.tfstate.d 11 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | To report a security issue, please use http://g.co/vulnz. We use 2 | http://g.co/vulnz for our intake, and do coordination and disclosure here on 3 | GitHub (including using GitHub Security Advisory). The Google Security Team will 4 | respond within 5 working days of your report on g.co/vulnz. 5 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_bigquery_trigger/functions/bq-to-cf/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/module/helloworld 2 | 3 | go 1.21 4 | 5 | require ( 6 | cloud.google.com/go/storage v1.29.0 7 | github.com/GoogleCloudPlatform/functions-framework-go v1.6.1 8 | github.com/cloudevents/sdk-go/v2 v2.15.2 9 | golang.org/x/oauth2 v0.6.0 10 | google.golang.org/api v0.113.0 11 | ) 12 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # NOTE: This file is automatically generated from values at: 2 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/org/locals.tf 3 | 4 | * @GoogleCloudPlatform/blueprint-solutions @prabhu34 5 | 6 | # NOTE: GitHub CODEOWNERS locations: 7 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-and-branch-protection 8 | 9 | CODEOWNERS @GoogleCloudPlatform/blueprint-solutions 10 | .github/CODEOWNERS @GoogleCloudPlatform/blueprint-solutions 11 | docs/CODEOWNERS @GoogleCloudPlatform/blueprint-solutions 12 | 13 | -------------------------------------------------------------------------------- /.github/release-please.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | releaseType: terraform-module 16 | handleGHRelease: true 17 | primaryBranch: main 18 | bumpMinorPreMajor: true 19 | -------------------------------------------------------------------------------- /test/install_build_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2023 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # OpenSSL is needed to generate a self-signed certificate 18 | apk add --no-cache openssl 19 | -------------------------------------------------------------------------------- /modules/secure-web-proxy/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "global_address_name" { 18 | description = "Google compute global address name." 19 | value = google_compute_global_address.private_ip_allocation.name 20 | } 21 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | --- 16 | driver: 17 | name: terraform 18 | verify_version: false 19 | 20 | provisioner: 21 | name: terraform 22 | 23 | verifier: 24 | name: terraform 25 | 26 | platforms: 27 | - name: default 28 | 29 | suites: 30 | - name: default 31 | -------------------------------------------------------------------------------- /test/integration/discover_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 21 | ) 22 | 23 | func TestAll(t *testing.T) { 24 | tft.AutoDiscoverAndTest(t) 25 | } 26 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "function_uri" { 18 | description = "URI of the Cloud Function (Gen 2)" 19 | value = google_cloudfunctions2_function.function.service_config[0].uri 20 | } 21 | 22 | output "function_name" { 23 | description = "Name of the Cloud Function (Gen 2)" 24 | value = var.function_name 25 | } 26 | -------------------------------------------------------------------------------- /test/setup/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | required_providers { 20 | google = { 21 | source = "hashicorp/google" 22 | version = ">= 3.25.0" 23 | } 24 | google-beta = { 25 | source = "hashicorp/google-beta" 26 | version = ">= 3.25.0" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/providers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | provider "google" { 18 | impersonate_service_account = var.terraform_service_account 19 | request_timeout = "60s" 20 | } 21 | 22 | provider "google-beta" { 23 | impersonate_service_account = var.terraform_service_account 24 | request_timeout = "60s" 25 | } 26 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_bigquery_trigger/providers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | provider "google" { 18 | impersonate_service_account = var.terraform_service_account 19 | request_timeout = "60s" 20 | } 21 | 22 | provider "google-beta" { 23 | impersonate_service_account = var.terraform_service_account 24 | request_timeout = "60s" 25 | } 26 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_internal_server/providers.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | provider "google" { 18 | impersonate_service_account = var.terraform_service_account 19 | request_timeout = "60s" 20 | } 21 | 22 | provider "google-beta" { 23 | impersonate_service_account = var.terraform_service_account 24 | request_timeout = "60s" 25 | } 26 | -------------------------------------------------------------------------------- /examples/cloud_function2_gcs_source/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "project_id" { 18 | description = "The ID of the project in which to provision resources." 19 | type = string 20 | } 21 | 22 | variable "function_location" { 23 | description = "The location of this cloud function" 24 | type = string 25 | default = "us-central1" 26 | } 27 | -------------------------------------------------------------------------------- /examples/cloud_function2_pubsub_trigger/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "project_id" { 18 | description = "The ID of the project in which to provision resources." 19 | type = string 20 | } 21 | 22 | variable "function_location" { 23 | description = "The location of this cloud function" 24 | type = string 25 | default = "us-central1" 26 | } 27 | -------------------------------------------------------------------------------- /.github/trusted-contribution.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/github 17 | 18 | annotations: 19 | - type: comment 20 | text: "/gcbrun" 21 | trustedContributors: 22 | - release-please[bot] 23 | - renovate[bot] 24 | - renovate-bot 25 | - forking-renovate[bot] 26 | - dependabot[bot] 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX leaves these everywhere on SMB shares 2 | ._* 3 | 4 | # OSX trash 5 | .DS_Store 6 | 7 | # Python 8 | *.pyc 9 | 10 | # Emacs save files 11 | *~ 12 | \#*\# 13 | .\#* 14 | 15 | # Vim-related files 16 | [._]*.s[a-w][a-z] 17 | [._]s[a-w][a-z] 18 | *.un~ 19 | Session.vim 20 | .netrwhist 21 | 22 | ### https://raw.github.com/github/gitignore/90f149de451a5433aebd94d02d11b0e28843a1af/Terraform.gitignore 23 | 24 | # Local .terraform directories 25 | **/.terraform/* 26 | 27 | # .tfstate files 28 | *.tfstate 29 | *.tfstate.* 30 | 31 | # Crash log files 32 | crash.log 33 | 34 | # Kitchen files 35 | **/inspec.lock 36 | **/.kitchen 37 | **/kitchen.local.yml 38 | **/Gemfile.lock 39 | 40 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 41 | # .tfvars files are managed as part of configuration and so should be included in 42 | # version control. 43 | **/*.tfvars 44 | 45 | credentials.json 46 | 47 | # tf lock file 48 | .terraform.lock.hcl 49 | 50 | # zip files 51 | **/*.zip 52 | 53 | **.vscode 54 | **.pem 55 | -------------------------------------------------------------------------------- /test/setup/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | variable "org_id" { 17 | description = "The numeric organization id" 18 | type = string 19 | } 20 | 21 | variable "folder_id" { 22 | description = "The folder to deploy in" 23 | type = string 24 | } 25 | 26 | variable "billing_account" { 27 | description = "The billing account id associated with the project, e.g. XXXXXX-YYYYYY-ZZZZZZ" 28 | type = string 29 | } 30 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "key_self_link" { 18 | description = "Key self link." 19 | value = module.cloud_function_kms.keys[var.key_name] 20 | } 21 | 22 | output "keyring_self_link" { 23 | description = "Self link of the keyring." 24 | value = module.cloud_function_kms.keyring 25 | } 26 | 27 | output "keyring_resource" { 28 | description = "Keyring resource." 29 | value = module.cloud_function_kms.keyring_resource 30 | } 31 | -------------------------------------------------------------------------------- /examples/cloud_function2_gcs_source/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example 2 | 3 | This example illustrates how to use the `cloud-functions` module. 4 | 5 | 6 | ## Inputs 7 | 8 | | Name | Description | Type | Default | Required | 9 | |------|-------------|------|---------|:--------:| 10 | | function\_location | The location of this cloud function | `string` | `"us-central1"` | no | 11 | | project\_id | The ID of the project in which to provision resources. | `string` | n/a | yes | 12 | 13 | ## Outputs 14 | 15 | | Name | Description | 16 | |------|-------------| 17 | | function\_location | Location of the Cloud Function (Gen 2) | 18 | | function\_name | Name of the Cloud Function (Gen 2) | 19 | | function\_uri | URI of the Cloud Function (Gen 2) | 20 | | project\_id | The project ID | 21 | 22 | 23 | 24 | To provision this example, run the following from within this directory: 25 | - `terraform init` to get the plugins 26 | - `terraform plan` to see the infrastructure plan 27 | - `terraform apply` to apply the infrastructure build 28 | - `terraform destroy` to destroy the built infrastructure 29 | -------------------------------------------------------------------------------- /test/setup/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "folder_id" { 18 | value = google_folder.ci-iam-folder.folder_id 19 | } 20 | 21 | output "project_id" { 22 | value = module.project.project_id 23 | } 24 | 25 | output "sa_key" { 26 | value = google_service_account_key.int_test.private_key 27 | sensitive = true 28 | } 29 | 30 | output "terraform_service_account" { 31 | value = google_service_account.int_test.email 32 | } 33 | 34 | output "access_level_members" { 35 | value = ["serviceAccount:${google_service_account.int_test.email}"] 36 | } 37 | -------------------------------------------------------------------------------- /modules/secure-web-proxy/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | 20 | required_providers { 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 5.12, < 7" 24 | } 25 | 26 | google-beta = { 27 | source = "hashicorp/google-beta" 28 | version = "< 7" 29 | } 30 | 31 | null = { 32 | source = "hashicorp/null" 33 | version = ">= 3.2.0" 34 | } 35 | 36 | time = { 37 | source = "hashicorp/time" 38 | version = ">= 0.9.1" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/cloud_function2_pubsub_trigger/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example 2 | 3 | This example illustrates how to use the `cloud-functions` module. 4 | 5 | 6 | ## Inputs 7 | 8 | | Name | Description | Type | Default | Required | 9 | |------|-------------|------|---------|:--------:| 10 | | function\_location | The location of this cloud function | `string` | `"us-central1"` | no | 11 | | project\_id | The ID of the project in which to provision resources. | `string` | n/a | yes | 12 | 13 | ## Outputs 14 | 15 | | Name | Description | 16 | |------|-------------| 17 | | function\_location | Location of the Cloud Function (Gen 2) | 18 | | function\_name | Name of the Cloud Function (Gen 2) | 19 | | function\_uri | URI of the Cloud Function (Gen 2) | 20 | | project\_id | The project ID | 21 | | pubsub\_topic | Name of the PubSub Topic | 22 | 23 | 24 | 25 | To provision this example, run the following from within this directory: 26 | - `terraform init` to get the plugins 27 | - `terraform plan` to see the infrastructure plan 28 | - `terraform apply` to apply the infrastructure build 29 | - `terraform destroy` to destroy the built infrastructure 30 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | required_providers { 20 | google = { 21 | source = "hashicorp/google" 22 | version = ">= 4.48, < 7" 23 | } 24 | google-beta = { 25 | source = "hashicorp/google-beta" 26 | version = ">= 4.48, < 7" 27 | } 28 | } 29 | 30 | provider_meta "google" { 31 | module_name = "blueprints/terraform/terraform-google-cloud-functions/v0.7.0" 32 | } 33 | provider_meta "google-beta" { 34 | module_name = "blueprints/terraform/terraform-google-cloud-functions/v0.7.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | # /** 2 | # * Copyright 2023 Google LLC 3 | # * 4 | # * Licensed under the Apache License, Version 2.0 (the "License"); 5 | # * you may not use this file except in compliance with the License. 6 | # * You may obtain a copy of the License at 7 | # * 8 | # * http://www.apache.org/licenses/LICENSE-2.0 9 | # * 10 | # * Unless required by applicable law or agreed to in writing, software 11 | # * distributed under the License is distributed on an "AS IS" BASIS, 12 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # * See the License for the specific language governing permissions and 14 | # * limitations under the License. 15 | # */ 16 | 17 | billing_account = "000000-000000-000000" 18 | org_id = "000000000000000000" 19 | folder_id = "000000000000" 20 | create_access_context_manager_access_policy = false 21 | access_context_manager_policy_id = "000000000000" 22 | access_level_members = ["user:email@email.com"] 23 | terraform_service_account = "ci-account@PROJECT.iam.gserviceaccount.com" 24 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_bigquery_trigger/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | # /** 2 | # * Copyright 2023 Google LLC 3 | # * 4 | # * Licensed under the Apache License, Version 2.0 (the "License"); 5 | # * you may not use this file except in compliance with the License. 6 | # * You may obtain a copy of the License at 7 | # * 8 | # * http://www.apache.org/licenses/LICENSE-2.0 9 | # * 10 | # * Unless required by applicable law or agreed to in writing, software 11 | # * distributed under the License is distributed on an "AS IS" BASIS, 12 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # * See the License for the specific language governing permissions and 14 | # * limitations under the License. 15 | # */ 16 | 17 | billing_account = "000000-000000-000000" 18 | org_id = "000000000000000000" 19 | folder_id = "000000000000" 20 | create_access_context_manager_access_policy = false 21 | access_context_manager_policy_id = "000000000000" 22 | access_level_members = ["user:email@email.com"] 23 | terraform_service_account = "ci-account@PROJECT.iam.gserviceaccount.com" 24 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_internal_server/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | # /** 2 | # * Copyright 2023 Google LLC 3 | # * 4 | # * Licensed under the Apache License, Version 2.0 (the "License"); 5 | # * you may not use this file except in compliance with the License. 6 | # * You may obtain a copy of the License at 7 | # * 8 | # * http://www.apache.org/licenses/LICENSE-2.0 9 | # * 10 | # * Unless required by applicable law or agreed to in writing, software 11 | # * distributed under the License is distributed on an "AS IS" BASIS, 12 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # * See the License for the specific language governing permissions and 14 | # * limitations under the License. 15 | # */ 16 | 17 | billing_account = "000000-000000-000000" 18 | org_id = "000000000000000000" 19 | folder_id = "000000000000" 20 | create_access_context_manager_access_policy = false 21 | access_context_manager_policy_id = "000000000000" 22 | access_level_members = ["user:email@email.com"] 23 | terraform_service_account = "ci-account@PROJECT.iam.gserviceaccount.com" 24 | -------------------------------------------------------------------------------- /examples/cloud_function2_gcs_source/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "function_uri" { 18 | description = "URI of the Cloud Function (Gen 2)" 19 | value = module.cloud_functions2.function_uri 20 | } 21 | 22 | output "function_name" { 23 | description = "Name of the Cloud Function (Gen 2)" 24 | value = module.cloud_functions2.function_name 25 | } 26 | 27 | output "function_location" { 28 | description = "Location of the Cloud Function (Gen 2)" 29 | value = var.function_location 30 | } 31 | 32 | output "project_id" { 33 | value = var.project_id 34 | description = "The project ID" 35 | } 36 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-core/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | 20 | required_providers { 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 4.74, < 7.0" 24 | } 25 | google-beta = { 26 | source = "hashicorp/google-beta" 27 | version = "< 7.0" 28 | } 29 | } 30 | provider_meta "google" { 31 | module_name = "blueprints/terraform/terraform-google-cloud-functions:secure-cloud-function-core/v0.7.0" 32 | } 33 | 34 | provider_meta "google-beta" { 35 | module_name = "blueprints/terraform/terraform-google-cloud-functions:secure-cloud-function-core/v0.7.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | 20 | required_providers { 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 4.74, < 7.0" 24 | } 25 | google-beta = { 26 | source = "hashicorp/google-beta" 27 | version = "< 7.0" 28 | } 29 | } 30 | 31 | provider_meta "google" { 32 | module_name = "blueprints/terraform/terraform-google-cloud-functions:secure-cloud-function-security/v0.7.0" 33 | } 34 | 35 | provider_meta "google-beta" { 36 | module_name = "blueprints/terraform/terraform-google-cloud-functions:secure-cloud-function-security/v0.7.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/helpers/destroy_db_user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2023 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Important information for understanding the script: 18 | # https://cloud.google.com/kms/docs/encrypt-decrypt 19 | # https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets 20 | 21 | set -e 22 | 23 | terraform_service_account=${1} 24 | instance_name=${2} 25 | instance_project_id=${3} 26 | user_name=${4} 27 | host=${5} 28 | 29 | destroy_user() { 30 | 31 | gcloud sql users delete "${user_name}" \ 32 | --instance "${instance_name}" \ 33 | --impersonate-service-account="${terraform_service_account}" \ 34 | --host="${host}" \ 35 | --project="${instance_project_id}" -q 36 | } 37 | 38 | destroy_user 39 | -------------------------------------------------------------------------------- /examples/cloud_function2_pubsub_trigger/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "function_uri" { 18 | description = "URI of the Cloud Function (Gen 2)" 19 | value = module.cloud_functions2.function_uri 20 | } 21 | 22 | output "function_name" { 23 | description = "Name of the Cloud Function (Gen 2)" 24 | value = module.cloud_functions2.function_name 25 | } 26 | 27 | output "function_location" { 28 | description = "Location of the Cloud Function (Gen 2)" 29 | value = var.function_location 30 | } 31 | 32 | output "pubsub_topic" { 33 | description = "Name of the PubSub Topic" 34 | value = module.pubsub.topic 35 | } 36 | 37 | output "project_id" { 38 | value = var.project_id 39 | description = "The project ID" 40 | } 41 | -------------------------------------------------------------------------------- /modules/secure-cloud-function/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | 20 | required_providers { 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 4.74, < 7.0" 24 | } 25 | google-beta = { 26 | source = "hashicorp/google-beta" 27 | version = "< 7.0" 28 | } 29 | 30 | time = { 31 | source = "hashicorp/time" 32 | version = ">= 0.9.1" 33 | } 34 | } 35 | 36 | provider_meta "google" { 37 | module_name = "blueprints/terraform/terraform-google-cloud-run:secure-cloud-run/v0.7.0" 38 | } 39 | 40 | provider_meta "google-beta" { 41 | module_name = "blueprints/terraform/terraform-google-cloud-run:secure-cloud-run/v0.7.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /helpers/generate_swp_certificate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2023 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | project_id=${1} 18 | location=${2} 19 | 20 | generate_self_signed_certificate() { 21 | if [[ ! -x "$(command -v openssl)" ]]; then 22 | echo "openssl not found" 23 | exit 1 24 | fi 25 | 26 | openssl req -x509 -newkey rsa:2048 \ 27 | -keyout key.pem \ 28 | -out cert.pem -days 365 \ 29 | -subj '/CN=myswp.example.com' -nodes \ 30 | -addext "subjectAltName=DNS:myswp.example.com" 31 | 32 | gcloud certificate-manager certificates create swp-certificate \ 33 | --certificate-file=cert.pem \ 34 | --private-key-file=key.pem \ 35 | --location="${location}" \ 36 | --project="${project_id}" 37 | } 38 | generate_self_signed_certificate 39 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_bigquery_trigger/templates/bigquery_schema.template: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Card_Type_Code", 4 | "mode": "NULLABLE", 5 | "type": "STRING" 6 | }, 7 | { 8 | "name": "Card_Type_Full_Name", 9 | "mode": "NULLABLE", 10 | "type": "STRING" 11 | }, 12 | { 13 | "name": "Issuing_Bank", 14 | "mode": "NULLABLE", 15 | "type": "STRING" 16 | }, 17 | { 18 | "name": "Card_Number", 19 | "mode": "NULLABLE", 20 | "type": "STRING" 21 | }, 22 | { 23 | "name": "Card_Holders_Name", 24 | "mode": "NULLABLE", 25 | "type": "STRING" 26 | }, 27 | { 28 | "name": "CVV_CVV2", 29 | "mode": "NULLABLE", 30 | "type": "STRING" 31 | }, 32 | { 33 | "name": "Issue_Date", 34 | "mode": "NULLABLE", 35 | "type": "STRING" 36 | }, 37 | { 38 | "name": "Expiry_Date", 39 | "mode": "NULLABLE", 40 | "type": "STRING" 41 | }, 42 | { 43 | "name": "Billing_Date", 44 | "mode": "NULLABLE", 45 | "type": "STRING" 46 | }, 47 | { 48 | "name": "Card_PIN", 49 | "mode": "NULLABLE", 50 | "type": "INT64" 51 | }, 52 | { 53 | "name": "Credit_Limit", 54 | "mode": "NULLABLE", 55 | "type": "STRING" 56 | } 57 | ] 58 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/kms.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "cloud_function_kms" { 18 | source = "terraform-google-modules/kms/google" 19 | version = "~> 4.0" 20 | 21 | project_id = var.kms_project_id 22 | location = var.location 23 | keyring = var.keyring_name 24 | keys = [var.key_name] 25 | set_decrypters_for = length(var.decrypters) > 0 ? [var.key_name] : [] 26 | set_encrypters_for = length(var.encrypters) > 0 ? [var.key_name] : [] 27 | decrypters = [join(",", var.decrypters)] 28 | encrypters = [join(",", var.encrypters)] 29 | set_owners_for = length(var.owners) > 0 ? [var.key_name] : [] 30 | owners = [join(",", var.owners)] 31 | prevent_destroy = var.prevent_destroy 32 | key_rotation_period = var.key_rotation_period 33 | key_protection_level = var.key_protection_level 34 | } 35 | -------------------------------------------------------------------------------- /examples/cloud_function2_gcs_source/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_storage_bucket" "bucket" { 18 | name = "${var.project_id}-gcf-source" 19 | location = "US" 20 | uniform_bucket_level_access = true 21 | project = var.project_id 22 | } 23 | 24 | resource "google_storage_bucket_object" "function-source" { 25 | name = "sample_function_py.zip" 26 | bucket = google_storage_bucket.bucket.name 27 | source = "../../helpers/sample_function_py.zip" 28 | } 29 | 30 | module "cloud_functions2" { 31 | source = "GoogleCloudPlatform/cloud-functions/google" 32 | version = "~> 0.6" 33 | 34 | project_id = var.project_id 35 | function_name = "function2-gcs-source-py" 36 | function_location = var.function_location 37 | runtime = "python38" 38 | entrypoint = "hello_http" 39 | storage_source = { 40 | bucket = google_storage_bucket.bucket.name 41 | object = google_storage_bucket_object.function-source.name 42 | generation = null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/helpers/create_db_user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2023 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Important information for understanding the script: 18 | # https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets 19 | 20 | set -e 21 | 22 | terraform_service_account=${1} 23 | instance_name=${2} 24 | instance_project_id=${3} 25 | secret_name=${4} 26 | secret_project_id=${5} 27 | user_name=${6} 28 | host=${7} 29 | 30 | create_user_and_save_pwd_in_secret() { 31 | 32 | pwd=$(echo $RANDOM | md5sum | head -c 20; echo;) 33 | password=$(echo "${pwd}" | base64) 34 | 35 | gcloud sql users create "${user_name}" \ 36 | --instance "${instance_name}" \ 37 | --impersonate-service-account="${terraform_service_account}" \ 38 | --password="${password}" --host="${host}" \ 39 | --type="BUILT_IN" \ 40 | --project="${instance_project_id}" 41 | 42 | 43 | printf "%s" "${password}" | \ 44 | gcloud secrets versions add "${secret_name}" \ 45 | --data-file=- \ 46 | --impersonate-service-account="${terraform_service_account}" \ 47 | --project="${secret_project_id}" 48 | } 49 | 50 | create_user_and_save_pwd_in_secret 51 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/functions/cf-to-sql/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/cloudsql 2 | 3 | go 1.21 4 | 5 | require ( 6 | cloud.google.com/go/cloudsqlconn v1.2.3 7 | github.com/GoogleCloudPlatform/functions-framework-go v1.7.1 8 | github.com/cloudevents/sdk-go/v2 v2.15.2 9 | github.com/go-sql-driver/mysql v1.7.1 10 | golang.org/x/sync v0.1.0 11 | ) 12 | 13 | require ( 14 | cloud.google.com/go/compute v1.19.0 // indirect 15 | cloud.google.com/go/compute/metadata v0.2.3 // indirect 16 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect 17 | github.com/golang/protobuf v1.5.3 // indirect 18 | github.com/google/s2a-go v0.1.0 // indirect 19 | github.com/google/uuid v1.3.0 // indirect 20 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect 21 | github.com/googleapis/gax-go/v2 v2.8.0 // indirect 22 | github.com/json-iterator/go v1.1.10 // indirect 23 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect 24 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect 25 | go.opencensus.io v0.24.0 // indirect 26 | go.uber.org/atomic v1.4.0 // indirect 27 | go.uber.org/multierr v1.1.0 // indirect 28 | go.uber.org/zap v1.10.0 // indirect 29 | golang.org/x/crypto v0.21.0 // indirect 30 | golang.org/x/net v0.23.0 // indirect 31 | golang.org/x/oauth2 v0.7.0 // indirect 32 | golang.org/x/sys v0.18.0 // indirect 33 | golang.org/x/text v0.14.0 // indirect 34 | golang.org/x/time v0.3.0 // indirect 35 | google.golang.org/api v0.117.0 // indirect 36 | google.golang.org/appengine v1.6.7 // indirect 37 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 38 | google.golang.org/grpc v1.54.0 // indirect 39 | google.golang.org/protobuf v1.33.0 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/github 17 | 18 | name: "Close stale issues" 19 | on: 20 | schedule: 21 | - cron: "0 23 * * *" 22 | 23 | permissions: 24 | contents: read 25 | issues: write 26 | pull-requests: write 27 | actions: write 28 | 29 | jobs: 30 | stale: 31 | if: github.repository_owner == 'GoogleCloudPlatform' || github.repository_owner == 'terraform-google-modules' 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/stale@v10 35 | with: 36 | repo-token: ${{ secrets.GITHUB_TOKEN }} 37 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days' 38 | stale-pr-message: 'This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days' 39 | exempt-issue-labels: 'triaged' 40 | exempt-pr-labels: 'dependencies,autorelease: pending' 41 | operations-per-run: 100 42 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-core/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "cloudfunction_name" { 18 | value = module.cloud_function.function_name 19 | description = "Name of the created service." 20 | } 21 | 22 | output "eventarc_google_channel_id" { 23 | value = google_eventarc_google_channel_config.primary.id 24 | description = "The ID of the Google Eventarc Channel." 25 | } 26 | 27 | output "artifact_registry_repository_id" { 28 | value = google_artifact_registry_repository.cloudfunction_repo.id 29 | description = "The ID of the Artifact Registry created to store Cloud Function images." 30 | } 31 | 32 | output "cloudbuild_worker_pool_id" { 33 | value = google_cloudbuild_worker_pool.pool.id 34 | description = "The ID of the Cloud Build worker pool created to build Cloud Function images." 35 | } 36 | 37 | output "cloudfunction_bucket_name" { 38 | value = module.cloudfunction_bucket.name 39 | description = "Name of the Cloud Function source bucket." 40 | } 41 | 42 | output "cloudfunction_bucket" { 43 | value = module.cloudfunction_bucket.bucket 44 | description = "The Cloud Function source bucket." 45 | } 46 | 47 | output "cloudfunction_url" { 48 | value = module.cloud_function.function_uri 49 | description = "The URL on which the deployed service is available." 50 | } 51 | -------------------------------------------------------------------------------- /ERRATA.md: -------------------------------------------------------------------------------- 1 | # Errata Summary 2 | 3 | This is an overview of the delta between the example Secure Serverless Functions architecture repository and the [Serverless architecture using Cloud Functions guide](https://cloud.google.com/architecture/serverless-functions-blueprint), including code discrepancies and notes on future automation. This document will be updated as new code is merged. 4 | 5 | ## [0.4.1](https://github.com/GoogleCloudPlatform/terraform-google-cloud-functions/releases/tag/v0.4.1) 6 | 7 | ### Blueprint Configuration 8 | 9 | #### Constraints 10 | 11 | The following constraints are being used for this blueprint: 12 | 13 | Cloud Functions 14 | - `constraints/cloudfunctions.allowedIngressSettings` 15 | - `constraints/cloudfunctions.requireVPCConnector` 16 | - `constraints/cloudfunctions.allowedVpcConnectorEgressSettings` 17 | 18 | Cloud Run 19 | - `constraints/run.allowedIngress` 20 | - `constraints/run.allowedVPCEgress` 21 | 22 | For this version, the Cloud Run constraints are being enforced instead of the Cloud Functions constraints during the deploy of Secured Cloud Function. This behaviour happens because Cloud Function Gen2 is using Cloud Run as the execution platform. 23 | 24 | #### Secure Web Proxy 25 | 26 | The [Secure Web Proxy](https://cloud.google.com/secure-web-proxy) is used to allow Cloud Functions build process to download code dependencies from external repositories in the internet. It should only be available during the build process execution. 27 | 28 | The Secure Web Proxy should be part of a defined deployment process that guarantees that it will be enabled only during the time necessary for the Cloud Build builds execution. The management of the Secure Web Proxy should be part of the build process instead of being running the whole time. 29 | 30 | #### Notes 31 | 32 | Please refer to [this documentation](https://github.com/renato-rudnicki/terraform-google-cloud-functions/blob/errata/docs/secure-web-proxy.md) for more details about how manually delete the Secure Web Proxy. 33 | 34 | -------------------------------------------------------------------------------- /test/setup/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "random_id" "folder-rand" { 18 | byte_length = 2 19 | } 20 | 21 | resource "google_folder" "ci-iam-folder" { 22 | display_name = "ci-cloud-functions-${random_id.folder-rand.hex}" 23 | parent = "folders/${var.folder_id}" 24 | } 25 | 26 | module "project" { 27 | source = "terraform-google-modules/project-factory/google" 28 | version = "~> 18.0" 29 | 30 | name = "ci-cloud-functions" 31 | random_project_id = "true" 32 | org_id = var.org_id 33 | folder_id = google_folder.ci-iam-folder.id 34 | billing_account = var.billing_account 35 | default_service_account = "keep" 36 | deletion_policy = "DELETE" 37 | 38 | activate_apis = [ 39 | "cloudresourcemanager.googleapis.com", 40 | "storage-api.googleapis.com", 41 | "serviceusage.googleapis.com", 42 | "cloudfunctions.googleapis.com", 43 | "run.googleapis.com", 44 | "cloudbuild.googleapis.com", 45 | "artifactregistry.googleapis.com", 46 | "pubsub.googleapis.com", 47 | "eventarc.googleapis.com", 48 | "iamcredentials.googleapis.com", 49 | "accesscontextmanager.googleapis.com", 50 | "iam.googleapis.com", 51 | "cloudbilling.googleapis.com", 52 | "cloudkms.googleapis.com", 53 | "bigquery.googleapis.com", 54 | "certificatemanager.googleapis.com", 55 | "sql-component.googleapis.com", 56 | "sqladmin.googleapis.com", 57 | "servicenetworking.googleapis.com" 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_internal_server/function/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package helloworld 16 | 17 | import ( 18 | "fmt" 19 | "io/ioutil" 20 | "log" 21 | "net/http" 22 | "os" 23 | 24 | "github.com/GoogleCloudPlatform/functions-framework-go/functions" 25 | ) 26 | 27 | func init() { 28 | functions.HTTP("helloHTTP", helloHTTP) 29 | } 30 | 31 | func helloHTTP(w http.ResponseWriter, r *http.Request) { 32 | ipAddress := os.Getenv("TARGET_IP") 33 | if ipAddress == "" { 34 | log.Println("TARGET_IP environment variable not set") 35 | http.Error(w, "TARGET_IP not set", http.StatusInternalServerError) 36 | return 37 | } 38 | 39 | url := fmt.Sprintf("http://%s:8000/index.html", ipAddress) 40 | 41 | // Send GET request to the server 42 | response, err := http.Get(url) 43 | if err != nil { 44 | log.Printf("Failed to send GET request: %s\n", err) 45 | http.Error(w, "Failed to send GET request", http.StatusInternalServerError) 46 | return 47 | } 48 | defer response.Body.Close() 49 | 50 | // Read the response body 51 | content, err := ioutil.ReadAll(response.Body) 52 | if err != nil { 53 | log.Printf("Failed to read response body: %s\n", err) 54 | http.Error(w, "Failed to read response body", http.StatusInternalServerError) 55 | return 56 | } 57 | 58 | // Log the content 59 | log.Printf("Message returned from internal server: %s\n", string(content)) 60 | 61 | // Write the content to the response 62 | w.Header().Set("Content-Type", "text/plain") 63 | w.WriteHeader(http.StatusOK) 64 | fmt.Fprint(w, string(content)) 65 | } 66 | -------------------------------------------------------------------------------- /examples/cloud_function2_pubsub_trigger/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_storage_bucket" "bucket" { 18 | name = "${var.project_id}-gcf-source-pubsub" 19 | location = "US" 20 | uniform_bucket_level_access = true 21 | project = var.project_id 22 | } 23 | 24 | resource "google_storage_bucket_object" "function-source" { 25 | name = "sample_function_py.zip" 26 | bucket = google_storage_bucket.bucket.name 27 | source = "../../helpers/sample_function_py.zip" 28 | } 29 | 30 | module "pubsub" { 31 | source = "terraform-google-modules/pubsub/google" 32 | version = "~> 7.0" 33 | 34 | topic = "function2-topic" 35 | project_id = var.project_id 36 | } 37 | 38 | module "cloud_functions2" { 39 | source = "GoogleCloudPlatform/cloud-functions/google" 40 | version = "~> 0.6" 41 | 42 | project_id = var.project_id 43 | function_name = "function2-pubsub-trigger-py" 44 | function_location = var.function_location 45 | runtime = "python38" 46 | entrypoint = "hello_http" 47 | storage_source = { 48 | bucket = google_storage_bucket.bucket.name 49 | object = google_storage_bucket_object.function-source.name 50 | generation = null 51 | } 52 | event_trigger = { 53 | trigger_region = "us-central1" 54 | event_type = "google.cloud.pubsub.topic.v1.messagePublished" 55 | service_account_email = null 56 | pubsub_topic = module.pubsub.id 57 | retry_policy = "RETRY_POLICY_RETRY" 58 | event_filters = null 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/iam.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_project_iam_member" "group_serverless_administrator_admin" { 18 | for_each = var.groups.group_serverless_administrator == null ? [] : toset(["roles/run.admin", "roles/cloudfunctions.admin", "roles/compute.networkViewer", "roles/compute.networkUser"]) 19 | 20 | project = var.serverless_project_id 21 | role = each.value 22 | member = "group:${var.groups.group_serverless_administrator}" 23 | } 24 | 25 | resource "google_project_iam_member" "group_serverless_security_administrator_viewer" { 26 | for_each = var.groups.group_serverless_security_administrator == null ? [] : toset(["roles/run.viewer", "roles/cloudfunctions.viewer", "roles/cloudkms.viewer", "roles/artifactregistry.reader"]) 27 | 28 | project = var.kms_project_id 29 | role = each.value 30 | member = "group:${var.groups.group_serverless_security_administrator}" 31 | } 32 | 33 | resource "google_project_iam_member" "group_cloud_function_developer_run_developer" { 34 | for_each = var.groups.group_cloud_function_developer == null ? [] : toset(["roles/cloudfunctions.developer", "roles/artifactregistry.writer", "cloudkms.cryptoKeyEncrypter"]) 35 | 36 | project = var.kms_project_id 37 | role = each.value 38 | member = "group:${var.groups.group_cloud_function_developer}" 39 | } 40 | 41 | resource "google_project_iam_member" "group_cloud_function_user_run_invoker" { 42 | for_each = var.groups.group_cloud_function_user == null ? [] : toset(["cloudfunctions.invoker", "run.invoker"]) 43 | 44 | project = var.serverless_project_id 45 | role = each.value 46 | member = "group:${var.groups.group_cloud_function_user}" 47 | } 48 | -------------------------------------------------------------------------------- /test/integration/cloud_function2_gcs_source/cloud_function2_gcs_source_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud_function2_gcs_source 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" 22 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestGCF2GCSSource(t *testing.T) { 27 | gcs_sourceT := tft.NewTFBlueprintTest(t) 28 | 29 | gcs_sourceT.DefineVerify(func(assert *assert.Assertions) { 30 | // Removing DefaultVerify because Cloud Function API is changing the build_config/source/storage_source/generation and this modification is breaking the build validation. 31 | // gcs_sourceT.DefaultVerify(assert) 32 | 33 | function_name := gcs_sourceT.GetStringOutput("function_name") 34 | projectID := gcs_sourceT.GetStringOutput("project_id") 35 | function_location := gcs_sourceT.GetStringOutput("function_location") 36 | 37 | function_cmd := gcloud.Run(t, "functions describe", gcloud.WithCommonArgs([]string{function_name, "--project", projectID, "--gen2", "--region", function_location, "--format", "json"})) 38 | 39 | // T01: Verify if the Cloud Functions deployed is in ACTIVE state 40 | assert.Equal("ACTIVE", function_cmd.Get("state").String(), fmt.Sprintf("Should be ACTIVE. Cloud Function is not successfully deployed.")) 41 | 42 | // T02: Verify if the Cloud Functions is deployed from Storage Source by verifying a non-empty block 43 | assert.NotEmpty(function_cmd.Get("buildConfig.source.storageSource").String(), fmt.Sprintf("Cloud Function is not deployed from Storage Source or maybe deployed from Repo Source")) 44 | }) 45 | gcs_sourceT.Test() 46 | } 47 | -------------------------------------------------------------------------------- /modules/secure-cloud-function/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "connector_id" { 18 | value = module.cloud_serverless_network.connector_id 19 | description = "VPC serverless connector ID." 20 | } 21 | 22 | output "keyring_self_link" { 23 | value = module.cloud_function_security.keyring_self_link 24 | description = "Name of the Cloud KMS keyring." 25 | } 26 | 27 | output "key_self_link" { 28 | value = module.cloud_function_security.key_self_link 29 | description = "Name of the Cloud KMS crypto key." 30 | } 31 | 32 | output "cloudfunction_name" { 33 | value = module.cloud_function_core.cloudfunction_name 34 | description = "ID of the created Cloud Function." 35 | } 36 | 37 | output "cloudfunction_url" { 38 | value = module.cloud_function_core.cloudfunction_url 39 | description = "Url of the created Cloud Function." 40 | } 41 | 42 | output "cloudfunction_bucket_name" { 43 | value = module.cloud_function_core.cloudfunction_bucket_name 44 | description = "The Cloud Function source bucket." 45 | } 46 | 47 | output "cloudfunction_bucket" { 48 | value = module.cloud_function_core.cloudfunction_bucket 49 | description = "The Cloud Function source bucket." 50 | } 51 | 52 | output "gca_vpcaccess_sa" { 53 | value = module.cloud_serverless_network.gca_vpcaccess_sa 54 | description = "Service Account for VPC Access." 55 | } 56 | 57 | output "cloud_services_sa" { 58 | value = module.cloud_serverless_network.cloud_services_sa 59 | description = "Service Account for Cloud Function." 60 | } 61 | 62 | output "serverless_identity_services_sa" { 63 | value = google_project_service_identity.cloudfunction_sa.email 64 | description = "Service Identity to serverless services." 65 | } 66 | -------------------------------------------------------------------------------- /test/integration/cloud_function2_pubsub_trigger/cloud_function2_pubsub_trigger_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud_function2_pubsub_trigger 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" 22 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestGCF2PubSubTrigger(t *testing.T) { 27 | pubsub_triggerT := tft.NewTFBlueprintTest(t) 28 | 29 | pubsub_triggerT.DefineVerify(func(assert *assert.Assertions) { 30 | // Removing DefaultVerify because Cloud Function API is changing the build_config/source/storage_source/generation and this modification is breaking the build validation. 31 | // pubsub_triggerT.DefaultVerify(assert) 32 | 33 | function_name := pubsub_triggerT.GetStringOutput("function_name") 34 | pubsubTopic := pubsub_triggerT.GetStringOutput("pubsub_topic") 35 | projectID := pubsub_triggerT.GetStringOutput("project_id") 36 | function_location := pubsub_triggerT.GetStringOutput("function_location") 37 | 38 | function_cmd := gcloud.Run(t, "functions describe", gcloud.WithCommonArgs([]string{function_name, "--project", projectID, "--gen2", "--region", function_location, "--format", "json"})) 39 | 40 | // T01: Verify if the Cloud Functions deployed is in ACTIVE state 41 | assert.Equal("ACTIVE", function_cmd.Get("state").String(), fmt.Sprintf("Should be ACTIVE. Cloud Function is not successfully deployed.")) 42 | 43 | // T02: Verify if the Cloud Functions with PubSub Event Trigger is deployed matching the output 44 | // Topic format: projects//topic/ 45 | // Output: 46 | assert.Contains(function_cmd.Get("eventTrigger.pubsubTopic").String(), pubsubTopic, fmt.Sprintf("Event Trigger is not based on PubSub Topic provided in variables. Check the EventType configuration.")) 47 | }) 48 | pubsub_triggerT.Test() 49 | } 50 | -------------------------------------------------------------------------------- /test/setup/iam.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | locals { 18 | int_required_roles = [ 19 | "roles/owner", 20 | "roles/iam.serviceAccountUser", 21 | "roles/certificatemanager.editor" 22 | ] 23 | 24 | folder_required_roles = [ 25 | "roles/resourcemanager.folderAdmin", 26 | "roles/resourcemanager.projectCreator", 27 | "roles/resourcemanager.projectDeleter", 28 | "roles/compute.xpnAdmin", 29 | "roles/iam.serviceAccountTokenCreator" 30 | ] 31 | 32 | org_required_roles = [ 33 | "roles/accesscontextmanager.policyAdmin", 34 | "roles/orgpolicy.policyAdmin" 35 | ] 36 | } 37 | 38 | resource "google_service_account" "int_test" { 39 | project = module.project.project_id 40 | account_id = "ci-account" 41 | display_name = "ci-account" 42 | } 43 | 44 | resource "google_project_iam_member" "int_test" { 45 | count = length(local.int_required_roles) 46 | 47 | project = module.project.project_id 48 | role = local.int_required_roles[count.index] 49 | member = "serviceAccount:${google_service_account.int_test.email}" 50 | } 51 | 52 | resource "google_folder_iam_member" "folder_test" { 53 | count = length(local.folder_required_roles) 54 | 55 | folder = google_folder.ci-iam-folder.id 56 | role = local.folder_required_roles[count.index] 57 | member = "serviceAccount:${google_service_account.int_test.email}" 58 | } 59 | 60 | 61 | resource "google_organization_iam_member" "org_member" { 62 | count = length(local.org_required_roles) 63 | 64 | org_id = var.org_id 65 | role = local.org_required_roles[count.index] 66 | member = "serviceAccount:${google_service_account.int_test.email}" 67 | } 68 | 69 | resource "google_billing_account_iam_member" "int_billing_admin" { 70 | billing_account_id = var.billing_account 71 | role = "roles/billing.user" 72 | member = "serviceAccount:${google_service_account.int_test.email}" 73 | } 74 | 75 | resource "google_service_account_key" "int_test" { 76 | service_account_id = google_service_account.int_test.id 77 | } 78 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_internal_server/web_server/internal_server_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2023 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | export HTTPS_PROXY=!PROXY_IP!:443 19 | export HTTP_PROXY=!PROXY_IP!:443 20 | export https_proxy=https://!PROXY_IP!:443 21 | export http_proxy=http://!PROXY_IP!:443 22 | 23 | tee -a /etc/apt/apt.conf.d/80proxy <<'EOF' 24 | Acquire::http::proxy "http://!PROXY_IP!:443/"; 25 | Acquire::https::proxy "https://!PROXY_IP!:443/"; 26 | Acquire::ftp::proxy "ftp://!PROXY_IP!:443/"; 27 | EOF 28 | 29 | tee -a /etc/apt/apt.conf.d/99verify-peer.conf <<'EOF' 30 | Acquire { https::Verify-Peer false } 31 | EOF 32 | 33 | tee -a ~/.wgetrc <<'EOF' 34 | use_proxy = on 35 | http_proxy = http://!PROXY_IP!:443/ 36 | https_proxy = http://!PROXY_IP!:443/ 37 | ftp_proxy = http://!PROXY_IP!:443/ 38 | EOF 39 | 40 | curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh --proxy-insecure 41 | 42 | sudo bash add-google-cloud-ops-agent-repo.sh --also-install 43 | sleep 60 44 | 45 | tee -a /tmp/index.html <<'EOF' 46 | ----------- hello world -------------- 47 | EOF 48 | 49 | tee -a /tmp/webserver.py <<'EOF' 50 | import http.server 51 | import socketserver 52 | import datetime 53 | import os 54 | 55 | PORT = 8000 56 | LOG_FILE = "/tmp/request_logs.log" 57 | DIRECTORY = "/tmp" 58 | 59 | # Change the current working directory to the desired directory 60 | os.chdir(DIRECTORY) 61 | 62 | class RequestHandler(http.server.SimpleHTTPRequestHandler): 63 | def do_GET(self): 64 | # Log the request 65 | log_entry = f"{datetime.datetime.now()} - Received request: {self.requestline}\n" 66 | with open(LOG_FILE, "a") as log_file: 67 | log_file.write(log_entry) 68 | 69 | # Call the parent class's do_GET method to handle the request 70 | super().do_GET() 71 | 72 | # Create the server with the custom request handler 73 | with socketserver.TCPServer(("", PORT), RequestHandler) as httpd: 74 | print(f"Serving at port {PORT} from directory {DIRECTORY}") 75 | httpd.serve_forever() 76 | EOF 77 | 78 | chmod +x /tmp/webserver.py 79 | python3 /tmp/webserver.py 80 | -------------------------------------------------------------------------------- /modules/secure-web-proxy/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "proxy_name" { 18 | description = "Secure Web Proxy name." 19 | type = string 20 | default = "secure-web-proxy" 21 | } 22 | 23 | variable "project_id" { 24 | description = "The network project id where the SWP should be deployed." 25 | type = string 26 | } 27 | 28 | variable "region" { 29 | description = "The region where the SWP should be deployed." 30 | type = string 31 | } 32 | 33 | variable "network_id" { 34 | description = "The network id where the subnetwork, firewall rule and SWP should be deployed." 35 | type = string 36 | } 37 | 38 | variable "subnetwork_id" { 39 | description = "The sub-network id where the SWP should be deployed." 40 | type = string 41 | } 42 | 43 | variable "subnetwork_ip_range" { 44 | description = "The sub-network ip range." 45 | type = string 46 | } 47 | 48 | variable "url_lists" { 49 | description = "A [URL list](https://cloud.google.com/secure-web-proxy/docs/url-list-syntax-reference) to allow access during Cloud Function build time." 50 | type = list(string) 51 | default = [] 52 | } 53 | 54 | variable "certificates" { 55 | description = "Certificate id list to be used on the Secure Web Proxy Gateway." 56 | type = list(string) 57 | } 58 | 59 | variable "addresses" { 60 | description = "IP address list to be used to access the Secure Web Proxy Gateway. Must be inside the range of the sub-network." 61 | type = list(string) 62 | } 63 | 64 | variable "ports" { 65 | description = "Protocol port list to be used to access the Secure Web Proxy Gateway." 66 | type = list(number) 67 | } 68 | 69 | variable "proxy_ip_range" { 70 | description = "The proxy sub-network ip range to be used by Secure Web Proxy Gateway. We recommend a subnet size of /23, or 512 proxy-only addresses." 71 | type = string 72 | } 73 | 74 | variable "global_address_prefix_length" { 75 | description = "The prefix length of the IP range for the private service connect. Defaults to /16." 76 | type = number 77 | default = 16 78 | } 79 | -------------------------------------------------------------------------------- /docs/secure-web-proxy.md: -------------------------------------------------------------------------------- 1 | # Secure Web Proxy usage in the Secure Cloud Function 2 | 3 | A Secure Web Proxy is a cloud first service that helps you secure egress web traffic (HTTP/S). It acts as an intermediary between users and the websites they want to access, ensuring the security, control and optimization of Internet access. See the [Secure Web Proxy](https://cloud.google.com/secure-web-proxy/docs/overview) documentation for additional information. 4 | 5 | In the context of the Secure Cloud Function the Secure Web Proxy is used to allow the Cloud Build build that is creating the image that will be used to deploy the Cloud Function to access the internet and download the dependencies of the source code of the function. 6 | Cloud Build is using a private worker pool where the instances do not have an external IP. This configuration does not allow direct access to the internet. Redirecting the Cloud Build http request through the Secure Web Proxy allows this to happen in a controlled and safe way. 7 | 8 | The Secure Web Proxy is needed to securely build your Cloud Functions. But the Secure Web Proxy is not needed at run-time. 9 | 10 | ## Pricing 11 | 12 | See detailed [Secure Web Proxy pricing](https://cloud.google.com/secure-web-proxy/pricing) information in the official documentation. 13 | 14 | ## Deleting the Secure Web proxy after deploying the examples 15 | 16 | To prevent additional charges related to the Secure Web Proxy after deploying the examples in this repository the Secure Web Proxy can be deleted using the gcloud command as by the following instructions: 17 | 18 | For the BigQuery and Internal Web Server examples use: 19 | 20 | ```bash 21 | export REGION="us-west1" 22 | ``` 23 | 24 | For the Cloud SQL example use: 25 | 26 | ```bash 27 | export REGION="us-central1" 28 | ``` 29 | 30 | 1. First need to delete the SWP itself, using the following gcloud command (we assume this command will be run from the directory of the example that was deployed): 31 | 32 | ```bash 33 | export PROJECT_ID=$(terraform output -raw network_project_id) 34 | 35 | gcloud network-services gateways delete secure-web-proxy --location=${REGION} --project=${PROJECT_ID} 36 | ``` 37 | 38 | 2. There is also an auto generated resource, a router, that needs to be deleted. 39 | To delete the auto-generated router, use the following instructions: 40 | 41 | ```bash 42 | # get the network number 43 | export NETWORK_NUMBER=$(gcloud compute networks describe projects/${PROJECT_ID}/regions/${REGION}/networks/vpc-secure-cloud-function --format='value(id)') 44 | 45 | # delete the auto generated router 46 | gcloud compute routers delete swg-autogen-router-${NETWORK_NUMBER} --project=${PROJECT_ID} 47 | ``` 48 | 49 | More information on how to manually create and delete secure web proxy in Google Cloud Platform is available in the official documentation. See the [Deploy a Secure Web Proxy instance](https://cloud.google.com/secure-web-proxy/docs/quickstart#clean-up) instruction. 50 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/functions/cf-to-sql/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloudsql 16 | 17 | import ( 18 | "context" 19 | "database/sql" 20 | "fmt" 21 | "log" 22 | "net" 23 | "os" 24 | 25 | // Pre importing this dependency because there is a redirect that doesn't work with Secure Web Proxy 26 | _ "golang.org/x/sync/errgroup" 27 | 28 | "cloud.google.com/go/cloudsqlconn" 29 | "github.com/GoogleCloudPlatform/functions-framework-go/functions" 30 | "github.com/cloudevents/sdk-go/v2/event" 31 | "github.com/go-sql-driver/mysql" 32 | ) 33 | 34 | func init() { 35 | functions.CloudEvent("HelloCloudFunction", connect) 36 | } 37 | 38 | func connect(ctx context.Context, e event.Event) error { 39 | instanceProjectID := os.Getenv("INSTANCE_PROJECT_ID") 40 | instanceUser := os.Getenv("INSTANCE_USER") 41 | instancePWD := os.Getenv("INSTANCE_PWD") 42 | instanceLocation := os.Getenv("INSTANCE_LOCATION") 43 | instanceName := os.Getenv("INSTANCE_NAME") 44 | databaseName := os.Getenv("DATABASE_NAME") 45 | 46 | d, err := cloudsqlconn.NewDialer( 47 | ctx, 48 | cloudsqlconn.WithDefaultDialOptions( 49 | cloudsqlconn.WithPrivateIP(), 50 | ), 51 | ) 52 | if err != nil { 53 | log.Fatal(err) 54 | fmt.Errorf("Error creating new Dialer", err) 55 | } 56 | 57 | instanceConnectionName := fmt.Sprintf("%s:%s:%s", instanceProjectID, instanceLocation, instanceName) 58 | 59 | fmt.Println("Registering Driver.") 60 | mysql.RegisterDialContext("cloudsqlconn", 61 | func(ctx context.Context, addr string) (net.Conn, error) { 62 | return d.Dial(ctx, instanceConnectionName) 63 | }) 64 | 65 | fmt.Println("Open connection.") 66 | db, err := sql.Open( 67 | "mysql", 68 | fmt.Sprintf("%s:%s@cloudsqlconn(%s)/%s", instanceUser, instancePWD, instanceConnectionName, databaseName), 69 | ) 70 | if err != nil { 71 | log.Fatal(err) 72 | fmt.Errorf("Error connecting to data base.", err) 73 | } 74 | err = db.Ping() 75 | if err != nil { 76 | log.Fatal(err) 77 | fmt.Errorf("Error during ping.", err) 78 | } 79 | 80 | var ( 81 | id int 82 | name string 83 | performance string 84 | ) 85 | 86 | fmt.Println("Select from table.") 87 | res, err := db.Query("SELECT * FROM characters") 88 | 89 | for res.Next() { 90 | err := res.Scan(&id, &name, &performance) 91 | if err != nil { 92 | log.Fatal(err) 93 | } 94 | fmt.Println(fmt.Sprintf("%v: %s: %s", id, name, performance)) 95 | } 96 | 97 | return err 98 | } 99 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from values at: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/org/locals.tf 17 | 18 | name: 'lint' 19 | 20 | on: 21 | workflow_dispatch: 22 | pull_request: 23 | types: [opened, edited, reopened, synchronize] 24 | branches: [main] 25 | 26 | permissions: 27 | contents: read 28 | 29 | concurrency: 30 | group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' 31 | cancel-in-progress: true 32 | 33 | jobs: 34 | lint: 35 | name: 'lint' 36 | runs-on: 'ubuntu-latest' 37 | steps: 38 | - uses: 'actions/checkout@v6' 39 | - id: variables 40 | run: | 41 | MAKEFILE=$(find . -name Makefile -print -quit) 42 | if [ -z "$MAKEFILE" ]; then 43 | echo dev-tools=gcr.io/cloud-foundation-cicd/cft/developer-tools:1 >> "$GITHUB_OUTPUT" 44 | else 45 | VERSION=$(grep "DOCKER_TAG_VERSION_DEVELOPER_TOOLS := " $MAKEFILE | cut -d\ -f3) 46 | IMAGE=$(grep "DOCKER_IMAGE_DEVELOPER_TOOLS := " $MAKEFILE | cut -d\ -f3) 47 | REGISTRY=$(grep "REGISTRY_URL := " $MAKEFILE | cut -d\ -f3) 48 | echo dev-tools=${REGISTRY}/${IMAGE}:${VERSION} >> "$GITHUB_OUTPUT" 49 | fi 50 | - run: docker run --rm -e ENABLE_BPMETADATA -v ${{ github.workspace }}:/workspace ${{ steps.variables.outputs.dev-tools }} module-swapper 51 | env: 52 | ENABLE_BPMETADATA: 1 53 | 54 | - run: docker run --rm -e ENABLE_BPMETADATA -v ${{ github.workspace }}:/workspace ${{ steps.variables.outputs.dev-tools }} /usr/local/bin/test_lint.sh 55 | env: 56 | ENABLE_BPMETADATA: 1 57 | 58 | commitlint: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v6 62 | with: 63 | fetch-depth: 0 64 | - name: Setup node 65 | uses: actions/setup-node@v6 66 | with: 67 | node-version: lts/* 68 | - name: Install commitlint 69 | run: | 70 | npm install -D @commitlint/cli@20.2.0 @commitlint/config-conventional@20.2.0 71 | echo "module.exports = { extends: ['@commitlint/config-conventional'], rules: {'subject-case': [0], 'header-max-length': [0]} };" > commitlint.config.js 72 | npx commitlint --version 73 | - name: Validate PR commits with commitlint 74 | if: github.event_name == 'pull_request' 75 | env: 76 | TITLE: ${{ github.event.pull_request.title }} 77 | run: 'echo "$TITLE" | npx commitlint --verbose' 78 | -------------------------------------------------------------------------------- /function_describe.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildConfig": { 3 | "build": "projects/97410184241/locations/us-west1/builds/f52182e4-a155-41e4-8c51-cf26f6b4b489", 4 | "dockerRepository": "projects/prj-secure-cloud-function-25de/locations/us-west1/repositories/rep-cloud-function-secure-cloud-function-bigquery", 5 | "entryPoint": "HelloCloudFunction", 6 | "runtime": "go121", 7 | "source": { 8 | "storageSource": { 9 | "bucket": "gcf-v2-sources-97410184241-us-west1", 10 | "object": "secure-cloud-function-bigquery/function-source.zip" 11 | } 12 | }, 13 | "sourceProvenance": { 14 | "resolvedStorageSource": { 15 | "bucket": "gcf-v2-sources-97410184241-us-west1", 16 | "generation": "1682514355580422", 17 | "object": "secure-cloud-function-bigquery/function-source.zip" 18 | } 19 | } 20 | }, 21 | "description": "Logs when there is a change in the BigQuery", 22 | "environment": "GEN_2", 23 | "eventTrigger": { 24 | "eventFilters": [ 25 | { 26 | "attribute": "methodName", 27 | "value": "google.cloud.bigquery.v2.JobService.InsertJob" 28 | }, 29 | { 30 | "attribute": "serviceName", 31 | "value": "bigquery.googleapis.com" 32 | }, 33 | { 34 | "attribute": "resourceName", 35 | "operator": "match-path-pattern", 36 | "value": "projects/prj-secure-cloud-function-25de/datasets/dst_secure_cloud_function/tables/tbl_test" 37 | } 38 | ], 39 | "eventType": "google.cloud.audit.log.v1.written", 40 | "pubsubTopic": "projects/prj-secure-cloud-function-25de/topics/eventarc-us-west1-secure-cloud-function-bigquery-750063-469", 41 | "retryPolicy": "RETRY_POLICY_RETRY", 42 | "serviceAccountEmail": "sa-cloud-function@prj-secure-cloud-function-25de.iam.gserviceaccount.com", 43 | "trigger": "projects/prj-secure-cloud-function-25de/locations/us-west1/triggers/secure-cloud-function-bigquery-750063", 44 | "triggerRegion": "us-west1" 45 | }, 46 | "name": "projects/prj-secure-cloud-function-25de/locations/us-west1/functions/secure-cloud-function-bigquery", 47 | "serviceConfig": { 48 | "allTrafficOnLatestRevision": true, 49 | "availableCpu": "0.1666", 50 | "availableMemory": "256Mi", 51 | "environmentVariables": { 52 | "NAME": "cloud function v2", 53 | "PROJECT_ID": "prj-secure-cloud-function-25de" 54 | }, 55 | "ingressSettings": "ALLOW_INTERNAL_AND_GCLB", 56 | "maxInstanceCount": 2, 57 | "maxInstanceRequestConcurrency": 1, 58 | "minInstanceCount": 1, 59 | "revision": "secure-cloud-function-bigquery-00001-yov", 60 | "service": "projects/prj-secure-cloud-function-25de/locations/us-west1/services/secure-cloud-function-bigquery", 61 | "serviceAccountEmail": "sa-cloud-function@prj-secure-cloud-function-25de.iam.gserviceaccount.com", 62 | "timeoutSeconds": 120, 63 | "uri": "https://secure-cloud-function-bigquery-62nzr3fd7q-uw.a.run.app", 64 | "vpcConnector": "projects/prj-secure-cloud-function-25de/locations/us-west1/connectors/con-secure-cloud-function", 65 | "vpcConnectorEgressSettings": "PRIVATE_RANGES_ONLY" 66 | }, 67 | "state": "ACTIVE", 68 | "updateTime": "2023-04-26T13:08:44.128134334Z" 69 | } 70 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/assets/sample-db-data.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2023 Google LLC 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- https://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | 16 | -- This code creates demo environment for Cloud Function accessing Cloud SQL 17 | -- This demo code is not built for production workload ## 18 | -- MySQL dump 10.13 Distrib 8.0.26, for Linux (x86_64) 19 | -- 20 | -- Host: 127.0.0.1 Database: mysql 21 | -- ------------------------------------------------------ 22 | -- Server version 8.0.26-google 23 | 24 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 25 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 26 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 27 | /*!50503 SET NAMES utf8mb4 */; 28 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 29 | /*!40103 SET TIME_ZONE='+00:00' */; 30 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 31 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 32 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 33 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 34 | 35 | -- 36 | -- Current Database: `mydb` 37 | -- 38 | 39 | CREATE DATABASE /*!32312 IF NOT EXISTS*/ `db-application` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */; 40 | 41 | USE `db-application`; 42 | 43 | -- 44 | -- Table structure for table `characters` 45 | -- 46 | 47 | DROP TABLE IF EXISTS `characters`; 48 | /*!40101 SET @saved_cs_client = @@character_set_client */; 49 | /*!50503 SET character_set_client = utf8mb4 */; 50 | CREATE TABLE `characters` ( 51 | `id` int NOT NULL, 52 | `name` varchar(30) DEFAULT NULL, 53 | `performance` varchar(30) DEFAULT NULL 54 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; 55 | /*!40101 SET character_set_client = @saved_cs_client */; 56 | 57 | -- 58 | -- Dumping data for table `characters` 59 | -- 60 | 61 | LOCK TABLES `characters` WRITE; 62 | /*!40000 ALTER TABLE `characters` DISABLE KEYS */; 63 | INSERT INTO `characters` VALUES (1,'Bugs Bunny','Looney Tunes'),(2,'Gandalf the Grey','Lord of the Rings'),(3,'Green Goblin','Spiderman'),(4,'Dorothy Gale','Wizard of Oz'); 64 | /*!40000 ALTER TABLE `characters` ENABLE KEYS */; 65 | UNLOCK TABLES; 66 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 67 | 68 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 69 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 70 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 71 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 72 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 73 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 74 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 75 | 76 | -- Dump completed on 2023-02-22 23:22:33 77 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/org_policies.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | locals { 18 | project = var.policy_for == "project" ? var.serverless_project_id : "" 19 | folder = var.policy_for == "folder" ? var.folder_id : "" 20 | organization = var.policy_for == "organization" ? var.organization_id : "" 21 | } 22 | 23 | module "cloudfunction_allowed_ingress" { 24 | source = "terraform-google-modules/org-policy/google" 25 | version = "~> 7.0" 26 | 27 | policy_for = var.policy_for 28 | project_id = local.project 29 | folder_id = local.folder 30 | organization_id = local.organization 31 | constraint = "constraints/cloudfunctions.allowedIngressSettings" 32 | policy_type = "list" 33 | allow = ["ALLOW_INTERNAL_ONLY"] 34 | allow_list_length = 1 35 | } 36 | 37 | module "cloudfunction_allowed_vpc_egress" { 38 | source = "terraform-google-modules/org-policy/google" 39 | version = "~> 7.0" 40 | 41 | policy_for = var.policy_for 42 | project_id = local.project 43 | folder_id = local.folder 44 | organization_id = local.organization 45 | constraint = "constraints/cloudfunctions.requireVPCConnector" 46 | policy_type = "boolean" 47 | enforce = "true" 48 | } 49 | 50 | module "cloudfunction_vpc_connector_egress_settings" { 51 | source = "terraform-google-modules/org-policy/google" 52 | version = "~> 7.0" 53 | 54 | policy_for = var.policy_for 55 | project_id = local.project 56 | folder_id = local.folder 57 | organization_id = local.organization 58 | policy_type = "list" 59 | constraint = "cloudfunctions.allowedVpcConnectorEgressSettings" 60 | allow = ["ALL_TRAFFIC"] 61 | allow_list_length = 1 62 | } 63 | 64 | module "cloudrun_allowed_ingress" { 65 | source = "terraform-google-modules/org-policy/google" 66 | version = "~> 7.0" 67 | 68 | policy_for = var.policy_for 69 | project_id = local.project 70 | folder_id = local.folder 71 | organization_id = local.organization 72 | constraint = "constraints/run.allowedIngress" 73 | policy_type = "list" 74 | allow = ["is:internal-and-cloud-load-balancing"] 75 | allow_list_length = 1 76 | } 77 | 78 | module "cloudrun_allowed_vpc_egress" { 79 | source = "terraform-google-modules/org-policy/google" 80 | version = "~> 7.0" 81 | 82 | policy_for = var.policy_for 83 | project_id = local.project 84 | folder_id = local.folder 85 | organization_id = local.organization 86 | constraint = "constraints/run.allowedVPCEgress" 87 | policy_type = "list" 88 | allow = ["all-traffic"] 89 | allow_list_length = 1 90 | } 91 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Please note that this file was generated from [terraform-google-module-template](https://github.com/terraform-google-modules/terraform-google-module-template). 16 | # Please make sure to contribute relevant changes upstream! 17 | 18 | # Make will use bash instead of sh 19 | SHELL := /usr/bin/env bash 20 | 21 | DOCKER_TAG_VERSION_DEVELOPER_TOOLS := 1.24 22 | DOCKER_IMAGE_DEVELOPER_TOOLS := cft/developer-tools 23 | REGISTRY_URL := gcr.io/cloud-foundation-cicd 24 | 25 | # Enter docker container for local development 26 | .PHONY: docker_run 27 | docker_run: 28 | docker run --rm -it \ 29 | -e SERVICE_ACCOUNT_JSON \ 30 | -v "$(CURDIR)":/workspace \ 31 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 32 | /bin/bash 33 | 34 | # Execute prepare tests within the docker container 35 | .PHONY: docker_test_prepare 36 | docker_test_prepare: 37 | docker run --rm -it \ 38 | -e SERVICE_ACCOUNT_JSON \ 39 | -e TF_VAR_org_id \ 40 | -e TF_VAR_folder_id \ 41 | -e TF_VAR_billing_account \ 42 | -v "$(CURDIR)":/workspace \ 43 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 44 | /usr/local/bin/execute_with_credentials.sh prepare_environment 45 | 46 | # Clean up test environment within the docker container 47 | .PHONY: docker_test_cleanup 48 | docker_test_cleanup: 49 | docker run --rm -it \ 50 | -e SERVICE_ACCOUNT_JSON \ 51 | -e TF_VAR_org_id \ 52 | -e TF_VAR_folder_id \ 53 | -e TF_VAR_billing_account \ 54 | -v "$(CURDIR)":/workspace \ 55 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 56 | /usr/local/bin/execute_with_credentials.sh cleanup_environment 57 | 58 | # Execute integration tests within the docker container 59 | .PHONY: docker_test_integration 60 | docker_test_integration: 61 | docker run --rm -it \ 62 | -e SERVICE_ACCOUNT_JSON \ 63 | -v "$(CURDIR)":/workspace \ 64 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 65 | /usr/local/bin/test_integration.sh 66 | 67 | # Execute lint tests within the docker container 68 | .PHONY: docker_test_lint 69 | docker_test_lint: 70 | docker run --rm -it \ 71 | -e EXCLUDE_LINT_DIRS \ 72 | -v "$(CURDIR)":/workspace \ 73 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 74 | /usr/local/bin/test_lint.sh 75 | 76 | # Generate documentation 77 | .PHONY: docker_generate_docs 78 | docker_generate_docs: 79 | docker run --rm -it \ 80 | -v "$(CURDIR)":/workspace \ 81 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 82 | /bin/bash -c 'source /usr/local/bin/task_helper_functions.sh && generate_docs' 83 | 84 | # Alias for backwards compatibility 85 | .PHONY: generate_docs 86 | generate_docs: docker_generate_docs 87 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_internal_server/outputs.tf: -------------------------------------------------------------------------------- 1 | # /** 2 | # * Copyright 2023 Google LLC 3 | # * 4 | # * Licensed under the Apache License, Version 2.0 (the "License"); 5 | # * you may not use this file except in compliance with the License. 6 | # * You may obtain a copy of the License at 7 | # * 8 | # * http://www.apache.org/licenses/LICENSE-2.0 9 | # * 10 | # * Unless required by applicable law or agreed to in writing, software 11 | # * distributed under the License is distributed on an "AS IS" BASIS, 12 | # * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # * See the License for the specific language governing permissions and 14 | # * limitations under the License. 15 | # */ 16 | 17 | output "serverless_project_id" { 18 | value = module.secure_harness.serverless_project_ids[0] 19 | description = "The serverless project id." 20 | } 21 | 22 | output "serverless_project_number" { 23 | value = module.secure_harness.serverless_project_numbers[module.secure_harness.serverless_project_ids[0]] 24 | description = "The serverless project number." 25 | } 26 | 27 | output "security_project_id" { 28 | value = module.secure_harness.security_project_id 29 | description = "The security project id." 30 | } 31 | 32 | output "security_project_number" { 33 | value = module.secure_harness.security_project_number 34 | description = "The security project number." 35 | } 36 | 37 | output "network_project_id" { 38 | value = module.secure_harness.network_project_id[0] 39 | description = "The network project id." 40 | } 41 | 42 | output "service_account_email" { 43 | value = module.secure_harness.service_account_email[module.secure_harness.serverless_project_ids[0]] 44 | description = "The service account email created to be used by Cloud Function." 45 | } 46 | 47 | output "cloud_function_name" { 48 | value = module.secure_cloud_function.cloudfunction_name 49 | description = "The service account email created to be used by Cloud Function." 50 | } 51 | 52 | output "service_vpc_self_link" { 53 | value = module.secure_harness.service_vpc[0].network.self_link 54 | description = "The Network self-link created in harness." 55 | } 56 | 57 | output "service_vpc_name" { 58 | value = module.secure_harness.service_vpc[0].network_name 59 | description = "The Network self-link created in harness." 60 | } 61 | 62 | output "service_vpc_subnet_name" { 63 | value = module.secure_harness.service_subnet[0] 64 | description = "The sub-network name created in harness." 65 | } 66 | output "connector_id" { 67 | value = module.secure_cloud_function.connector_id 68 | description = "VPC serverless connector ID." 69 | } 70 | 71 | output "restricted_service_perimeter_name" { 72 | value = module.secure_harness.restricted_service_perimeter_name 73 | description = "Service Perimeter name." 74 | } 75 | 76 | output "restricted_access_level_name" { 77 | value = module.secure_harness.restricted_access_level_name 78 | description = "Access level name." 79 | } 80 | 81 | output "cloudfunction_bucket_name" { 82 | value = module.secure_cloud_function.cloudfunction_bucket_name 83 | description = "Name of the Cloud Function source bucket." 84 | } 85 | 86 | output "cloudfunction_bucket" { 87 | value = module.secure_cloud_function.cloudfunction_bucket 88 | description = "The Cloud Function source bucket." 89 | } 90 | 91 | output "cloudfunction_url" { 92 | value = module.secure_cloud_function.cloudfunction_url 93 | description = "The URL on which the deployed service is available." 94 | } 95 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_bigquery_trigger/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "serverless_project_id" { 18 | value = module.secure_harness.serverless_project_ids[0] 19 | description = "The serverless project id." 20 | } 21 | 22 | output "serverless_project_number" { 23 | value = module.secure_harness.serverless_project_numbers[module.secure_harness.serverless_project_ids[0]] 24 | description = "The serverless project number." 25 | } 26 | 27 | output "security_project_id" { 28 | value = module.secure_harness.security_project_id 29 | description = "The security project id." 30 | } 31 | 32 | output "security_project_number" { 33 | value = module.secure_harness.security_project_number 34 | description = "The security project number." 35 | } 36 | 37 | output "network_project_id" { 38 | value = module.secure_harness.network_project_id[0] 39 | description = "The network project id." 40 | } 41 | 42 | output "service_account_email" { 43 | value = module.secure_harness.service_account_email[module.secure_harness.serverless_project_ids[0]] 44 | description = "The service account email created to be used by Cloud Function." 45 | } 46 | 47 | output "cloud_function_name" { 48 | value = module.secure_cloud_function.cloudfunction_name 49 | description = "The service account email created to be used by Cloud Function." 50 | } 51 | 52 | output "service_vpc_self_link" { 53 | value = module.secure_harness.service_vpc[0].network.self_link 54 | description = "The Network self-link created in harness." 55 | } 56 | 57 | output "service_vpc_name" { 58 | value = module.secure_harness.service_vpc[0].network_name 59 | description = "The Network self-link created in harness." 60 | } 61 | 62 | output "service_vpc_subnet_name" { 63 | value = module.secure_harness.service_subnet[0] 64 | description = "The sub-network name created in harness." 65 | } 66 | output "connector_id" { 67 | value = module.secure_cloud_function.connector_id 68 | description = "VPC serverless connector ID." 69 | } 70 | 71 | output "table_id" { 72 | value = module.bigquery.bigquery_tables[local.table_name]["id"] 73 | description = "Bigquery table name." 74 | } 75 | 76 | output "restricted_service_perimeter_name" { 77 | value = module.secure_harness.restricted_service_perimeter_name 78 | description = "Service Perimeter name." 79 | } 80 | 81 | output "bigquery_kms_key" { 82 | value = module.bigquery_kms.keys[local.kms_bigquery] 83 | description = "KMS Key used in the Bigquery dataset." 84 | } 85 | 86 | output "restricted_access_level_name" { 87 | value = module.secure_harness.restricted_access_level_name 88 | description = "Access level name." 89 | } 90 | 91 | output "cloudfunction_bucket_name" { 92 | value = module.secure_cloud_function.cloudfunction_bucket_name 93 | description = "Name of the Cloud Function source bucket." 94 | } 95 | 96 | output "cloudfunction_bucket" { 97 | value = module.secure_cloud_function.cloudfunction_bucket 98 | description = "The Cloud Function source bucket." 99 | } 100 | 101 | output "cloudfunction_url" { 102 | value = module.secure_cloud_function.cloudfunction_url 103 | description = "The URL on which the deployed service is available." 104 | } 105 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "kms_project_id" { 18 | description = "The project where KMS will be created." 19 | type = string 20 | } 21 | 22 | variable "serverless_project_id" { 23 | description = "The project where Cloud Function is going to be deployed." 24 | type = string 25 | } 26 | 27 | variable "prevent_destroy" { 28 | description = "Set the prevent_destroy lifecycle attribute on keys.." 29 | type = bool 30 | default = true 31 | } 32 | 33 | variable "keyring_name" { 34 | description = "Keyring name." 35 | type = string 36 | } 37 | 38 | variable "key_rotation_period" { 39 | description = "Period of key rotation in seconds." 40 | type = string 41 | default = "2592000s" 42 | } 43 | 44 | variable "key_name" { 45 | description = "Key name." 46 | type = string 47 | } 48 | 49 | variable "key_protection_level" { 50 | description = "The protection level to use when creating a version based on this template. Possible values: [\"SOFTWARE\", \"HSM\"]" 51 | type = string 52 | default = "HSM" 53 | } 54 | 55 | variable "location" { 56 | description = "The location where resources are going to be deployed." 57 | type = string 58 | default = "us-east4" 59 | } 60 | 61 | variable "owners" { 62 | description = "List of comma-separated owners for each key declared in set_owners_for." 63 | type = list(string) 64 | default = [] 65 | } 66 | 67 | variable "encrypters" { 68 | description = "List of comma-separated owners for each key declared in set_encrypters_for." 69 | type = list(string) 70 | default = [] 71 | } 72 | 73 | variable "decrypters" { 74 | description = "List of comma-separated owners for each key declared in set_decrypters_for." 75 | type = list(string) 76 | default = [] 77 | } 78 | 79 | variable "policy_for" { 80 | description = "Policy Root: set one of the following values to determine where the policy is applied. Possible values: [\"project\", \"folder\", \"organization\"]." 81 | type = string 82 | default = "project" 83 | } 84 | 85 | variable "folder_id" { 86 | description = "The folder ID to apply the policy to." 87 | type = string 88 | default = "" 89 | } 90 | 91 | variable "organization_id" { 92 | description = "The organization ID to apply the policy to." 93 | type = string 94 | default = "" 95 | } 96 | 97 | variable "groups" { 98 | description = < to see your current agreements on file or 13 | to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code Reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult 23 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 24 | information on using pull requests. 25 | 26 | ## Development 27 | 28 | The following dependencies must be installed on the development system: 29 | 30 | - [Docker Engine][docker-engine] 31 | - [Google Cloud SDK][google-cloud-sdk] 32 | - [make] 33 | 34 | ### Generating Documentation for Inputs and Outputs 35 | 36 | The Inputs and Outputs tables in the READMEs of the root module, 37 | submodules, and example modules are automatically generated based on 38 | the `variables` and `outputs` of the respective modules. These tables 39 | must be refreshed if the module interfaces are changed. 40 | 41 | #### Execution 42 | 43 | Run `make generate_docs` to generate new Inputs and Outputs tables. 44 | 45 | ### Integration Testing 46 | 47 | Integration tests are used to verify the behaviour of the root module, 48 | submodules, and example modules. Additions, changes, and fixes should 49 | be accompanied with tests. 50 | 51 | The integration tests are run using [Kitchen][kitchen], 52 | [Kitchen-Terraform][kitchen-terraform], and [InSpec][inspec]. These 53 | tools are packaged within a Docker image for convenience. 54 | 55 | The general strategy for these tests is to verify the behaviour of the 56 | [example modules](./examples/), thus ensuring that the root module, 57 | submodules, and example modules are all functionally correct. 58 | 59 | #### Test Environment 60 | The easiest way to test the module is in an isolated test project. The setup for such a project is defined in [test/setup](./test/setup/) directory. 61 | 62 | To use this setup, you need a service account with these permissions (on a Folder or Organization): 63 | - Project Creator 64 | - Project Billing Manager 65 | 66 | The project that the service account belongs to must have the following APIs enabled (the setup won't 67 | create any resources on the service account's project): 68 | - Cloud Resource Manager 69 | - Cloud Billing 70 | - Service Usage 71 | - Identity and Access Management (IAM) 72 | 73 | Export the Service Account credentials to your environment like so: 74 | 75 | ``` 76 | export SERVICE_ACCOUNT_JSON=$(< credentials.json) 77 | ``` 78 | 79 | You will also need to set a few environment variables: 80 | ``` 81 | export TF_VAR_org_id="your_org_id" 82 | export TF_VAR_folder_id="your_folder_id" 83 | export TF_VAR_billing_account="your_billing_account_id" 84 | ``` 85 | 86 | With these settings in place, you can prepare a test project using Docker: 87 | ``` 88 | make docker_test_prepare 89 | ``` 90 | 91 | #### Noninteractive Execution 92 | 93 | Run `make docker_test_integration` to test all of the example modules 94 | noninteractively, using the prepared test project. 95 | 96 | #### Interactive Execution 97 | 98 | 1. Run `make docker_run` to start the testing Docker container in 99 | interactive mode. 100 | 101 | 1. Run `kitchen_do create ` to initialize the working 102 | directory for an example module. 103 | 104 | 1. Run `kitchen_do converge ` to apply the example module. 105 | 106 | 1. Run `kitchen_do verify ` to test the example module. 107 | 108 | 1. Run `kitchen_do destroy ` to destroy the example module 109 | state. 110 | 111 | ### Linting and Formatting 112 | 113 | Many of the files in the repository can be linted or formatted to 114 | maintain a standard of quality. 115 | 116 | #### Execution 117 | 118 | Run `make docker_test_lint`. 119 | 120 | [docker-engine]: https://www.docker.com/products/docker-engine 121 | [flake8]: http://flake8.pycqa.org/en/latest/ 122 | [gofmt]: https://golang.org/cmd/gofmt/ 123 | [google-cloud-sdk]: https://cloud.google.com/sdk/install 124 | [hadolint]: https://github.com/hadolint/hadolint 125 | [inspec]: https://inspec.io/ 126 | [kitchen-terraform]: https://github.com/newcontext-oss/kitchen-terraform 127 | [kitchen]: https://kitchen.ci/ 128 | [make]: https://en.wikipedia.org/wiki/Make_(software) 129 | [shellcheck]: https://www.shellcheck.net/ 130 | [terraform-docs]: https://github.com/segmentio/terraform-docs 131 | [terraform]: https://terraform.io/ 132 | -------------------------------------------------------------------------------- /modules/secure-web-proxy/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_compute_subnetwork" "swp_subnetwork_proxy" { 18 | name = "sb-swp-${var.region}" 19 | ip_cidr_range = var.proxy_ip_range 20 | project = var.project_id 21 | region = var.region 22 | network = var.network_id 23 | purpose = "REGIONAL_MANAGED_PROXY" 24 | role = "ACTIVE" 25 | } 26 | 27 | module "swp_firewall_rule" { 28 | source = "terraform-google-modules/network/google//modules/firewall-rules" 29 | version = "~> 11.0" 30 | project_id = var.project_id 31 | network_name = var.network_id 32 | 33 | rules = [{ 34 | name = "fw-allow-tcp-443-egress-to-secure-web-proxy" 35 | description = "Allow Cloud Build to connect in Secure Web Proxy" 36 | direction = "EGRESS" 37 | priority = 100 38 | ranges = [var.proxy_ip_range, var.subnetwork_ip_range] 39 | source_tags = [] 40 | allow = [{ 41 | protocol = "tcp" 42 | ports = var.ports 43 | }] 44 | deny = [] 45 | log_config = { 46 | metadata = "INCLUDE_ALL_METADATA" 47 | } 48 | }] 49 | } 50 | 51 | resource "google_compute_global_address" "private_ip_allocation" { 52 | name = "swp-cloud-function-internal-connection" 53 | project = var.project_id 54 | address_type = "INTERNAL" 55 | purpose = "VPC_PEERING" 56 | prefix_length = var.global_address_prefix_length 57 | network = var.network_id 58 | } 59 | 60 | resource "google_service_networking_connection" "private_service_connect" { 61 | network = var.network_id 62 | service = "servicenetworking.googleapis.com" 63 | reserved_peering_ranges = [google_compute_global_address.private_ip_allocation.name] 64 | deletion_policy = "ABANDON" 65 | 66 | depends_on = [ 67 | google_compute_global_address.private_ip_allocation 68 | ] 69 | } 70 | 71 | resource "time_sleep" "wait_network_config_propagation" { 72 | create_duration = "1m" 73 | destroy_duration = "2m" 74 | 75 | depends_on = [ 76 | google_service_networking_connection.private_service_connect, 77 | google_compute_subnetwork.swp_subnetwork_proxy 78 | ] 79 | } 80 | 81 | resource "google_network_security_gateway_security_policy" "swp_security_policy" { 82 | name = "swp-security-policy" 83 | project = var.project_id 84 | location = var.region 85 | description = "Secure Web Proxy security policy." 86 | } 87 | 88 | resource "google_network_security_url_lists" "swp_url_lists" { 89 | name = "swp-url-lists" 90 | project = var.project_id 91 | location = var.region 92 | description = "Secure Web Proxy list of allowed URLs." 93 | values = var.url_lists 94 | } 95 | 96 | resource "google_network_security_gateway_security_policy_rule" "swp_security_policy_rule" { 97 | name = "swp-security-policy-rule" 98 | project = var.project_id 99 | location = var.region 100 | gateway_security_policy = google_network_security_gateway_security_policy.swp_security_policy.name 101 | enabled = true 102 | description = "Secure Web Proxy security policy rule." 103 | priority = 1 104 | session_matcher = "inUrlList(host(), '${google_network_security_url_lists.swp_url_lists.id}')" 105 | tls_inspection_enabled = false 106 | basic_profile = "ALLOW" 107 | 108 | depends_on = [ 109 | google_network_security_url_lists.swp_url_lists, 110 | google_network_security_gateway_security_policy.swp_security_policy 111 | ] 112 | } 113 | 114 | resource "google_network_services_gateway" "secure_web_proxy" { 115 | project = var.project_id 116 | name = var.proxy_name 117 | location = var.region 118 | type = "SECURE_WEB_GATEWAY" 119 | addresses = var.addresses 120 | ports = var.ports 121 | certificate_urls = var.certificates 122 | gateway_security_policy = google_network_security_gateway_security_policy.swp_security_policy.id 123 | network = var.network_id 124 | subnetwork = var.subnetwork_id 125 | scope = "samplescope" 126 | delete_swg_autogen_router_on_destroy = true 127 | 128 | depends_on = [ 129 | google_compute_subnetwork.swp_subnetwork_proxy, 130 | google_service_networking_connection.private_service_connect, 131 | google_network_security_gateway_security_policy_rule.swp_security_policy_rule 132 | ] 133 | } 134 | 135 | resource "time_sleep" "wait_secure_web_proxy" { 136 | create_duration = "2m" 137 | 138 | depends_on = [ 139 | google_network_services_gateway.secure_web_proxy 140 | ] 141 | } 142 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "project_id" { 18 | description = "Project ID to create Cloud Function" 19 | type = string 20 | } 21 | 22 | variable "function_name" { 23 | description = "A user-defined name of the function" 24 | type = string 25 | } 26 | 27 | variable "function_location" { 28 | description = "The location of this cloud function" 29 | type = string 30 | } 31 | 32 | variable "description" { 33 | description = "Short description of the function" 34 | type = string 35 | default = null 36 | } 37 | 38 | variable "labels" { 39 | description = "A set of key/value label pairs associated with this Cloud Function" 40 | type = map(string) 41 | default = null 42 | } 43 | 44 | variable "runtime" { 45 | description = "The runtime in which to run the function." 46 | type = string 47 | } 48 | 49 | variable "entrypoint" { 50 | description = "The name of the function (as defined in source code) that will be executed. Defaults to the resource name suffix, if not specified" 51 | type = string 52 | } 53 | 54 | variable "build_env_variables" { 55 | description = "User-provided build-time environment variables" 56 | type = map(string) 57 | default = null 58 | } 59 | 60 | variable "worker_pool" { 61 | description = "Name of the Cloud Build Custom Worker Pool that should be used to build the function." 62 | type = string 63 | default = null 64 | } 65 | 66 | variable "docker_repository" { 67 | description = "User managed repository created in Artifact Registry optionally with a customer managed encryption key." 68 | type = string 69 | default = null 70 | } 71 | 72 | variable "storage_source" { 73 | description = "Get the source from this location in Google Cloud Storage" 74 | type = object({ 75 | bucket = string 76 | object = string 77 | generation = optional(string, null) 78 | }) 79 | default = null 80 | } 81 | 82 | variable "repo_source" { 83 | description = "Get the source from this location in a Cloud Source Repository" 84 | type = object({ 85 | project_id = optional(string) 86 | repo_name = string 87 | branch_name = string 88 | dir = optional(string) 89 | tag_name = optional(string) 90 | commit_sha = optional(string) 91 | invert_regex = optional(bool, false) 92 | }) 93 | default = null 94 | } 95 | 96 | variable "event_trigger" { 97 | description = "Event triggers for the function" 98 | type = object({ 99 | trigger_region = optional(string) 100 | event_type = string 101 | service_account_email = string 102 | pubsub_topic = optional(string) 103 | retry_policy = string 104 | event_filters = optional(set(object({ 105 | attribute = string 106 | attribute_value = string 107 | operator = optional(string) 108 | }))) 109 | }) 110 | default = null 111 | } 112 | 113 | variable "service_config" { 114 | description = "Details of the service" 115 | type = object({ 116 | max_instance_count = optional(string, 100) 117 | min_instance_count = optional(string, 1) 118 | available_memory = optional(string, "256M") 119 | available_cpu = optional(string, 1) 120 | timeout_seconds = optional(string, 60) 121 | runtime_env_variables = optional(map(string), null) 122 | runtime_secret_env_variables = optional(set(object({ 123 | key_name = string 124 | project_id = optional(string) 125 | secret = string 126 | version = string 127 | })), null) 128 | secret_volumes = optional(set(object({ 129 | mount_path = string 130 | project_id = optional(string) 131 | secret = string 132 | versions = set(object({ 133 | version = string 134 | path = string 135 | })) 136 | })), null) 137 | vpc_connector = optional(string, null) 138 | vpc_connector_egress_settings = optional(string, null) 139 | ingress_settings = optional(string, null) 140 | service_account_email = optional(string, null) 141 | all_traffic_on_latest_revision = optional(bool, true) 142 | }) 143 | default = {} 144 | } 145 | 146 | // IAM 147 | variable "members" { 148 | type = map(list(string)) 149 | description = "Cloud Function Invoker and Developer roles for Users/SAs. Key names must be developers and/or invokers" 150 | default = {} 151 | validation { 152 | condition = alltrue([ 153 | for key in keys(var.members) : contains(["invokers", "developers"], key) 154 | ]) 155 | error_message = "The supported keys are invokers and developers." 156 | } 157 | } 158 | 159 | variable "build_service_account" { 160 | type = string 161 | description = "Cloud Function Build Service Account Id. This is The fully-qualified name of the service account to be used for building the container." 162 | default = null 163 | } 164 | -------------------------------------------------------------------------------- /examples/secure_cloud_function_with_sql/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "serverless_project_id" { 18 | value = module.secure_harness.serverless_project_ids[0] 19 | description = "The serverless project id." 20 | } 21 | 22 | output "serverless_project_number" { 23 | value = module.secure_harness.serverless_project_numbers[module.secure_harness.serverless_project_ids[0]] 24 | description = "The serverless project number." 25 | } 26 | 27 | output "cloudsql_project_id" { 28 | value = module.secure_harness.serverless_project_ids[1] 29 | description = "The Cloud SQL project id." 30 | } 31 | 32 | output "security_project_id" { 33 | value = module.secure_harness.security_project_id 34 | description = "The security project id." 35 | } 36 | 37 | output "security_project_number" { 38 | value = module.secure_harness.security_project_number 39 | description = "The security project number." 40 | } 41 | 42 | output "network_project_id" { 43 | value = module.secure_harness.network_project_id[0] 44 | description = "The network project id." 45 | } 46 | 47 | output "service_account_email" { 48 | value = module.secure_harness.service_account_email[module.secure_harness.serverless_project_ids[0]] 49 | description = "The service account email created to be used by Cloud Function." 50 | } 51 | 52 | output "cloud_function_name" { 53 | value = module.secure_cloud_function.cloudfunction_name 54 | description = "The service account email created to be used by Cloud Function." 55 | } 56 | 57 | output "service_vpc_self_link" { 58 | value = module.secure_harness.service_vpc[0].network.self_link 59 | description = "The Network self-link created in harness." 60 | } 61 | 62 | output "service_vpc_name" { 63 | value = module.secure_harness.service_vpc[0].network_name 64 | description = "The Network self-link created in harness." 65 | } 66 | 67 | output "secret_manager_name" { 68 | value = local.secret_name 69 | description = "Secret Manager name created to store Database password." 70 | } 71 | 72 | output "scheduler_name" { 73 | value = google_cloud_scheduler_job.job.name 74 | description = "Cloud Scheduler Job name." 75 | } 76 | 77 | output "secret_manager_id" { 78 | value = google_secret_manager_secret.password_secret.id 79 | description = "Secret Manager id created to store Database password." 80 | } 81 | 82 | output "secret_manager_version" { 83 | value = data.google_secret_manager_secret_version.latest_version.version 84 | description = "Secret Manager version created to store Database password." 85 | } 86 | 87 | output "service_vpc_subnet_name" { 88 | value = module.secure_harness.service_subnet[0] 89 | description = "The sub-network name created in harness." 90 | } 91 | 92 | output "connector_id" { 93 | value = module.secure_cloud_function.connector_id 94 | description = "VPC serverless connector ID." 95 | } 96 | 97 | output "restricted_service_perimeter_name" { 98 | value = module.secure_harness.restricted_service_perimeter_name 99 | description = "Service Perimeter name." 100 | } 101 | 102 | output "restricted_access_level_name" { 103 | value = module.secure_harness.restricted_access_level_name 104 | description = "Access level name." 105 | } 106 | 107 | output "mysql_name" { 108 | description = "The name for Cloud SQL instance." 109 | value = module.safer_mysql_db.instance_name 110 | } 111 | 112 | output "mysql_conn" { 113 | value = module.safer_mysql_db.instance_connection_name 114 | description = "The connection name of the master instance to be used in connection strings." 115 | } 116 | 117 | output "mysql_public_ip_address" { 118 | description = "The first public (PRIMARY) IPv4 address assigned for the master instance." 119 | value = module.safer_mysql_db.public_ip_address 120 | } 121 | 122 | output "mysql_private_ip_address" { 123 | description = "The first private (PRIVATE) IPv4 address assigned for the master instance." 124 | value = module.safer_mysql_db.private_ip_address 125 | } 126 | 127 | output "mysql_user" { 128 | description = "The user created in database instance." 129 | value = local.db_user 130 | } 131 | 132 | output "cloud_sql_kms_key" { 133 | description = "The KMS Key create to encrypt Cloud SQL." 134 | value = module.kms_keys.keys["key-sql"] 135 | } 136 | 137 | output "topic_kms_key" { 138 | description = "The KMS Key create to encrypt Pub/Sub Topic messages." 139 | value = module.kms_keys.keys["key-topic"] 140 | } 141 | 142 | output "secret_kms_key" { 143 | description = "The KMS Key create to encrypt Secrets." 144 | value = module.kms_keys.keys["key-secret"] 145 | } 146 | 147 | output "cloudfunction_bucket_name" { 148 | value = module.secure_cloud_function.cloudfunction_bucket_name 149 | description = "Name of the Cloud Function source bucket." 150 | } 151 | 152 | output "cloudfunction_bucket" { 153 | value = module.secure_cloud_function.cloudfunction_bucket 154 | description = "The Cloud Function source bucket." 155 | } 156 | 157 | output "cloudfunction_url" { 158 | value = module.secure_cloud_function.cloudfunction_url 159 | description = "The URL on which the deployed service is available." 160 | } 161 | 162 | output "topic_id" { 163 | value = module.pubsub.id 164 | description = "The Pub/Sub topic which will trigger Cloud Function." 165 | } 166 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-security/README.md: -------------------------------------------------------------------------------- 1 | # Secure Cloud Function (2nd Gen) Security 2 | 3 | This module handles the basic deployment security configurations for Cloud Function (2nd Gen) usage. 4 | 5 | The resources/services/activations/deletions that this module will create/trigger are: 6 | 7 | * Creates KMS Keyring and Key for [customer managed encryption keys](https://cloud.google.com/run/docs/securing/using-cmek) in the **KMS Project** 8 | to be used by Cloud Function (2nd Gen). 9 | * Enables Organization Policies related to Cloud Function (2nd Gen) in the **Serverless Project**. 10 | * Allow Ingress only from internal and Cloud Load Balancing. 11 | * Allow VPC Egress to Private Ranges Only. 12 | * When groups emails are provided, this module will grant the roles for each persona. 13 | * Serverless administrator - Service Project 14 | * roles/run.admin 15 | * roles/cloudfunctions.admin 16 | * roles/compute.networkViewer 17 | * compute.networkUser 18 | * Servervless Security Administrator - Security project 19 | * roles/cloudfunctions.viewer 20 | * roles/run.viewer 21 | * roles/cloudkms.viewer 22 | * roles/artifactregistry.reader 23 | * Cloud Function (2nd Gen) developer - Security project 24 | * roles/cloudfunctions.developer 25 | * roles/artifactregistry.writer 26 | * roles/cloudkms.cryptoKeyEncrypter 27 | * Cloud Function (2nd Gen) user - Service project 28 | * roles/cloudfunctions.invoker 29 | 30 | ## Usage 31 | 32 | ```hcl 33 | module "secure_cloud_function_security" { 34 | source = "GoogleCloudPlatform/cloud-functions/google//modules/secure-cloud-serverless-security" 35 | version = "~> 0.7" 36 | 37 | kms_project_id = 38 | location = 39 | serverless_project_id = 40 | key_name = 41 | keyring_name = 42 | key_rotation_period = 43 | key_protection_level = 44 | 45 | encrypters = [ 46 | "serviceAccount:", 47 | "serviceAccount:" 48 | ] 49 | 50 | decrypters = [ 51 | "serviceAccount:", 52 | "serviceAccount:" 53 | ] 54 | } 55 | ``` 56 | 57 | 58 | ## Inputs 59 | 60 | | Name | Description | Type | Default | Required | 61 | |------|-------------|------|---------|:--------:| 62 | | decrypters | List of comma-separated owners for each key declared in set\_decrypters\_for. | `list(string)` | `[]` | no | 63 | | encrypters | List of comma-separated owners for each key declared in set\_encrypters\_for. | `list(string)` | `[]` | no | 64 | | folder\_id | The folder ID to apply the policy to. | `string` | `""` | no | 65 | | groups | Groups which will have roles assigned.
The Serverless Administrators email group which the following roles will be added: Cloud Function Admin, Compute Network Viewer and Compute Network User.
The Serverless Security Administrators email group which the following roles will be added: Cloud Function Viewer, Cloud KMS Viewer and Artifact Registry Reader.
The Cloud Function Developer email group which the following roles will be added: Cloud Function Developer, Artifact Registry Writer and Cloud KMS CryptoKey Encrypter.
The Cloud Function User email group which the following roles will be added: Cloud Function Invoker. |
object({
group_serverless_administrator = optional(string, null)
group_serverless_security_administrator = optional(string, null)
group_cloud_function_developer = optional(string, null)
group_cloud_function_user = optional(string, null)
})
| `{}` | no | 66 | | key\_name | Key name. | `string` | n/a | yes | 67 | | key\_protection\_level | The protection level to use when creating a version based on this template. Possible values: ["SOFTWARE", "HSM"] | `string` | `"HSM"` | no | 68 | | key\_rotation\_period | Period of key rotation in seconds. | `string` | `"2592000s"` | no | 69 | | keyring\_name | Keyring name. | `string` | n/a | yes | 70 | | kms\_project\_id | The project where KMS will be created. | `string` | n/a | yes | 71 | | location | The location where resources are going to be deployed. | `string` | `"us-east4"` | no | 72 | | organization\_id | The organization ID to apply the policy to. | `string` | `""` | no | 73 | | owners | List of comma-separated owners for each key declared in set\_owners\_for. | `list(string)` | `[]` | no | 74 | | policy\_for | Policy Root: set one of the following values to determine where the policy is applied. Possible values: ["project", "folder", "organization"]. | `string` | `"project"` | no | 75 | | prevent\_destroy | Set the prevent\_destroy lifecycle attribute on keys.. | `bool` | `true` | no | 76 | | serverless\_project\_id | The project where Cloud Function is going to be deployed. | `string` | n/a | yes | 77 | 78 | ## Outputs 79 | 80 | | Name | Description | 81 | |------|-------------| 82 | | key\_self\_link | Key self link. | 83 | | keyring\_resource | Keyring resource. | 84 | | keyring\_self\_link | Self link of the keyring. | 85 | 86 | 87 | 88 | ## Requirements 89 | 90 | ### Software 91 | 92 | The following dependencies must be available: 93 | 94 | * [Terraform](https://www.terraform.io/downloads.html) >= 1.3 95 | * [Terraform Provider for GCP](https://github.com/terraform-providers/terraform-provider-google) < 5.0 96 | 97 | ### APIs 98 | 99 | A project with the following APIs enabled must be used to host the 100 | resources of this module: 101 | 102 | * KMS Project 103 | * Google Cloud Key Management Service: `cloudkms.googleapis.com` 104 | 105 | ### Service Account 106 | 107 | A service account with the following roles must be used to provision 108 | the resources of this module: 109 | 110 | * KMS Project 111 | * Cloud KMS Admin: `roles/cloudkms.admin` 112 | * Serverless Project 113 | * Organization Policy Administrator: `roles/orgpolicy.policyAdmin` 114 | * Project IAM Admin: `roles/resourcemanager.projectIamAdmin` 115 | -------------------------------------------------------------------------------- /metadata.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: blueprints.cloud.google.com/v1alpha1 16 | kind: BlueprintMetadata 17 | metadata: 18 | name: terraform-google-cloud-functions 19 | annotations: 20 | config.kubernetes.io/local-config: "true" 21 | spec: 22 | title: Terraform Google Cloud Functions (Gen 2) module 23 | source: 24 | repo: sso://user/prabhuramasamy/terraform-google-cloud-functions 25 | sourceType: git 26 | version: 0.7.0 27 | actuationTool: 28 | type: Terraform 29 | version: '>= 0.13' 30 | examples: 31 | - name: cloud_function2_gcs_source 32 | location: examples/cloud_function2_gcs_source 33 | - name: cloud_function2_pubsub_trigger 34 | location: examples/cloud_function2_pubsub_trigger 35 | variables: 36 | - name: build_env_variables 37 | description: User-provided build-time environment variables 38 | type: map(string) 39 | required: false 40 | - name: description 41 | description: Short description of the function 42 | type: string 43 | required: false 44 | - name: docker_repository 45 | description: User managed repository created in Artifact Registry optionally with a customer managed encryption key. 46 | type: string 47 | required: false 48 | - name: entrypoint 49 | description: The name of the function (as defined in source code) that will be executed. Defaults to the resource name suffix, if not specified 50 | type: string 51 | required: true 52 | - name: event_trigger 53 | description: Event triggers for the function 54 | type: |- 55 | object({ 56 | trigger_region = string 57 | event_type = string 58 | service_account_email = string 59 | pubsub_topic = string 60 | retry_policy = string 61 | event_filters = set(object({ 62 | attribute = string 63 | attribute_value = string 64 | operator = string 65 | })) 66 | }) 67 | required: false 68 | - name: function_location 69 | description: The location of this cloud function 70 | type: string 71 | required: true 72 | - name: function_name 73 | description: A user-defined name of the function 74 | type: string 75 | required: true 76 | - name: labels 77 | description: A set of key/value label pairs associated with this Cloud Function 78 | type: map(string) 79 | required: false 80 | - name: project_id 81 | description: Project ID to create Cloud Function 82 | type: string 83 | required: true 84 | - name: repo_source 85 | description: Get the source from this location in a Cloud Source Repository 86 | type: |- 87 | object({ 88 | project_id = string 89 | repo_name = string 90 | branch_name = string 91 | dir = string 92 | tag_name = string 93 | commit_sha = string 94 | invert_regex = bool 95 | }) 96 | required: false 97 | - name: runtime 98 | description: The runtime in which to run the function. 99 | type: string 100 | required: true 101 | - name: service_config 102 | description: Details of the service 103 | type: |- 104 | object({ 105 | max_instance_count = string 106 | min_instance_count = string 107 | available_memory = string 108 | timeout_seconds = string 109 | runtime_env_variables = map(string) 110 | runtime_secret_env_variables = set(object({ 111 | key_name = string 112 | project_id = string 113 | secret = string 114 | version = string 115 | })) 116 | secret_volumes = set(object({ 117 | mount_path = string 118 | project_id = string 119 | secret = string 120 | versions = set(object({ 121 | version = string 122 | path = string 123 | })) 124 | })) 125 | vpc_connector = string 126 | vpc_connector_egress_settings = string 127 | ingress_settings = string 128 | service_account_email = string 129 | all_traffic_on_latest_revision = bool 130 | }) 131 | default: 132 | all_traffic_on_latest_revision: true 133 | available_memory: 256M 134 | ingress_settings: null 135 | max_instance_count: "100" 136 | min_instance_count: null 137 | runtime_env_variables: null 138 | runtime_secret_env_variables: null 139 | secret_volumes: null 140 | service_account_email: null 141 | timeout_seconds: "60" 142 | vpc_connector: null 143 | vpc_connector_egress_settings: null 144 | required: false 145 | - name: storage_source 146 | description: Get the source from this location in Google Cloud Storage 147 | type: |- 148 | object({ 149 | bucket = string 150 | object = string 151 | generation = string 152 | }) 153 | required: false 154 | - name: worker_pool 155 | description: Name of the Cloud Build Custom Worker Pool that should be used to build the function. 156 | type: string 157 | required: false 158 | outputs: 159 | - name: function_name 160 | description: Name of the Cloud Function (Gen 2) 161 | - name: function_uri 162 | description: URI of the Cloud Function (Gen 2) 163 | roles: 164 | - level: Project 165 | roles: 166 | - roles/owner 167 | services: 168 | - cloudresourcemanager.googleapis.com 169 | - storage-api.googleapis.com 170 | - serviceusage.googleapis.com 171 | -------------------------------------------------------------------------------- /modules/secure-cloud-function/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | module "cloud_serverless_network" { 19 | source = "GoogleCloudPlatform/cloud-run/google//modules/secure-serverless-net" 20 | version = "~> 0.21.5" 21 | 22 | connector_name = var.connector_name 23 | subnet_name = var.subnet_name 24 | enable_load_balancer_fw = "false" 25 | location = var.location 26 | vpc_project_id = var.vpc_project_id 27 | serverless_project_id = var.serverless_project_id 28 | shared_vpc_name = var.shared_vpc_name 29 | connector_on_host_project = false 30 | ip_cidr_range = var.ip_cidr_range 31 | create_subnet = var.create_subnet 32 | resource_names_suffix = var.resource_names_suffix 33 | 34 | serverless_service_identity_email = google_project_service_identity.cloudfunction_sa.email 35 | } 36 | 37 | data "google_service_account" "cloud_serverless_sa" { 38 | account_id = var.service_account_email 39 | } 40 | 41 | resource "google_service_account_iam_member" "identity_service_account_user" { 42 | service_account_id = data.google_service_account.cloud_serverless_sa.name 43 | role = "roles/iam.serviceAccountUser" 44 | member = "serviceAccount:${google_project_service_identity.cloudfunction_sa.email}" 45 | } 46 | 47 | resource "google_project_service_identity" "eventarc_sa" { 48 | provider = google-beta 49 | 50 | project = var.serverless_project_id 51 | service = "eventarc.googleapis.com" 52 | } 53 | 54 | resource "google_project_service_identity" "cloudfunction_sa" { 55 | provider = google-beta 56 | 57 | project = var.serverless_project_id 58 | service = "cloudfunctions.googleapis.com" 59 | } 60 | 61 | resource "google_project_service_identity" "artifact_sa" { 62 | provider = google-beta 63 | 64 | project = var.serverless_project_id 65 | service = "artifactregistry.googleapis.com" 66 | } 67 | 68 | data "google_storage_project_service_account" "gcs_account" { 69 | project = var.serverless_project_id 70 | } 71 | 72 | resource "google_project_service_identity" "pubsub_sa" { 73 | provider = google-beta 74 | 75 | project = var.serverless_project_id 76 | service = "pubsub.googleapis.com" 77 | } 78 | 79 | resource "time_sleep" "wait_service_identity_propagation" { 80 | create_duration = var.time_to_wait_service_identity_propagation 81 | 82 | depends_on = [ 83 | google_project_service_identity.artifact_sa, 84 | google_project_service_identity.pubsub_sa, 85 | google_project_service_identity.cloudfunction_sa, 86 | google_project_service_identity.eventarc_sa 87 | ] 88 | } 89 | 90 | module "cloud_function_security" { 91 | source = "../secure-cloud-function-security" 92 | 93 | kms_project_id = var.kms_project_id 94 | location = var.location 95 | serverless_project_id = var.serverless_project_id 96 | prevent_destroy = var.prevent_destroy 97 | key_name = var.key_name 98 | keyring_name = var.keyring_name 99 | key_rotation_period = var.key_rotation_period 100 | key_protection_level = var.key_protection_level 101 | policy_for = var.policy_for 102 | folder_id = var.folder_id 103 | organization_id = var.organization_id 104 | groups = var.groups 105 | 106 | encrypters = [ 107 | "serviceAccount:${google_project_service_identity.cloudfunction_sa.email}", 108 | "serviceAccount:${var.service_account_email}", 109 | "serviceAccount:${google_project_service_identity.artifact_sa.email}", 110 | "serviceAccount:${google_project_service_identity.eventarc_sa.email}", 111 | "serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}", 112 | "serviceAccount:${google_project_service_identity.pubsub_sa.email}" 113 | ] 114 | 115 | decrypters = [ 116 | "serviceAccount:${google_project_service_identity.cloudfunction_sa.email}", 117 | "serviceAccount:${var.service_account_email}", 118 | "serviceAccount:${google_project_service_identity.artifact_sa.email}", 119 | "serviceAccount:${google_project_service_identity.eventarc_sa.email}", 120 | "serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}", 121 | "serviceAccount:${google_project_service_identity.pubsub_sa.email}" 122 | ] 123 | 124 | depends_on = [ 125 | time_sleep.wait_service_identity_propagation 126 | ] 127 | } 128 | 129 | module "cloud_function_core" { 130 | source = "../secure-cloud-function-core" 131 | 132 | function_name = var.function_name 133 | function_description = var.function_description 134 | project_id = var.serverless_project_id 135 | project_number = var.serverless_project_number 136 | labels = var.labels 137 | location = var.location 138 | runtime = var.runtime 139 | entry_point = var.entry_point 140 | repo_source = var.repo_source 141 | storage_source = var.storage_source 142 | build_environment_variables = var.build_environment_variables 143 | event_trigger = var.event_trigger 144 | force_destroy = !var.prevent_destroy 145 | encryption_key = module.cloud_function_security.key_self_link 146 | bucket_lifecycle_rules = var.bucket_lifecycle_rules 147 | bucket_cors = var.bucket_cors 148 | network_id = var.network_id 149 | 150 | service_config = { 151 | max_instance_count = var.max_scale_instances 152 | min_instance_count = var.min_scale_instances 153 | available_memory = var.available_memory_mb 154 | timeout_seconds = var.timeout_seconds 155 | vpc_connector = module.cloud_serverless_network.connector_id 156 | service_account_email = var.service_account_email 157 | ingress_settings = var.ingress_settings 158 | all_traffic_on_latest_revision = var.all_traffic_on_latest_revision 159 | vpc_connector_egress_settings = var.vpc_egress_value 160 | runtime_env_variables = var.environment_variables 161 | 162 | runtime_secret_env_variables = var.secret_environment_variables 163 | secret_volumes = var.secret_volumes 164 | } 165 | 166 | depends_on = [ 167 | google_service_account_iam_member.identity_service_account_user 168 | ] 169 | } 170 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-core/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "project_id" { 18 | description = "The project ID to deploy to." 19 | type = string 20 | } 21 | 22 | variable "network_id" { 23 | description = "VPC network ID which is going to be used to connect the WorkerPool." 24 | type = string 25 | } 26 | 27 | variable "project_number" { 28 | description = "The project number to deploy to." 29 | type = number 30 | default = null 31 | } 32 | 33 | variable "encryption_key" { 34 | description = "The KMS Key to Encrypt Event Arc, source Bucket, docker repository." 35 | type = string 36 | } 37 | 38 | variable "function_name" { 39 | description = "The name of the Cloud Function to create." 40 | type = string 41 | } 42 | 43 | variable "function_description" { 44 | description = "The description of the Cloud Function to create." 45 | type = string 46 | default = "" 47 | } 48 | 49 | variable "labels" { 50 | description = "Labels to be assigned to resources." 51 | type = map(any) 52 | default = {} 53 | } 54 | 55 | variable "location" { 56 | description = "Cloud Function deployment location." 57 | type = string 58 | default = "us-east4" 59 | } 60 | 61 | variable "runtime" { 62 | description = "The runtime in which the function will be executed." 63 | type = string 64 | } 65 | 66 | variable "entry_point" { 67 | description = "The name of a method in the function source which will be invoked when the function is executed." 68 | type = string 69 | } 70 | 71 | variable "storage_source" { 72 | description = "Get the source from this location in Google Cloud Storage." 73 | type = object({ 74 | bucket = string 75 | object = string 76 | generation = optional(string, null) 77 | }) 78 | default = null 79 | } 80 | 81 | variable "repo_source" { 82 | description = "The source repository where the Cloud Function Source is stored. Do not use combined with source_path." 83 | type = object({ 84 | project_id = optional(string) 85 | repo_name = string 86 | branch_name = string 87 | dir = optional(string) 88 | tag_name = optional(string) 89 | commit_sha = optional(string) 90 | invert_regex = optional(bool, false) 91 | }) 92 | default = null 93 | } 94 | 95 | variable "build_environment_variables" { 96 | type = map(string) 97 | default = {} 98 | description = "A set of key/value environment variable pairs to be used when building the Function." 99 | } 100 | 101 | variable "event_trigger" { 102 | type = object({ 103 | trigger_region = optional(string) 104 | event_type = string 105 | service_account_email = string 106 | pubsub_topic = optional(string) 107 | retry_policy = string 108 | event_filters = optional(set(object({ 109 | attribute = string 110 | attribute_value = string 111 | operator = optional(string) 112 | }))) 113 | }) 114 | description = "A source that fires events in response to a condition in another service." 115 | } 116 | 117 | variable "service_config" { 118 | type = object({ 119 | max_instance_count = optional(string, 100) 120 | min_instance_count = optional(string, 1) 121 | available_memory = optional(string, "256M") 122 | timeout_seconds = optional(string, 60) 123 | runtime_env_variables = optional(map(string), null) 124 | runtime_secret_env_variables = optional(set(object({ 125 | key_name = string 126 | project_id = optional(string) 127 | secret = string 128 | version = string 129 | })), null) 130 | secret_volumes = optional(set(object({ 131 | mount_path = string 132 | project_id = optional(string) 133 | secret = string 134 | versions = set(object({ 135 | version = string 136 | path = string 137 | })) 138 | })), null) 139 | vpc_connector = string 140 | vpc_connector_egress_settings = optional(string, "ALL_TRAFFIC") 141 | ingress_settings = optional(string, "ALLOW_INTERNAL_AND_GCLB") 142 | service_account_email = string 143 | all_traffic_on_latest_revision = optional(bool, true) 144 | }) 145 | description = "Details of the service" 146 | } 147 | 148 | variable "force_destroy" { 149 | description = "Set the `force_destroy` attribute on the Cloud Storage." 150 | type = bool 151 | default = false 152 | } 153 | 154 | variable "bucket_cors" { 155 | description = "Configuration of CORS for bucket with structure as defined in https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket#cors." 156 | type = any 157 | default = [{ 158 | max_age_seconds = 0 159 | method = [ 160 | "GET", 161 | ] 162 | origin = [ 163 | "https://*.cloud.google.com", 164 | "https://*.corp.google.com", 165 | "https://*.corp.google.com:*", 166 | "https://*.cloud.google", 167 | "https://*.byoid.goog", 168 | ] 169 | response_header = [] 170 | }] 171 | } 172 | 173 | variable "bucket_lifecycle_rules" { 174 | description = "The bucket's Lifecycle Rules configuration." 175 | type = list(object({ 176 | # Object with keys: 177 | # - type - The type of the action of this Lifecycle Rule. Supported values: Delete and SetStorageClass. 178 | # - storage_class - (Required if action type is SetStorageClass) The target Storage Class of objects affected by this Lifecycle Rule. 179 | action = any 180 | 181 | # Object with keys: 182 | # - age - (Optional) Minimum age of an object in days to satisfy this condition. 183 | # - created_before - (Optional) Creation date of an object in RFC 3339 (e.g. 2017-06-13) to satisfy this condition. 184 | # - with_state - (Optional) Match to live and/or archived objects. Supported values include: "LIVE", "ARCHIVED", "ANY". 185 | # - matches_storage_class - (Optional) Storage Class of objects to satisfy this condition. Supported values include: MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, DURABLE_REDUCED_AVAILABILITY. 186 | # - matches_prefix - (Optional) One or more matching name prefixes to satisfy this condition. 187 | # - matches_suffix - (Optional) One or more matching name suffixes to satisfy this condition 188 | # - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition. 189 | condition = any 190 | })) 191 | default = [{ 192 | action = { 193 | type = "Delete" 194 | } 195 | condition = { 196 | age = 0 197 | days_since_custom_time = 0 198 | days_since_noncurrent_time = 0 199 | num_newer_versions = 3 200 | with_state = "ARCHIVED" 201 | } 202 | }] 203 | } 204 | -------------------------------------------------------------------------------- /test/integration/secure_cloud_function_internal_server/cloud_function_internal_server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cloud_function_internal_server 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | "testing" 21 | "time" 22 | 23 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" 24 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 25 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/utils" 26 | "github.com/stretchr/testify/assert" 27 | "github.com/tidwall/gjson" 28 | ) 29 | 30 | var ( 31 | RetryableTransientErrors = map[string]string{ 32 | // Error code 409 for concurrent policy changes. 33 | ".*Error 409.*There were concurrent policy changes.*": "Concurrent policy changes.", 34 | 35 | // API Rate limit exceeded errors can be retried. 36 | ".*rateLimitExceeded.*": "Rate limit exceeded.", 37 | 38 | // Project deletion is eventually consistent. Even if google_project resources inside the folder are deleted there may be a deletion error. 39 | ".*FOLDER_TO_DELETE_NON_EMPTY_VIOLATION.*": "Failed to delete non empty folder.", 40 | 41 | // Granting IAM Roles is eventually consistent. 42 | ".*Error 403.*Permission.*denied on resource.*": "Permission denied on resource.", 43 | 44 | // Editing VPC Service Controls is eventually consistent. 45 | ".*Error 403.*Request is prohibited by organization's policy.*vpcServiceControlsUniqueIdentifier.*": "Request is prohibited by organization's policy.", 46 | ".*Error code 7.*Request is prohibited by organization's policy.*vpcServiceControlsUniqueIdentifier.*": "Request is prohibited by organization's policy.", 47 | 48 | // Google Storage Service Agent propagation issue. 49 | ".*Error 400.*Service account service-.*@gs-project-accounts.iam.gserviceaccount.com does not exist.*": "Google Storage Service Agent propagation issue", 50 | } 51 | ) 52 | 53 | type Protocols struct { 54 | Protocol string 55 | Ports []string 56 | } 57 | 58 | func GetLastSplitElement(value string, sep string) string { 59 | splitted := strings.Split(value, sep) 60 | return splitted[len(splitted)-1] 61 | } 62 | 63 | func GetResultFieldStrSlice(rs []gjson.Result, field string) []string { 64 | s := make([]string, 0) 65 | for _, r := range rs { 66 | s = append(s, r.Get(field).String()) 67 | } 68 | return s 69 | } 70 | 71 | // GetOrgACMPolicyID gets the Organization Access Context Manager Policy ID 72 | func GetOrgACMPolicyID(t testing.TB, orgID string) string { 73 | filter := fmt.Sprintf("parent:organizations/%s", orgID) 74 | id := gcloud.Runf(t, "access-context-manager policies list --organization %s --filter %s --quiet", orgID, filter).Array() 75 | if len(id) == 0 { 76 | return "" 77 | } 78 | return GetLastSplitElement(id[0].Get("name").String(), "/") 79 | } 80 | 81 | func TestCFInternalServer(t *testing.T) { 82 | orgID := utils.ValFromEnv(t, "TF_VAR_org_id") 83 | policyID := GetOrgACMPolicyID(t, orgID) 84 | createACM := false 85 | 86 | vars := map[string]interface{}{ 87 | "create_access_context_manager_access_policy": createACM, 88 | "access_context_manager_policy_id": policyID, 89 | } 90 | 91 | if policyID == "" { 92 | createACM = true 93 | vars = map[string]interface{}{ 94 | "create_access_context_manager_access_policy": createACM, 95 | } 96 | } 97 | 98 | cft := tft.NewTFBlueprintTest(t, 99 | tft.WithVars(vars), 100 | tft.WithRetryableTerraformErrors(RetryableTransientErrors, 5, 1*time.Minute), 101 | ) 102 | 103 | cft.DefineVerify(func(assert *assert.Assertions) { 104 | // Removing DefaultVerify because Cloud Function API is changing the build_config/source/storage_source/generation and this modification is breaking the build validation. 105 | // cft.DefaultVerify(assert) 106 | 107 | location := "us-west1" 108 | networkProjectID := cft.GetStringOutput("network_project_id") 109 | projectID := cft.GetStringOutput("serverless_project_id") 110 | functionName := cft.GetStringOutput("cloud_function_name") 111 | connectorID := cft.GetStringOutput("connector_id") 112 | saEmail := cft.GetStringOutput("service_account_email") 113 | 114 | cf := gcloud.Runf(t, "functions describe %s --project %s --gen2 --region %s", functionName, projectID, location) 115 | cfTrigger := cf.Get("eventTrigger.trigger") 116 | assert.Equal("ACTIVE", cf.Get("state").String(), "Should be ACTIVE. Cloud Function is not successfully deployed.") 117 | assert.Equal(connectorID, cf.Get("serviceConfig.vpcConnector").String(), fmt.Sprintf("VPC Connector should be %s. Connector was not set.", connectorID)) 118 | assert.Equal("ALL_TRAFFIC", cf.Get("serviceConfig.vpcConnectorEgressSettings").String(), "Egress setting should be ALL_TRAFFIC.") 119 | assert.Equal("ALLOW_INTERNAL_AND_GCLB", cf.Get("serviceConfig.ingressSettings").String(), "Ingress setting should be ALLOW_INTERNAL_AND_GCLB.") 120 | assert.Equal(saEmail, cf.Get("serviceConfig.serviceAccountEmail").String(), fmt.Sprintf("Cloud Function should use the service account %s.", saEmail)) 121 | assert.Equal("google.cloud.storage.object.v1.finalized", cf.Get("eventTrigger.eventType").String(), "Cloud Function EventType should be google.cloud.storage.object.v1.finalized.") 122 | assert.NotNil(t, cfTrigger, "Trigger should exist.") 123 | 124 | gcloudArgsBucket := gcloud.WithCommonArgs([]string{"--project", projectID, "--json"}) 125 | bucketName := cft.GetStringOutput("cloudfunction_bucket_name") 126 | opBucket := gcloud.Run(t, fmt.Sprintf("alpha storage ls --buckets gs://%s", bucketName), gcloudArgsBucket).Array() 127 | assert.Equal(bucketName, opBucket[0].Get("metadata.name").String(), fmt.Sprintf("The bucket name should be %s.", bucketName)) 128 | assert.True(opBucket[0].Exists(), "Bucket %s should exist.", bucketName) 129 | 130 | instanceName := "webserver" 131 | instanceZone := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/zones/us-west1-b", projectID) 132 | opInstance := gcloud.Runf(t, "compute instances describe %s --zone=us-west1-b --project=%s", instanceName, projectID) 133 | assert.Equal(instanceName, opInstance.Get("name").String(), fmt.Sprintf("Instance name should be %s", instanceName)) 134 | assert.Equal(instanceZone, opInstance.Get("zone").String(), fmt.Sprintf("Instance should be in zone %s", instanceZone)) 135 | 136 | denyAllEgressName := "fw-e-shared-restricted-internal-server" 137 | denyAllEgressRule := gcloud.Runf(t, "compute firewall-rules describe %s --project %s", denyAllEgressName, networkProjectID) 138 | assert.Equal(denyAllEgressName, denyAllEgressRule.Get("name").String(), fmt.Sprintf("firewall rule %s should exist", denyAllEgressName)) 139 | assert.Equal("EGRESS", denyAllEgressRule.Get("direction").String(), fmt.Sprintf("firewall rule %s direction should be EGRESS", denyAllEgressName)) 140 | assert.True(denyAllEgressRule.Get("logConfig.enable").Bool(), fmt.Sprintf("firewall rule %s should have log configuration enabled", denyAllEgressName)) 141 | assert.Equal("10.0.0.0/28", denyAllEgressRule.Get("destinationRanges").Array()[0].String(), fmt.Sprintf("firewall rule %s destination ranges should be 10.0.0.0/28", denyAllEgressName)) 142 | assert.Equal("8000", denyAllEgressRule.Get("allowed.0.ports.0").String(), fmt.Sprintf("firewall rule %s should allow port 8000", denyAllEgressName)) 143 | 144 | }) 145 | cft.Test() 146 | } 147 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /****************************************** 18 | Cloud Function Definition with 19 | Repo/Storage Build Source and Event Trigger 20 | *****************************************/ 21 | resource "google_cloudfunctions2_function" "function" { 22 | name = var.function_name 23 | location = var.function_location 24 | description = var.description 25 | project = var.project_id 26 | 27 | build_config { 28 | runtime = var.runtime 29 | entry_point = var.entrypoint 30 | environment_variables = var.build_env_variables 31 | service_account = var.build_service_account 32 | 33 | source { 34 | dynamic "storage_source" { 35 | for_each = var.repo_source == null ? [var.storage_source] : [] 36 | content { 37 | bucket = storage_source.value.bucket 38 | object = storage_source.value.object 39 | generation = storage_source.value.generation 40 | } 41 | } 42 | 43 | dynamic "repo_source" { 44 | for_each = var.storage_source == null ? [var.repo_source] : [] 45 | content { 46 | project_id = repo_source.value.project_id 47 | repo_name = repo_source.value.repo_name 48 | branch_name = repo_source.value.branch_name 49 | dir = repo_source.value.dir 50 | tag_name = repo_source.value.tag_name 51 | commit_sha = repo_source.value.commit_sha 52 | invert_regex = repo_source.value.invert_regex 53 | } 54 | } 55 | } 56 | 57 | worker_pool = var.worker_pool 58 | docker_repository = var.docker_repository 59 | } 60 | 61 | dynamic "event_trigger" { 62 | for_each = var.event_trigger != null ? [var.event_trigger] : [] 63 | content { 64 | trigger_region = event_trigger.value["trigger_region"] != null ? event_trigger.value["trigger_region"] : null 65 | event_type = event_trigger.value["event_type"] != null ? event_trigger.value["event_type"] : null 66 | pubsub_topic = event_trigger.value["pubsub_topic"] != null ? event_trigger.value["pubsub_topic"] : null 67 | service_account_email = event_trigger.value["service_account_email"] != null ? event_trigger.value["service_account_email"] : null 68 | retry_policy = event_trigger.value["retry_policy"] != null ? event_trigger.value["retry_policy"] : null 69 | 70 | dynamic "event_filters" { 71 | for_each = event_trigger.value.event_filters != null ? event_trigger.value.event_filters : [] 72 | content { 73 | attribute = event_filters.value.attribute 74 | value = event_filters.value.attribute_value 75 | operator = event_filters.value.operator 76 | } 77 | } 78 | } 79 | } 80 | 81 | dynamic "service_config" { 82 | for_each = var.service_config != null ? [var.service_config] : [] 83 | content { 84 | max_instance_count = service_config.value.max_instance_count 85 | min_instance_count = service_config.value.min_instance_count 86 | available_memory = service_config.value.available_memory 87 | available_cpu = service_config.value.available_cpu 88 | timeout_seconds = service_config.value.timeout_seconds 89 | environment_variables = service_config.value.runtime_env_variables != null ? service_config.value.runtime_env_variables : {} 90 | 91 | vpc_connector = service_config.value.vpc_connector 92 | vpc_connector_egress_settings = service_config.value.vpc_connector != null ? service_config.value.vpc_connector_egress_settings : null 93 | ingress_settings = service_config.value.ingress_settings 94 | 95 | service_account_email = service_config.value.service_account_email 96 | all_traffic_on_latest_revision = service_config.value.all_traffic_on_latest_revision 97 | 98 | dynamic "secret_environment_variables" { 99 | for_each = service_config.value.runtime_secret_env_variables != null ? service_config.value.runtime_secret_env_variables : [] 100 | iterator = sev 101 | content { 102 | key = sev.value.key_name 103 | project_id = sev.value.project_id 104 | secret = sev.value.secret 105 | version = sev.value.version 106 | } 107 | } 108 | 109 | dynamic "secret_volumes" { 110 | for_each = service_config.value.secret_volumes != null ? service_config.value.secret_volumes : [] 111 | content { 112 | mount_path = secret_volumes.value.mount_path 113 | project_id = secret_volumes.value.project_id 114 | secret = secret_volumes.value.secret 115 | dynamic "versions" { 116 | for_each = secret_volumes.value.versions != null ? secret_volumes.value.versions : [] 117 | content { 118 | version = versions.value.version 119 | path = versions.value.path 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | labels = var.labels != null ? var.labels : {} 128 | } 129 | 130 | // IAM for invoking HTTP functions (roles/run.invoker) 131 | resource "google_cloudfunctions2_function_iam_member" "invokers" { 132 | for_each = toset(contains(keys(var.members), "invokers") ? var.members["invokers"] : []) 133 | location = google_cloudfunctions2_function.function.location 134 | project = google_cloudfunctions2_function.function.project 135 | cloud_function = google_cloudfunctions2_function.function.name 136 | role = "roles/cloudfunctions.invoker" 137 | member = each.value 138 | 139 | depends_on = [ 140 | google_cloudfunctions2_function.function 141 | ] 142 | } 143 | 144 | // Read and write access to all functions-related resources (roles/run.developer) 145 | resource "google_cloudfunctions2_function_iam_member" "developers" { 146 | for_each = toset(contains(keys(var.members), "developers") ? var.members["developers"] : []) 147 | location = google_cloudfunctions2_function.function.location 148 | project = google_cloudfunctions2_function.function.project 149 | cloud_function = google_cloudfunctions2_function.function.name 150 | role = "roles/cloudfunctions.developer" 151 | member = each.value 152 | 153 | depends_on = [ 154 | google_cloudfunctions2_function.function 155 | ] 156 | } 157 | 158 | // IAM for invoking HTTP functions (roles/run.invoker) 159 | resource "google_cloud_run_service_iam_member" "invokers" { 160 | for_each = toset(contains(keys(var.members), "invokers") ? var.members["invokers"] : []) 161 | location = google_cloudfunctions2_function.function.location 162 | project = google_cloudfunctions2_function.function.project 163 | service = google_cloudfunctions2_function.function.name 164 | role = "roles/run.invoker" 165 | member = each.value 166 | 167 | depends_on = [ 168 | google_cloudfunctions2_function.function 169 | ] 170 | } 171 | 172 | // Read and write access to all functions-related resources (roles/run.developer) 173 | resource "google_cloud_run_service_iam_member" "developers" { 174 | for_each = toset(contains(keys(var.members), "developers") ? var.members["developers"] : []) 175 | location = google_cloudfunctions2_function.function.location 176 | project = google_cloudfunctions2_function.function.project 177 | service = google_cloudfunctions2_function.function.name 178 | role = "roles/run.developer" 179 | member = each.value 180 | 181 | depends_on = [ 182 | google_cloudfunctions2_function.function 183 | ] 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Google Cloud Functions (Gen 2) module 2 | 3 | The Terraform module handles the deployment of Cloud Functions (Gen 2) on GCP. 4 | 5 | The resources/services/activations/deletions that this module will create/trigger are: 6 | 7 | - Deploy Cloud Functions (2nd Gen) with provided source code and trigger 8 | - Provide Cloud Functions Invoker or Developer roles to the users and service accounts 9 | 10 | ## Assumptions and Prerequisites 11 | 12 | This module assumes that below mentioned prerequisites are in place before consuming the module. 13 | 14 | * APIs are enabled 15 | * Permissions are available. 16 | * You have explicitly granted the necessary IAM roles for the underlying service account used by Cloud Build, `build_service_account`. If `build_service_account` is not specified, then the default compute service account is used, which has [no default IAM roles in new organizations]([url](https://cloud.google.com/resource-manager/docs/secure-by-default-organizations#organization_policies_enforced_on_organization_resources)). At a minimum, the following IAM roles are required for the build service account: 17 | * `roles/logging.logWriter` 18 | * `roles/storage.objectViewer` 19 | * `roles/artifactregistry.writer` 20 | 21 | 22 | ## Usage 23 | 24 | Basic usage of this module is as follows: 25 | 26 | ```hcl 27 | module "cloud_functions2" { 28 | source = "GoogleCloudPlatform/cloud-functions/google" 29 | version = "~> 0.7" 30 | 31 | # Required variables 32 | function_name = "" 33 | project_id = "" 34 | function_location = "" 35 | runtime = "" 36 | entrypoint = "" 37 | storage_source = { 38 | bucket = "" 39 | object = "" 40 | generation = "" 41 | } 42 | } 43 | ``` 44 | 45 | Functional examples are included in the 46 | [examples](./examples/) directory. 47 | 48 | 49 | ## Inputs 50 | 51 | | Name | Description | Type | Default | Required | 52 | |------|-------------|------|---------|:--------:| 53 | | build\_env\_variables | User-provided build-time environment variables | `map(string)` | `null` | no | 54 | | build\_service\_account | Cloud Function Build Service Account Id. This is The fully-qualified name of the service account to be used for building the container. | `string` | `null` | no | 55 | | description | Short description of the function | `string` | `null` | no | 56 | | docker\_repository | User managed repository created in Artifact Registry optionally with a customer managed encryption key. | `string` | `null` | no | 57 | | entrypoint | The name of the function (as defined in source code) that will be executed. Defaults to the resource name suffix, if not specified | `string` | n/a | yes | 58 | | event\_trigger | Event triggers for the function |
object({
trigger_region = optional(string)
event_type = string
service_account_email = string
pubsub_topic = optional(string)
retry_policy = string
event_filters = optional(set(object({
attribute = string
attribute_value = string
operator = optional(string)
})))
})
| `null` | no | 59 | | function\_location | The location of this cloud function | `string` | n/a | yes | 60 | | function\_name | A user-defined name of the function | `string` | n/a | yes | 61 | | labels | A set of key/value label pairs associated with this Cloud Function | `map(string)` | `null` | no | 62 | | members | Cloud Function Invoker and Developer roles for Users/SAs. Key names must be developers and/or invokers | `map(list(string))` | `{}` | no | 63 | | project\_id | Project ID to create Cloud Function | `string` | n/a | yes | 64 | | repo\_source | Get the source from this location in a Cloud Source Repository |
object({
project_id = optional(string)
repo_name = string
branch_name = string
dir = optional(string)
tag_name = optional(string)
commit_sha = optional(string)
invert_regex = optional(bool, false)
})
| `null` | no | 65 | | runtime | The runtime in which to run the function. | `string` | n/a | yes | 66 | | service\_config | Details of the service |
object({
max_instance_count = optional(string, 100)
min_instance_count = optional(string, 1)
available_memory = optional(string, "256M")
available_cpu = optional(string, 1)
timeout_seconds = optional(string, 60)
runtime_env_variables = optional(map(string), null)
runtime_secret_env_variables = optional(set(object({
key_name = string
project_id = optional(string)
secret = string
version = string
})), null)
secret_volumes = optional(set(object({
mount_path = string
project_id = optional(string)
secret = string
versions = set(object({
version = string
path = string
}))
})), null)
vpc_connector = optional(string, null)
vpc_connector_egress_settings = optional(string, null)
ingress_settings = optional(string, null)
service_account_email = optional(string, null)
all_traffic_on_latest_revision = optional(bool, true)
})
| `{}` | no | 67 | | storage\_source | Get the source from this location in Google Cloud Storage |
object({
bucket = string
object = string
generation = optional(string, null)
})
| `null` | no | 68 | | worker\_pool | Name of the Cloud Build Custom Worker Pool that should be used to build the function. | `string` | `null` | no | 69 | 70 | ## Outputs 71 | 72 | | Name | Description | 73 | |------|-------------| 74 | | function\_name | Name of the Cloud Function (Gen 2) | 75 | | function\_uri | URI of the Cloud Function (Gen 2) | 76 | 77 | 78 | 79 | ## Requirements 80 | 81 | These sections describe requirements for using this module. 82 | 83 | ### Software 84 | 85 | The following dependencies must be available: 86 | 87 | - [Terraform][terraform] v1.3+ 88 | - [Terraform Provider for GCP][terraform-provider-gcp] plugin v3.0 89 | 90 | ### Service Account 91 | 92 | A service account with the following roles must be used to provision 93 | the resources of this module: 94 | 95 | - Storage Admin: `roles/storage.admin` 96 | - Cloud Functions Admin: `roles/cloudfunctions.admin` 97 | - Cloud Run Admin: `roles/run.admin` 98 | - Pub/Sub Admin: `roles/pubsub.admin` 99 | - Artifact Registry Admin: `roles/artifactregistry.admin` 100 | - Cloud Build Editor: `roles/cloudbuild.builds.editor` 101 | - Secret Manager Admin: `roles/secretmanager.admin` 102 | 103 | The [Project Factory module][project-factory-module] and the 104 | [IAM module][iam-module] may be used in combination to provision a 105 | service account with the necessary roles applied. 106 | 107 | ### APIs 108 | 109 | A project with the following APIs enabled must be used to host the 110 | resources of this module: 111 | 112 | - Google Cloud Storage JSON API: `storage-api.googleapis.com` 113 | - Cloud Functions API: `cloudfunctions.googleapis.com` 114 | - Cloud Run Admin API: `run.googleapis.com` 115 | - Cloud Build API: `cloudbuild.googleapis.com` 116 | - Artifact Registry API: `artifactregistry.googleapis.com` 117 | - Pub/Sub API: `pubsub.googleapis.com` 118 | - Secret Manager API: `secretmanager.googleapis.com` 119 | - EventArc API: `eventarc.googleapis.com` 120 | 121 | The [Project Factory module][project-factory-module] can be used to 122 | provision a project with the necessary APIs enabled. 123 | 124 | ## Contributing 125 | 126 | Refer to the [contribution guidelines](./CONTRIBUTING.md) for 127 | information on contributing to this module. 128 | 129 | [iam-module]: https://registry.terraform.io/modules/terraform-google-modules/iam/google 130 | [project-factory-module]: https://registry.terraform.io/modules/terraform-google-modules/project-factory/google 131 | [terraform-provider-gcp]: https://www.terraform.io/docs/providers/google/index.html 132 | [terraform]: https://www.terraform.io/downloads.html 133 | 134 | ## Security Disclosures 135 | 136 | Please see our [security disclosure process](./SECURITY.md). 137 | -------------------------------------------------------------------------------- /modules/secure-cloud-function-core/README.md: -------------------------------------------------------------------------------- 1 | # Secure Cloud Function (2nd Gen) Core 2 | 3 | This module handles the basic deployment core configurations for Cloud Function (2nd Gen) module. 4 | 5 | The resources/services/activations/deletions that this module will create/trigger are: 6 | 7 | * Creates a Cloud Function (2nd Gen). 8 | * Creates the Cloud Function source bucket in the same location as the Cloud Function. 9 | * Configure the EventArc Google Channel to use Customer Encryption Key in the Cloud Function location. 10 | * **Warning:** If there is another CMEK configured for the same region, it will be overwritten. 11 | * Creates a private worker pool for Cloud Build configured to not use External IP. 12 | * Grants Cloud Functions Invoker to EventArc Trigger Service Account. 13 | * Enables Container Scanning. 14 | 15 | ## Usage 16 | 17 | ```hcl 18 | module "secure_cloud_function_core" { 19 | source = "GoogleCloudPlatform/cloud-functions/google//modules/secure-cloud-function-core" 20 | version = "~> 0.7" 21 | 22 | function_name = 23 | function_description = 24 | project_id = 25 | project_number = 26 | labels = 27 | location = 28 | runtime = 29 | entry_point = 30 | storage_source = 31 | build_environment_variables = 32 | event_trigger = 33 | encryption_key = 34 | 35 | service_config = { 36 | vpc_connector = 37 | service_account_email = 38 | ingress_settings = "ALLOW_INTERNAL_AND_GCLB" 39 | all_traffic_on_latest_revision = true 40 | vpc_connector_egress_settings = "ALL_TRAFFIC" 41 | runtime_env_variables = 42 | 43 | runtime_secret_env_variables = 44 | secret_volumes = 45 | } 46 | 47 | ``` 48 | 49 | 50 | ## Inputs 51 | 52 | | Name | Description | Type | Default | Required | 53 | |------|-------------|------|---------|:--------:| 54 | | bucket\_cors | Configuration of CORS for bucket with structure as defined in https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket#cors. | `any` |
[
{
"max_age_seconds": 0,
"method": [
"GET"
],
"origin": [
"https://*.cloud.google.com",
"https://*.corp.google.com",
"https://*.corp.google.com:*",
"https://*.cloud.google",
"https://*.byoid.goog"
],
"response_header": []
}
]
| no | 55 | | bucket\_lifecycle\_rules | The bucket's Lifecycle Rules configuration. |
list(object({
# Object with keys:
# - type - The type of the action of this Lifecycle Rule. Supported values: Delete and SetStorageClass.
# - storage_class - (Required if action type is SetStorageClass) The target Storage Class of objects affected by this Lifecycle Rule.
action = any

# Object with keys:
# - age - (Optional) Minimum age of an object in days to satisfy this condition.
# - created_before - (Optional) Creation date of an object in RFC 3339 (e.g. 2017-06-13) to satisfy this condition.
# - with_state - (Optional) Match to live and/or archived objects. Supported values include: "LIVE", "ARCHIVED", "ANY".
# - matches_storage_class - (Optional) Storage Class of objects to satisfy this condition. Supported values include: MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, DURABLE_REDUCED_AVAILABILITY.
# - matches_prefix - (Optional) One or more matching name prefixes to satisfy this condition.
# - matches_suffix - (Optional) One or more matching name suffixes to satisfy this condition
# - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition.
condition = any
}))
|
[
{
"action": {
"type": "Delete"
},
"condition": {
"age": 0,
"days_since_custom_time": 0,
"days_since_noncurrent_time": 0,
"num_newer_versions": 3,
"with_state": "ARCHIVED"
}
}
]
| no | 56 | | build\_environment\_variables | A set of key/value environment variable pairs to be used when building the Function. | `map(string)` | `{}` | no | 57 | | encryption\_key | The KMS Key to Encrypt Event Arc, source Bucket, docker repository. | `string` | n/a | yes | 58 | | entry\_point | The name of a method in the function source which will be invoked when the function is executed. | `string` | n/a | yes | 59 | | event\_trigger | A source that fires events in response to a condition in another service. |
object({
trigger_region = optional(string)
event_type = string
service_account_email = string
pubsub_topic = optional(string)
retry_policy = string
event_filters = optional(set(object({
attribute = string
attribute_value = string
operator = optional(string)
})))
})
| n/a | yes | 60 | | force\_destroy | Set the `force_destroy` attribute on the Cloud Storage. | `bool` | `false` | no | 61 | | function\_description | The description of the Cloud Function to create. | `string` | `""` | no | 62 | | function\_name | The name of the Cloud Function to create. | `string` | n/a | yes | 63 | | labels | Labels to be assigned to resources. | `map(any)` | `{}` | no | 64 | | location | Cloud Function deployment location. | `string` | `"us-east4"` | no | 65 | | network\_id | VPC network ID which is going to be used to connect the WorkerPool. | `string` | n/a | yes | 66 | | project\_id | The project ID to deploy to. | `string` | n/a | yes | 67 | | project\_number | The project number to deploy to. | `number` | `null` | no | 68 | | repo\_source | The source repository where the Cloud Function Source is stored. Do not use combined with source\_path. |
object({
project_id = optional(string)
repo_name = string
branch_name = string
dir = optional(string)
tag_name = optional(string)
commit_sha = optional(string)
invert_regex = optional(bool, false)
})
| `null` | no | 69 | | runtime | The runtime in which the function will be executed. | `string` | n/a | yes | 70 | | service\_config | Details of the service |
object({
max_instance_count = optional(string, 100)
min_instance_count = optional(string, 1)
available_memory = optional(string, "256M")
timeout_seconds = optional(string, 60)
runtime_env_variables = optional(map(string), null)
runtime_secret_env_variables = optional(set(object({
key_name = string
project_id = optional(string)
secret = string
version = string
})), null)
secret_volumes = optional(set(object({
mount_path = string
project_id = optional(string)
secret = string
versions = set(object({
version = string
path = string
}))
})), null)
vpc_connector = string
vpc_connector_egress_settings = optional(string, "ALL_TRAFFIC")
ingress_settings = optional(string, "ALLOW_INTERNAL_AND_GCLB")
service_account_email = string
all_traffic_on_latest_revision = optional(bool, true)
})
| n/a | yes | 71 | | storage\_source | Get the source from this location in Google Cloud Storage. |
object({
bucket = string
object = string
generation = optional(string, null)
})
| `null` | no | 72 | 73 | ## Outputs 74 | 75 | | Name | Description | 76 | |------|-------------| 77 | | artifact\_registry\_repository\_id | The ID of the Artifact Registry created to store Cloud Function images. | 78 | | cloudbuild\_worker\_pool\_id | The ID of the Cloud Build worker pool created to build Cloud Function images. | 79 | | cloudfunction\_bucket | The Cloud Function source bucket. | 80 | | cloudfunction\_bucket\_name | Name of the Cloud Function source bucket. | 81 | | cloudfunction\_name | Name of the created service. | 82 | | cloudfunction\_url | The URL on which the deployed service is available. | 83 | | eventarc\_google\_channel\_id | The ID of the Google Eventarc Channel. | 84 | 85 | 86 | 87 | ## Requirements 88 | 89 | ### Software 90 | 91 | The following dependencies must be available: 92 | 93 | * [Terraform](https://www.terraform.io/downloads.html) >= 1.3 94 | * [Terraform Provider for GCP](https://github.com/terraform-providers/terraform-provider-google) plugin < 5.0 95 | 96 | ### APIs 97 | 98 | A project with the following APIs enabled must be used to host the 99 | resources of this module: 100 | 101 | * Serverless Project 102 | * Container Scanning: `containerscanning.googleapis.com` 103 | 104 | ### Service Account 105 | 106 | A service account with the following roles must be used to provision 107 | the resources of this module: 108 | 109 | * Viewer: `roles/viewer` 110 | * Cloud Function Developer: `roles/cloudfunctions.developer` 111 | * Compute Network User: `roles/compute.networkUser` 112 | * Artifact Registry Admin: `roles/artifactregistry.admin` 113 | * Cloud Build Editor: `roles/cloudbuild.builds.editor` 114 | * Cloud Build Worker Pool Owner: `roles/cloudbuild.workerPoolOwner` 115 | * Pub/Sub Admin: `roles/pubsub.admin` 116 | * Storage Admin: `roles/storage.admin` 117 | * Service Usage Admin: `roles/serviceusage.serviceUsageAdmin` 118 | * Eventarc Developer: `roles/eventarc.developer` 119 | --------------------------------------------------------------------------------