* Disable tests for VPC connector and Cloud Functions, CFs are not supporrted in the default region * fix permissions to secrets for Cloud Run * add permissions admin permissions to any SA within project to `var.bucket` * add permissions to access the secret to any SA within project to secrets created by fixture * disable custom roles in E2E tests, as `var.organization_id` is not the same org, within which projects are created in E2E
Cloud Function Module (V1)
Cloud Function management, with support for IAM roles, optional bucket creation and bundle via GCS URI, local zip, or local source folder.
TODO
- add support for
source_repository
Examples
HTTP trigger
This deploys a Cloud Function with an HTTP endpoint, using a pre-existing GCS bucket for deployment, creating service account dedicated for this function, granting it roles/logging.logWriter and roles/monitoring.metricWriter roles, and delegating access control to the containing project.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=http-trigger.yaml e2e
PubSub and non-HTTP triggers
Other trigger types other than HTTP are configured via the trigger_config variable. This example shows a PubSub trigger.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
trigger_config = {
event = "google.pubsub.topic.publish"
resource = module.pubsub.topic.name
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest inventory=pubsub-non-http-trigger.yaml fixtures=fixtures/pubsub.tf,fixtures/functions-default-sa-iam-grants.tf e2e
Controlling HTTP access
To allow anonymous access to the function, grant the roles/cloudfunctions.invoker role to the special allUsers identifier. Use specific identities (service accounts, groups, etc.) instead of allUsers to only allow selective access.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
iam = {
"roles/cloudfunctions.invoker" = ["allUsers"]
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=iam.yaml e2e
GCS bucket creation
You can have the module auto-create the GCS bucket used for deployment via the bucket_config variable. Setting bucket_config.location to null will also use the function region for GCS.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
prefix = var.prefix
name = "test-cf-http"
bucket_name = var.bucket
bucket_config = {
force_destroy = true
lifecycle_delete_age_days = 1
}
bundle_config = {
path = "assets/sample-function/"
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=bucket-creation.yaml e2e
Service account management
To use a custom service account managed by the module, set service_account_config.create to true.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
service_account_config = {
create = true
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest inventory=service-account.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
To use an externally managed service account, pass its email in service_account_config.email and set service_account_config.create to false.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
service_account_config = {
create = false
email = var.service_account.email
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest modules=1 resources=5 fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
Custom bundle config
The Cloud Function bundle can be configured via the bundle_config variable. The only mandatory argument is bundle_config.path which can point to:
- a GCS URI of a ZIP archive
- a local path to a ZIP archive
- a local path to a source folder
When a GCS URI or a local zip file are used, a change in their names will trigger redeployment. When a local source folder is used a ZIP archive will be automatically generated and its internally derived checksum will drive redeployment. You can optionally control its name and exclusions via the attributes in bundle_config.folder_options.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
folder_options = {
archive_path = "bundle.zip"
excludes = ["__pycache__"]
}
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest inventory=custom-bundle.yaml fixtures=fixtures/functions-default-sa-iam-grants.tf e2e
Private Cloud Build Pool
This deploys a Cloud Function with an HTTP endpoint, using a pre-existing GCS bucket for deployment using a pre existing private Cloud Build worker pool.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
build_worker_pool = google_cloudbuild_worker_pool.pool.id
bundle_config = {
path = "assets/sample-function/"
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest inventory=private-build-pool.yaml fixtures=fixtures/cloudbuild-custom-pool.tf,fixtures/functions-default-sa-iam-grants.tf e2e
Multiple Cloud Functions within project
When deploying multiple functions do not reuse bundle_config.archive_path between instances as the result is undefined. Default archive_path creates file in /tmp folder using project Id and function name to avoid name conflicts.
module "cf-http-one" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http-one"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
}
module "cf-http-two" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http-two"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=multiple_functions.yaml e2e
Mounting secrets from Secret Manager
This provides the latest value of the secret var_secret as VARIABLE_SECRET environment variable and three values of path_secret mounted in filesystem:
/app/secret/ver1contains version referenced bymodule.secret-manager.version_versions["credentials:v1"]
Remember to grant access to secrets to the service account running Cloud Function.
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
secrets = {
VARIABLE_SECRET = {
is_volume = false
project_id = var.project_number # use project_number to avoid perm-diff
secret = reverse(split("/", module.secret-manager.secrets["credentials"].name))[0]
versions = [
"latest"
]
}
"/app/secret" = {
is_volume = true
project_id = var.project_number # use project_number to avoid perm-diff
secret = reverse(split("/", module.secret-manager.secrets["credentials"].name))[0]
versions = [
"${module.secret-manager.version_versions["credentials/v1"]}:/ver1"
]
}
}
depends_on = [
google_project_iam_member.bucket_default_compute_account_grant,
]
}
module "secret-manager" {
source = "./fabric/modules/secret-manager"
project_id = var.project_id
secrets = {
credentials = {
iam = {
"roles/secretmanager.secretAccessor" = [module.cf-http.service_account_iam_email]
}
versions = {
v1 = { data = "manual foo bar spam" }
}
}
}
}
# tftest fixtures=fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
Using CMEK to encrypt function resources
This encrypt bucket gcf-sources-* with the provided kms key. The repository has to be encrypted with the same kms key.
module "project" {
source = "./fabric/modules/project"
name = "cf-v1"
billing_account = var.billing_account_id
prefix = var.prefix
parent = var.folder_id
services = [
"artifactregistry.googleapis.com",
"cloudbuild.googleapis.com",
"cloudfunctions.googleapis.com",
"cloudkms.googleapis.com",
"compute.googleapis.com",
"storage.googleapis.com",
]
iam = {
# grant compute default service account that is used by Cloud Founction
# permission to read from the buckets so it can read function sources
"roles/storage.objectViewer" = [
"serviceAccount:${module.project.default_service_accounts.compute}"
]
}
}
module "kms" {
source = "./fabric/modules/kms"
project_id = module.project.project_id
keyring = {
location = var.regions.secondary
name = "${var.prefix}-keyring"
}
keys = {
"key-regional" = {
}
}
iam = {
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
module.project.service_agents["artifactregistry"].iam_email,
module.project.service_agents["cloudfunctions"].iam_email,
module.project.service_agents["storage"].iam_email,
]
}
}
module "artifact-registry" {
source = "./fabric/modules/artifact-registry"
project_id = module.project.project_id
location = var.regions.secondary
name = "registry"
format = { docker = { standard = {} } }
encryption_key = module.kms.key_ids["key-regional"]
iam = {
"roles/artifactregistry.createOnPushWriter" = [
# grant compute default service account that is used by Cloud Build
# permission to push compiled container into Artifact Registry
"serviceAccount:${module.project.default_service_accounts.compute}",
]
}
}
module "cf-http" {
source = "./fabric/modules/cloud-function-v1"
project_id = module.project.project_id
region = var.regions.secondary
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
kms_key = module.kms.key_ids["key-regional"]
repository_settings = {
repository = module.artifact-registry.id
}
}
# tftest inventory=cmek.yaml
VPC Access Connector
You can use an existing VPC Access Connector to connect to a VPC from Cloud Run.
module "cf_http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.region
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
vpc_connector = {
name = google_vpc_access_connector.connector.id
egress_setting = "ALL_TRAFFIC"
}
}
# tftest fixtures=fixtures/vpc-connector.tf inventory=service-vpc-access-connector.yaml
If creation of the VPC Access Connector is required, use the vpc_connector.create and vpc_connector_create variable which also supports optional attributes like number of instances, machine type, or throughput.
module "cf_http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.region
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
vpc_connector = {
create = true
}
vpc_connector_create = {
ip_cidr_range = "10.10.10.0/28"
network = var.vpc.self_link
instances = {
max = 10
min = 3
}
}
}
# tftest inventory=service-vpc-access-connector-create.yaml
Note that if you are using a Shared VPC for the connector, you need to specify a subnet and the host project if this is not where the Cloud Run service is deployed.
module "cf_http" {
source = "./fabric/modules/cloud-function-v1"
project_id = var.project_id
region = var.region
name = "test-cf-http"
bucket_name = var.bucket
bundle_config = {
path = "assets/sample-function/"
}
vpc_connector = {
create = true
}
vpc_connector_create = {
machine_type = "e2-standard-4"
subnet = {
name = module.net-vpc-host.subnets["${var.region}/fixture-subnet-28"].name
project_id = module.project-host.project_id
}
throughput = {
max = 300
min = 200
}
}
}
# tftest fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml
Variables
| name | description | type | required | default |
|---|---|---|---|---|
| bucket_name | Name of the bucket that will be used for the function code. It will be created with prefix prepended if bucket_config is not null. | string |
✓ | |
| bundle_config | Cloud function source. Path can point to a GCS object URI, or a local path. A local path to a zip archive will generate a GCS object using its basename, a folder will be zipped and the GCS object name inferred when not specified. | object({…}) |
✓ | |
| name | Name used for cloud function and associated resources. | string |
✓ | |
| project_id | Project id used for all resources. | string |
✓ | |
| region | Region used for all resources. | string |
✓ | |
| bucket_config | Enable and configure auto-created bucket. Set fields to null to use defaults. | object({…}) |
null |
|
| build_environment_variables | A set of key/value environment variable pairs available during build time. | map(string) |
{} |
|
| build_worker_pool | Build worker pool, in projects//locations//workerPools/<POOL_NAME> format. | string |
null |
|
| context | Context-specific interpolations. | object({…}) |
{} |
|
| description | Optional description. | string |
"Terraform managed." |
|
| environment_variables | Cloud function environment variables. | map(string) |
{} |
|
| function_config | Cloud function configuration. Defaults to using main as entrypoint, 1 instance with 256MiB of memory, and 180 second timeout. | object({…}) |
{…} |
|
| https_security_level | The security level for the function: Allowed values are SECURE_ALWAYS, SECURE_OPTIONAL. | string |
null |
|
| iam | IAM bindings for topic in {ROLE => [MEMBERS]} format. | map(list(string)) |
{} |
|
| ingress_settings | Control traffic that reaches the cloud function. Allowed values are ALLOW_ALL, ALLOW_INTERNAL_AND_GCLB and ALLOW_INTERNAL_ONLY . | string |
null |
|
| kms_key | Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt function resources in key id format. If specified, you must also provide an artifact registry repository using the docker_repository field that was created with the same KMS crypto key. | string |
null |
|
| labels | Resource labels. | map(string) |
{} |
|
| prefix | Optional prefix used for resource names. | string |
null |
|
| repository_settings | Docker Registry to use for storing the function's Docker images and specific repository. If kms_key is provided, the repository must have already been encrypted with the key. | object({…}) |
{…} |
|
| secrets | Secret Manager secrets. Key is the variable name or mountpoint, volume versions are in version:path format. | map(object({…})) |
{} |
|
| service_account_config | Service account configurations. | object({…}) |
{} |
|
| trigger_config | Function trigger configuration. Leave null for HTTP trigger. | object({…}) |
null |
|
| vpc_connector | VPC connector configuration. Set create to 'true' if a new connector needs to be created. | object({…}) |
{} |
|
| vpc_connector_create | VPC connector network configuration. Must be provided if new VPC connector is being created. | object({…}) |
null |
Outputs
| name | description | sensitive |
|---|---|---|
| bucket | Bucket resource (only if auto-created). | |
| bucket_name | Bucket name. | |
| function | Cloud function resources. | |
| function_name | Cloud function name. | |
| id | Fully qualified function id. | |
| invoke_command | Command to invoke Cloud Function. | |
| service_account | Service account resource. | |
| service_account_email | Service account email. | |
| service_account_iam_email | Service account email. | |
| vpc_connector | VPC connector resource if created. |