Cloud Functions can pull secrets directly from Secret Manager without needing to write any extra code to fetch them.

Let’s see this in action. Imagine you have a Cloud Function that needs to connect to a database. Instead of hardcoding credentials or fetching them with a SecretManagerServiceClient in your function’s code, you can configure the function to inject them as environment variables.

Here’s a gcloud command to deploy a Python Cloud Function that uses a secret:

gcloud functions deploy my-function \
  --runtime python311 \
  --entry-point main \
  --trigger-http \
  --set-secrets=DATABASE_PASSWORD=projects/my-project-id/secrets/my-db-secret:latest \
  --region us-central1

When this function runs, the DATABASE_PASSWORD environment variable will be automatically populated with the latest version of my-db-secret from Secret Manager. Your Python code then simply accesses os.environ.get('DATABASE_PASSWORD').

The magic happens in the Cloud Functions runtime. When you specify a secret using --set-secrets, the Cloud Functions environment creates a secure, temporary file on the function’s execution environment. This file contains the secret’s value. The environment variable you specified (DATABASE_PASSWORD in this case) is then set to the path of this temporary file, not the secret’s value directly. Your function code reads the file path from the environment variable, opens the file, and then reads the secret value. This indirection is a security feature, preventing the secret value itself from appearing directly in environment variable listings, which could be accidentally logged or exposed.

The primary problem this solves is managing sensitive credentials and configuration across your serverless applications. Without it, you’d be looking at:

  • Hardcoded secrets: A major security risk.
  • Environment variables managed manually: Prone to errors, especially across different deployment environments (dev, staging, prod).
  • Fetching secrets within the function: Adds boilerplate code, increases function initialization time, and requires careful error handling for secret retrieval.

By using --set-secrets, you declaratively tell Cloud Functions which secrets to make available. The runtime handles the secure injection. You specify the environment variable name and the full resource name of the secret in Secret Manager, including the version (e.g., :latest for the newest, or a specific version number like :1).

The runtime and entry-point flags are standard for deploying Cloud Functions. The --trigger-http flag means this function is invoked via an HTTP request. The crucial part is --set-secrets. The format ENV_VAR_NAME=SECRET_RESOURCE_NAME:VERSION is key. SECRET_RESOURCE_NAME follows the pattern projects/PROJECT_ID/secrets/SECRET_NAME.

When the function is invoked, the Cloud Functions infrastructure ensures that the specified secret version is fetched from Secret Manager and made accessible. If the secret version doesn’t exist or the function’s service account lacks permission to access it, the function invocation will fail. The service account associated with your Cloud Function needs the Secret Manager Secret Accessor role (roles/secretmanager.secretAccessor) on the specific secret or the project.

The benefit is a cleaner, more secure deployment. Your function code remains focused on its business logic, not on the mechanics of secret management. The operational burden of rotating secrets or managing access is shifted to Secret Manager, which is designed for exactly that. This also simplifies local development; you can often simulate secret injection or use placeholder values, but the production deployment relies on the secure, managed secret.

You can also set multiple secrets for a single function deployment:

gcloud functions deploy my-function \
  --runtime python311 \
  --entry-point main \
  --trigger-http \
  --set-secrets=API_KEY=projects/my-project-id/secrets/my-api-key:latest,DATABASE_PASSWORD=projects/my-project-id/secrets/my-db-secret:latest \
  --region us-central1

This allows you to inject all necessary sensitive configuration into your function in a single, declarative step.

The actual value of the secret is written to a file in /etc/secrets/ within the function’s execution environment. The environment variable you specify (DATABASE_PASSWORD, for example) is then set to the path of this file. So, when your code reads os.environ.get('DATABASE_PASSWORD'), it gets /etc/secrets/DATABASE_PASSWORD. Your code then needs to read the content of this file to get the actual secret.

This mechanism is designed to prevent the secret’s value from being directly exposed in the environment, which could be inadvertently logged by tools that inspect environment variables. It’s a subtle but important security detail: the environment variable holds a path, and you must read from that path to obtain the secret.

The next hurdle you’ll likely encounter is managing different secret versions across different environments, such as development, staging, and production.

Want structured learning?

Take the full Cloud-functions course →