---
title: Google Secret Manager Plugin
description: Using Google Cloud Secret Manager with Varlock
---

import { Steps, Icon } from '@astrojs/starlight/components';
import Badge from '@/components/Badge.astro';

<div class="page-badges">
  <Badge npmPackage="@varlock/google-secret-manager-plugin" />
</div>

Our [Google Cloud Secret Manager](https://cloud.google.com/secret-manager) plugin enables secure loading of secrets from GCP Secret Manager using declarative instructions within your `.env` files.

It supports authentication via [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/application-default-credentials), explicitly passing in a service account JSON key, or [OIDC Workload Identity Federation](/guides/oidc/).

**Key features:**
- ✅ Automatic secret naming using config item keys
- ✅ Application Default Credentials or Service Account authentication
- ✅ [OIDC Workload Identity Federation](/guides/oidc/) for platform-based authentication (Vercel, GitHub Actions, etc.)
- ✅ Versioned secret access
- ✅ Multiple plugin instances for different projects

## Installation and setup

In a JS/TS project, you may install the `@varlock/google-secret-manager-plugin` package as a normal dependency.
Otherwise you can just load it directly from your `.env.schema` file, as long as you add a version specifier.
See the [plugins guide](/guides/plugins/#installation) for more instructions on installing plugins.

```env-spec title=".env.schema"
# 1. Load the plugin
# @plugin(@varlock/google-secret-manager-plugin)
#
# 2. Initialize the plugin - see below for more details on options
# @initGsm(projectId=my-gcp-project)
```

:::tip[Project ID]
The `projectId` parameter is optional if your service account JSON includes a `project_id` field, or if you have set `GOOGLE_CLOUD_PROJECT` in your environment. However, it's recommended to set it explicitly for clarity. If you don't provide `projectId` and are using ADC, the plugin will attempt to detect it from your `gcloud` configuration or environment variables.
:::


### Using Application Default Credentials (ADC) - recommended

By default (when no `credentials` parameter is set), this plugin will use [Application Default Credentials (ADC)](https://cloud.google.com/docs/authentication/application-default-credentials) to authenticate with Google Cloud. This is the recommended way to authenticate for local dev, and within GCP.

Within GCP, you will need to set up a [service account](https://cloud.google.com/iam/docs/service-accounts) with the correct permissions, and attach it to the resources where your code will be running.

Outside of GCP, you may set up ADC credentials using the [`gcloud auth application-default login`](https://docs.cloud.google.com/sdk/gcloud/reference/auth/application-default/login) command, which will store credentials locally, and make them available for ADC.


### Using a service account key

In rare cases, it may be useful to pass in a service account key explicitly. This is useful for deployed environments other than GCP, or if you need to use a different service account than the one attached to your service.

<Steps>

1. **Create and download a JSON key** for your service account. This can be done via the [Cloud Console](https://console.cloud.google.com/iam-admin/serviceaccounts) or using the `gcloud` CLI. [docs](https://cloud.google.com/iam/docs/keys-create-delete)

    ```bash
    gcloud iam service-accounts keys create key.json \
      --iam-account=SERVICE_ACCOUNT_EMAIL
    ```

2. **Wire up the service account key in your config**. Add a config item of type `gcpServiceAccountJson` to hold the key value, and reference it when initializing the plugin.

    ```diff lang="env-spec" title=".env.schema" add="credentials=$GCP_SA_KEY"
    # @plugin(@varlock/google-secret-manager-plugin)
    # @initGsm(projectId=my-gcp-project, credentials=$GCP_SA_KEY)
    # ---
    +# @type=gcpServiceAccountJson @sensitive @internal
    +GCP_SA_KEY=
    ```

3. **Set your service account key in deployed environments**. Copy the JSON key content from the file you downloaded, and set it in deployed environments using your platform's env var management UI. Be sure to use the same name as you defined in your schema (e.g. `GCP_SA_KEY`).

</Steps>


:::caution[Keep service account keys secure]
Service account JSON keys are highly sensitive credentials. Store them securely and never commit them to version control. Consider using your platform's secret management system to store the key itself.
:::



### OIDC Workload Identity Federation

For deployments on platforms that issue OIDC tokens (Vercel, GitHub Actions, GitLab CI, Fly.io), you can use [OIDC workload identity federation](/guides/oidc/) to authenticate without a service account key. Varlock auto-detects the platform's OIDC token and exchanges it for GCP credentials via Workload Identity Federation.

**Provider-side setup:**

1. Create a Workload Identity Pool and Provider:
```bash
gcloud iam workload-identity-pools create varlock-pool \
  --location=global

gcloud iam workload-identity-pools providers create-oidc vercel \
  --location=global \
  --workload-identity-pool=varlock-pool \
  --issuer-uri=https://oidc.vercel.com \
  --attribute-mapping="google.subject=assertion.sub"
```

2. Grant the pool access to a service account:
```bash
gcloud iam service-accounts add-iam-policy-binding \
  my-sa@my-project.iam.gserviceaccount.com \
  --role=roles/iam.workloadIdentityUser \
  --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/varlock-pool/*"
```

3. Grant the service account Secret Manager access.

**Varlock config:**
```env-spec title=".env.schema"
# @plugin(@varlock/google-secret-manager-plugin)
# @initGsm(
#   projectId=my-project,
#   workloadIdentityProvider="//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/varlock-pool/providers/vercel",
#   serviceAccountEmail="my-sa@my-project.iam.gserviceaccount.com"
# )
```

The `workloadIdentityProvider` is the full resource name of the provider. For platforms not auto-detected, pass an explicit token via `oidcToken`.

### GCP Prerequisites

If you are already using GCP Secret Manager, you likely have completed these steps already, but if not, you will need to do so before using this plugin:

<Steps>
1. **Enable the Secret Manager API** (if not already done)

    Go to the Google Cloud Console and enable the Secret Manager API for your project.

2. **Create a new service account** in your GCP project. This can be done via the [Google Cloud Console](https://console.cloud.google.com/iam-admin/serviceaccounts) or using the `gcloud` CLI. [docs](https://cloud.google.com/iam/docs/service-accounts-create)

     This service account, whether accessed using ADC or a service account key, will now serve as your _secret-zero_, granting access to the rest of your sensitive data stored in Secret Manager. For local-only workflows, you can use [device-local encryption](/guides/local-encryption/) to secure this value.

3. **Grant the service account permissions** to access secrets. The service account needs the `Secret Manager Secret Accessor` role (`roles/secretmanager.secretAccessor`) on the secrets or project level. [docs](https://cloud.google.com/secret-manager/docs/access-control)

    ```bash
    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
      --role="roles/secretmanager.secretAccessor"
    ```

4. **Attach the service account to GCP resources**
    
    You must [attach this service account](https://docs.cloud.google.com/iam/docs/attach-service-accounts#attaching-to-resources) to any resources where your code will be running.

</Steps>


## Pulling data from Secret Manager

Once the plugin is installed and initialized, you can start adding config items that load values from Google Secret Manager using the new `gsm()` resolver function.

### Basic Secret Fetching

```env-spec
# @plugin(@varlock/google-secret-manager-plugin)
# @initGsm(projectId=my-project)
# ---

# Secret name defaults to the config item key
SIMPLEST_VAR=gsm()

# Or you can explicitly specify the secret name
RENAMED_VAR=gsm("database-password")

# You can fetch a specific version
API_KEY_LATEST=gsm("api-key@latest")
API_KEY_V5=gsm("api-key@5")

# Use complete resource paths for maximum control:
FULL_PATH_VAR=gsm("projects/my-project/secrets/db-url/versions/3")
```

:::tip[Auto-naming]
When called without arguments, `gsm()` automatically uses the config item key as the secret name in Google Secret Manager (e.g., `DATABASE_URL=gsm()` will fetch a secret named `DATABASE_URL`). This provides a clean, convention-over-configuration approach when your secret names match your config keys.
:::

:::tip[Secret versions]
If you don't specify a version in the short format, `latest` will be used automatically. For the full path format, you must include the version (use `latest` if you want the most recent version).
:::

### Multiple plugin instances

If you need to connect using different project ids, or different credentials, particularly at the same time, you can create multiple named instances, and then use that id when fetching secrets.

```env-spec
# @plugin(@varlock/google-secret-manager-plugin)
# @initGsm(id=prod, projectId=prod-project, credentials=$PROD_KEY)
# @initGsm(id=dev, projectId=dev-project, credentials=$DEV_KEY)
# ---

PROD_DATABASE=gsm(prod, "database-url")
DEV_DATABASE=gsm(dev, "database-url")
```

### Dynamic project ID

The `projectId` parameter supports dynamic resolution, allowing you to specify project IDs from environment variables or other resolver functions. This is useful when you need to use different project IDs based on your deployment environment.

```env-spec
# @plugin(@varlock/google-secret-manager-plugin)
# @initGsm(projectId=$GCP_PROJECT_ID)
# ---
# Use environment variable for project ID
GCP_PROJECT_ID=
API_KEY=gsm("api-key")
```

You can also use resolver functions to construct the project ID dynamically:

```env-spec
# @plugin(@varlock/google-secret-manager-plugin)
# @initGsm(projectId=concat($APP_ENV, "-project"))
# ---
APP_ENV=
API_KEY=gsm("api-key")
```

----


## Reference

### Root decorators
<div class="reference-docs">
<div>
#### `@initGsm()`

Initializes an instance of the Google Secret Manager plugin - setting up options and authentication. Can be called multiple times to set up different instances.

**Key/value args:**
- `id` (optional): identifier for this instance, used when multiple instances are needed
- `projectId` (optional): GCP project ID. Required for short secret reference format, or if credentials don't include a `project_id` field
- `credentials` (optional): service account JSON key. Should be a reference to a config item of type `gcpServiceAccountJson`. If omitted, Application Default Credentials will be used
- `workloadIdentityProvider` (optional): full resource name of the Workload Identity Provider for OIDC authentication
- `serviceAccountEmail` (optional): service account email to impersonate via Workload Identity Federation
- `oidcToken` (optional): explicit OIDC JWT token (auto-detected from platform if omitted)
- `cacheTtl` (optional): cache resolved values for the specified duration (e.g. `"5m"`, `"1h"`, `"1d"`, or `forever` to cache until manually cleared). For cache mode behavior and CLI cache controls, see the [Caching guide](/guides/caching/).

```env-spec "@initGsm"
# @initGsm(id=prod, projectId=my-gcp-project, credentials=$GCP_SA_KEY)
# ---
# @type=gcpServiceAccountJson @sensitive @internal
GCP_SA_KEY=
```
</div>
</div>


### Data types
<div class="reference-docs">
<div>
#### `gcpServiceAccountJson`

Represents a [Google Cloud service account JSON key](https://cloud.google.com/iam/docs/service-accounts). Validation ensures the JSON is valid and contains required fields (`type`, `project_id`, `private_key`, `client_email`). The type itself is marked as `@sensitive`, so adding an explicit `@sensitive` decorator is optional.

```env-spec "gcpServiceAccountJson"
# @type=gcpServiceAccountJson
GCP_SA_KEY=
```

:::tip[Does your app use these credentials too?]
The examples here mark the credential [`@internal`](/reference/item-decorators/#internal) so it's used by varlock to fetch your secrets but **not** injected into your app. This is the recommended posture when varlock is the only consumer. Unlike the dedicated secrets-manager tokens, GCP service account keys are **not** `@internal` by default (they're commonly read directly by the Google Cloud SDK), so if you need the credential at runtime for other purposes, opt out with `@internal=false` to keep it injected:

```env-spec
# @type=gcpServiceAccountJson @internal=false
GCP_SA_KEY=
```
:::
</div>
</div>


### Resolver functions
<div class="reference-docs">
<div>
#### `gsm()`

Fetches a secret value from Google Secret Manager

**Signatures:**
- `gsm()` - Fetch using config item key as secret name from default instance (e.g., `DATABASE_URL=gsm()` will fetch a secret named `DATABASE_URL`)
- `gsm(secretRef)` - Fetch specific secret from default instance
- `gsm(instanceId, secretRef)` - Fetch from named instance

**Array args:**
- `instanceId` (optional): instance identifier to use when multiple plugin instances are initialized
- `secretReference` (optional): secret reference, either in short format (`"secret-name"` or `"secret-name@version"`) or full path format (`"projects/PROJECT_ID/secrets/SECRET_NAME/versions/VERSION"`). If omitted, uses the config item key as the secret name

**Secret Reference Formats:**
- `"secret-name"` - Uses latest version from configured project
- `"secret-name@5"` - Specific version from configured project
- `"projects/PROJECT/secrets/NAME/versions/VERSION"` - Full resource path

**Returns:** The secret value as a string


```env-spec /gsm\\(.*\\)/
# Secret name defaults to the config item key
SIMPLEST_VAR=gsm()

# Or you can explicitly specify the secret name
RENAMED_VAR=gsm("database-password")

# You can fetch a specific version
API_KEY_V5=gsm("api-key@5")

# Use complete resource paths for maximum control:
FULL_PATH_VAR=gsm("projects/my-project/secrets/db-url/versions/3")

# Example using a plugin instance id
PROD_SECRET=gsm(prod, "prod-secret")
```
</div>
</div>



