Secure and store sensitive data using the Secret Manager connector

Secret Manager is a secure and convenient storage system for API keys, passwords, certificates, and other sensitive data. Secret Manager provides a central place and single source of truth to manage, access, and audit secrets across Google Cloud.

You can use Workflows' connector for the Secret Manager API to access Secret Manager within a workflow. This simplifies the integration for you, because the connector handles the formatting of requests, and provides methods and arguments so that you don't need to know the details of the Secret Manager API. The connector also has built-in behavior for handling retries and long-running operations. To learn more about using Workflows connectors, see Understand connectors.

Grant the Workflows service account access to Secret Manager

Secret Manager uses Identity and Access Management (IAM) for access control. To create, manage, list, or access a secret, the appropriate IAM permissions must be granted at the project level and at the individual resource level. For more information, see Access control with IAM.

Workflows uses service accounts to give workflows access to Google Cloud resources. To access a secret version, you must grant the Secret Manager Secret Accessor role (roles/secretmanager.secretAccessor) on the secret, project, folder, or organization to the service account. Learn more about deploying a workflow with a user-managed service account.

Enable the APIs

Before using the Workflows' connector for the Secret Manager API, ensure that you enable the Secret Manager and Workflows APIs.

Console

Enable the APIs

gcloud

  gcloud services enable secretmanager.googleapis.com workflows.googleapis.com

Invoke a connector call

Similar to invoking an HTTP endpoint, a connector call requires call and args fields. For more information, see Invoke a connector call.

In addition to using a call step, you can call the helper methods in an expression like this:

${googleapis.secretmanager.v1.projects.secrets.versions.accessString(secret_id, version, project_id)}

For example, you can use the helper method accessString to retrieve the secret data as a string. This is simpler than using the access API as the secret data is automatically decoded to a string format.

You can also use the helper method addVersionString to add a new secret value to an existing secret. This is simpler than using the addVersion API as the secret data is automatically encoded to a base-64 string, which is required by addVersion.

Retrieve a secret using the Secret Manager connector

The following workflow demonstrates how to use the Secret Manager connector to retrieve a secret.

YAML

# This workflow demonstrates how to use the Cloud Secret Manager connector to.
# retrieve a secret.
# Expected successful output: the secret data.

- init:
    assign:
      - project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
      - secret_id: "test-secret"  # Make sure you have this secret and it has a version of 1.
      - version: "1"
# We provide a helper method to add a secret data to an existing secret without base-64 encoding.
- add_version_string:
    call: googleapis.secretmanager.v1.projects.secrets.addVersionString
    args:
      secret_id: ${secret_id}
      project_id: ${project_id}
      data: "a new secret"
# We provide a helper method to access the secret in string format without base-64 decoding.
# To compare the usage between accessRaw() and access(), we list two demo steps to retrieve
# the same secret below.
#
# accessString assumes the secret data is a valid UTF-8 string and if it detects non-UTF-8
# bytes, an error will be raised.
- access_string_secret:
    call: googleapis.secretmanager.v1.projects.secrets.versions.accessString
    args:
      secret_id: ${secret_id}
      version: ${version}  # If not set, "latest" will be used.
      project_id: ${project_id}
    result: str_secret
- access_secret:
    call: googleapis.secretmanager.v1.projects.secrets.versions.access
    args:
      name: ${"projects/" + project_id + "/secrets/" + secret_id + "/versions/" + version}
    result: base64_encoded_secret
# Secret can also be retrieved by using positional arguments in an expression.
- expression:
    assign:
      - secret_str_from_exp: ${googleapis.secretmanager.v1.projects.secrets.versions.accessString(secret_id, version, project_id)}
- the_end:
    return:
      - ${str_secret}
      - ${secret_str_from_exp}
      - ${text.decode(base64.decode(base64_encoded_secret.payload.data))}

JSON

[
  {
    "init": {
      "assign": [
        {
          "project_id": "${sys.get_env(\"GOOGLE_CLOUD_PROJECT_ID\")}"
        },
        {
          "secret_id": "test-secret"
        },
        {
          "version": "1"
        }
      ]
    }
  },
  {
    "add_version_string": {
      "call": "googleapis.secretmanager.v1.projects.secrets.addVersionString",
      "args": {
        "secret_id": "${secret_id}",
        "project_id": "${project_id}",
        "data": "a new secret"
      }
    }
  },
  {
    "access_string_secret": {
      "call": "googleapis.secretmanager.v1.projects.secrets.versions.accessString",
      "args": {
        "secret_id": "${secret_id}",
        "version": "${version}",
        "project_id": "${project_id}"
      },
      "result": "str_secret"
    }
  },
  {
    "access_secret": {
      "call": "googleapis.secretmanager.v1.projects.secrets.versions.access",
      "args": {
        "name": "${\"projects/\" + project_id + \"/secrets/\" + secret_id + \"/versions/\" + version}"
      },
      "result": "base64_encoded_secret"
    }
  },
  {
    "expression": {
      "assign": [
        {
          "secret_str_from_exp": "${googleapis.secretmanager.v1.projects.secrets.versions.accessString(secret_id, version, project_id)}"
        }
      ]
    }
  },
  {
    "the_end": {
      "return": [
        "${str_secret}",
        "${secret_str_from_exp}",
        "${text.decode(base64.decode(base64_encoded_secret.payload.data))}"
      ]
    }
  }
]

What's next