Create GCP Secret Manager Secret Versions

The gcp:secretmanager/secretVersion:SecretVersion resource, part of the Pulumi GCP provider, creates a version of a Secret Manager secret, storing the actual secret data. This guide focuses on three capabilities: state-tracked vs write-only storage, deletion policies, and base64 encoding for binary files.

Secret versions belong to Secret Manager Secret resources, which define replication strategy and IAM policies. The examples are intentionally small. Combine them with your own Secret resources and access controls.

Store a secret value with state tracking

Most workflows store plaintext values that applications retrieve at runtime, with the secret data tracked in Pulumi state for drift detection.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const secret_basic = new gcp.secretmanager.Secret("secret-basic", {
    secretId: "secret-version",
    labels: {
        label: "my-label",
    },
    replication: {
        auto: {},
    },
});
const secret_version_basic = new gcp.secretmanager.SecretVersion("secret-version-basic", {
    secret: secret_basic.id,
    secretData: "secret-data",
});
import pulumi
import pulumi_gcp as gcp

secret_basic = gcp.secretmanager.Secret("secret-basic",
    secret_id="secret-version",
    labels={
        "label": "my-label",
    },
    replication={
        "auto": {},
    })
secret_version_basic = gcp.secretmanager.SecretVersion("secret-version-basic",
    secret=secret_basic.id,
    secret_data="secret-data")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		secret_basic, err := secretmanager.NewSecret(ctx, "secret-basic", &secretmanager.SecretArgs{
			SecretId: pulumi.String("secret-version"),
			Labels: pulumi.StringMap{
				"label": pulumi.String("my-label"),
			},
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretVersion(ctx, "secret-version-basic", &secretmanager.SecretVersionArgs{
			Secret:     secret_basic.ID(),
			SecretData: pulumi.String("secret-data"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var secret_basic = new Gcp.SecretManager.Secret("secret-basic", new()
    {
        SecretId = "secret-version",
        Labels = 
        {
            { "label", "my-label" },
        },
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var secret_version_basic = new Gcp.SecretManager.SecretVersion("secret-version-basic", new()
    {
        Secret = secret_basic.Id,
        SecretData = "secret-data",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationAutoArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var secret_basic = new Secret("secret-basic", SecretArgs.builder()
            .secretId("secret-version")
            .labels(Map.of("label", "my-label"))
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var secret_version_basic = new SecretVersion("secret-version-basic", SecretVersionArgs.builder()
            .secret(secret_basic.id())
            .secretData("secret-data")
            .build());

    }
}
resources:
  secret-basic:
    type: gcp:secretmanager:Secret
    properties:
      secretId: secret-version
      labels:
        label: my-label
      replication:
        auto: {}
  secret-version-basic:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["secret-basic"].id}
      secretData: secret-data

The secret property references the parent Secret resource. The secretData property stores the actual secret value. Because this uses secretData (not secretDataWo), Pulumi tracks the value in state and can detect external changes.

Store a secret without persisting to state

Some teams keep secret values out of Pulumi state entirely using write-only properties that are sent to GCP but never read back.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const secret_basic_write_only = new gcp.secretmanager.Secret("secret-basic-write-only", {
    secretId: "secret-version-write-only",
    labels: {
        label: "my-label",
    },
    replication: {
        auto: {},
    },
});
const secret_version_basic_write_only = new gcp.secretmanager.SecretVersion("secret-version-basic-write-only", {
    secret: secret_basic_write_only.id,
    secretDataWoVersion: 1,
    secretDataWo: "secret-data-write-only",
});
import pulumi
import pulumi_gcp as gcp

secret_basic_write_only = gcp.secretmanager.Secret("secret-basic-write-only",
    secret_id="secret-version-write-only",
    labels={
        "label": "my-label",
    },
    replication={
        "auto": {},
    })
secret_version_basic_write_only = gcp.secretmanager.SecretVersion("secret-version-basic-write-only",
    secret=secret_basic_write_only.id,
    secret_data_wo_version=1,
    secret_data_wo="secret-data-write-only")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		secret_basic_write_only, err := secretmanager.NewSecret(ctx, "secret-basic-write-only", &secretmanager.SecretArgs{
			SecretId: pulumi.String("secret-version-write-only"),
			Labels: pulumi.StringMap{
				"label": pulumi.String("my-label"),
			},
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretVersion(ctx, "secret-version-basic-write-only", &secretmanager.SecretVersionArgs{
			Secret:              secret_basic_write_only.ID(),
			SecretDataWoVersion: pulumi.Int(1),
			SecretDataWo:        pulumi.String("secret-data-write-only"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var secret_basic_write_only = new Gcp.SecretManager.Secret("secret-basic-write-only", new()
    {
        SecretId = "secret-version-write-only",
        Labels = 
        {
            { "label", "my-label" },
        },
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var secret_version_basic_write_only = new Gcp.SecretManager.SecretVersion("secret-version-basic-write-only", new()
    {
        Secret = secret_basic_write_only.Id,
        SecretDataWoVersion = 1,
        SecretDataWo = "secret-data-write-only",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationAutoArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var secret_basic_write_only = new Secret("secret-basic-write-only", SecretArgs.builder()
            .secretId("secret-version-write-only")
            .labels(Map.of("label", "my-label"))
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var secret_version_basic_write_only = new SecretVersion("secret-version-basic-write-only", SecretVersionArgs.builder()
            .secret(secret_basic_write_only.id())
            .secretDataWoVersion(1)
            .secretDataWo("secret-data-write-only")
            .build());

    }
}
resources:
  secret-basic-write-only:
    type: gcp:secretmanager:Secret
    properties:
      secretId: secret-version-write-only
      labels:
        label: my-label
      replication:
        auto: {}
  secret-version-basic-write-only:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["secret-basic-write-only"].id}
      secretDataWoVersion: 1
      secretDataWo: secret-data-write-only

The secretDataWo property stores the secret without persisting it to state. The secretDataWoVersion property triggers updates when you change the secret value. This approach prevents secrets from appearing in state files or diffs, at the cost of losing drift detection.

Preserve secret versions when removing from stack

When decommissioning infrastructure, some secrets must remain accessible for audit or compliance even after Pulumi resources are destroyed.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const secret_basic = new gcp.secretmanager.Secret("secret-basic", {
    secretId: "secret-version",
    replication: {
        userManaged: {
            replicas: [{
                location: "us-central1",
            }],
        },
    },
});
const secret_version_deletion_policy = new gcp.secretmanager.SecretVersion("secret-version-deletion-policy", {
    secret: secret_basic.id,
    secretData: "secret-data",
    deletionPolicy: "ABANDON",
});
import pulumi
import pulumi_gcp as gcp

secret_basic = gcp.secretmanager.Secret("secret-basic",
    secret_id="secret-version",
    replication={
        "user_managed": {
            "replicas": [{
                "location": "us-central1",
            }],
        },
    })
secret_version_deletion_policy = gcp.secretmanager.SecretVersion("secret-version-deletion-policy",
    secret=secret_basic.id,
    secret_data="secret-data",
    deletion_policy="ABANDON")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		secret_basic, err := secretmanager.NewSecret(ctx, "secret-basic", &secretmanager.SecretArgs{
			SecretId: pulumi.String("secret-version"),
			Replication: &secretmanager.SecretReplicationArgs{
				UserManaged: &secretmanager.SecretReplicationUserManagedArgs{
					Replicas: secretmanager.SecretReplicationUserManagedReplicaArray{
						&secretmanager.SecretReplicationUserManagedReplicaArgs{
							Location: pulumi.String("us-central1"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretVersion(ctx, "secret-version-deletion-policy", &secretmanager.SecretVersionArgs{
			Secret:         secret_basic.ID(),
			SecretData:     pulumi.String("secret-data"),
			DeletionPolicy: pulumi.String("ABANDON"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var secret_basic = new Gcp.SecretManager.Secret("secret-basic", new()
    {
        SecretId = "secret-version",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            UserManaged = new Gcp.SecretManager.Inputs.SecretReplicationUserManagedArgs
            {
                Replicas = new[]
                {
                    new Gcp.SecretManager.Inputs.SecretReplicationUserManagedReplicaArgs
                    {
                        Location = "us-central1",
                    },
                },
            },
        },
    });

    var secret_version_deletion_policy = new Gcp.SecretManager.SecretVersion("secret-version-deletion-policy", new()
    {
        Secret = secret_basic.Id,
        SecretData = "secret-data",
        DeletionPolicy = "ABANDON",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationUserManagedArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var secret_basic = new Secret("secret-basic", SecretArgs.builder()
            .secretId("secret-version")
            .replication(SecretReplicationArgs.builder()
                .userManaged(SecretReplicationUserManagedArgs.builder()
                    .replicas(SecretReplicationUserManagedReplicaArgs.builder()
                        .location("us-central1")
                        .build())
                    .build())
                .build())
            .build());

        var secret_version_deletion_policy = new SecretVersion("secret-version-deletion-policy", SecretVersionArgs.builder()
            .secret(secret_basic.id())
            .secretData("secret-data")
            .deletionPolicy("ABANDON")
            .build());

    }
}
resources:
  secret-basic:
    type: gcp:secretmanager:Secret
    properties:
      secretId: secret-version
      replication:
        userManaged:
          replicas:
            - location: us-central1
  secret-version-deletion-policy:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["secret-basic"].id}
      secretData: secret-data
      deletionPolicy: ABANDON

The deletionPolicy property controls what happens when you remove the resource from your stack. Setting it to “ABANDON” leaves the secret version in GCP rather than deleting it. The default “DELETE” policy removes the version entirely.

Store binary files as base64-encoded secrets

Applications that distribute certificates, keys, or other binary files can encode them as base64 and store them in Secret Manager.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const secret_basic = new gcp.secretmanager.Secret("secret-basic", {
    secretId: "secret-version",
    replication: {
        userManaged: {
            replicas: [{
                location: "us-central1",
            }],
        },
    },
});
const secret_version_base64 = new gcp.secretmanager.SecretVersion("secret-version-base64", {
    secret: secret_basic.id,
    isSecretDataBase64: true,
    secretData: std.filebase64({
        input: "secret-data.pfx",
    }).then(invoke => invoke.result),
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

secret_basic = gcp.secretmanager.Secret("secret-basic",
    secret_id="secret-version",
    replication={
        "user_managed": {
            "replicas": [{
                "location": "us-central1",
            }],
        },
    })
secret_version_base64 = gcp.secretmanager.SecretVersion("secret-version-base64",
    secret=secret_basic.id,
    is_secret_data_base64=True,
    secret_data=std.filebase64(input="secret-data.pfx").result)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		secret_basic, err := secretmanager.NewSecret(ctx, "secret-basic", &secretmanager.SecretArgs{
			SecretId: pulumi.String("secret-version"),
			Replication: &secretmanager.SecretReplicationArgs{
				UserManaged: &secretmanager.SecretReplicationUserManagedArgs{
					Replicas: secretmanager.SecretReplicationUserManagedReplicaArray{
						&secretmanager.SecretReplicationUserManagedReplicaArgs{
							Location: pulumi.String("us-central1"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		invokeFilebase64, err := std.Filebase64(ctx, &std.Filebase64Args{
			Input: "secret-data.pfx",
		}, nil)
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretVersion(ctx, "secret-version-base64", &secretmanager.SecretVersionArgs{
			Secret:             secret_basic.ID(),
			IsSecretDataBase64: pulumi.Bool(true),
			SecretData:         pulumi.String(invokeFilebase64.Result),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var secret_basic = new Gcp.SecretManager.Secret("secret-basic", new()
    {
        SecretId = "secret-version",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            UserManaged = new Gcp.SecretManager.Inputs.SecretReplicationUserManagedArgs
            {
                Replicas = new[]
                {
                    new Gcp.SecretManager.Inputs.SecretReplicationUserManagedReplicaArgs
                    {
                        Location = "us-central1",
                    },
                },
            },
        },
    });

    var secret_version_base64 = new Gcp.SecretManager.SecretVersion("secret-version-base64", new()
    {
        Secret = secret_basic.Id,
        IsSecretDataBase64 = true,
        SecretData = Std.Filebase64.Invoke(new()
        {
            Input = "secret-data.pfx",
        }).Apply(invoke => invoke.Result),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationUserManagedArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.Filebase64Args;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var secret_basic = new Secret("secret-basic", SecretArgs.builder()
            .secretId("secret-version")
            .replication(SecretReplicationArgs.builder()
                .userManaged(SecretReplicationUserManagedArgs.builder()
                    .replicas(SecretReplicationUserManagedReplicaArgs.builder()
                        .location("us-central1")
                        .build())
                    .build())
                .build())
            .build());

        var secret_version_base64 = new SecretVersion("secret-version-base64", SecretVersionArgs.builder()
            .secret(secret_basic.id())
            .isSecretDataBase64(true)
            .secretData(StdFunctions.filebase64(Filebase64Args.builder()
                .input("secret-data.pfx")
                .build()).result())
            .build());

    }
}
resources:
  secret-basic:
    type: gcp:secretmanager:Secret
    properties:
      secretId: secret-version
      replication:
        userManaged:
          replicas:
            - location: us-central1
  secret-version-base64:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${["secret-basic"].id}
      isSecretDataBase64: true
      secretData:
        fn::invoke:
          function: std:filebase64
          arguments:
            input: secret-data.pfx
          return: result

The isSecretDataBase64 property tells Secret Manager to treat the input as base64-encoded. The filebase64 function reads a local file and encodes it. This pattern works for TLS certificates, private keys, or any binary credential format.

Beyond these examples

These snippets focus on specific secret version features: state-tracked vs write-only secret storage, deletion policies for lifecycle management, and base64 encoding for binary data. They’re intentionally minimal rather than full secret management solutions.

The examples reference pre-existing infrastructure such as Secret Manager Secret resources (parent containers). They focus on storing secret data rather than provisioning the surrounding secret configuration.

To keep things focused, common secret version patterns are omitted, including:

  • Secret rotation and versioning strategies
  • Enabling and disabling versions (enabled property)
  • Replication configuration (handled by parent Secret)
  • IAM permissions for secret access

These omissions are intentional: the goal is to illustrate how each secret version feature is wired, not provide drop-in secret management modules. See the Secret Manager SecretVersion resource reference for all available configuration options.

Let's create GCP Secret Manager Secret Versions

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Secret Data Management
What's the difference between secretData and secretDataWo?
secretData is stored in Pulumi state and can be read back, while secretDataWo is write-only and never stored in state. Use secretDataWo when you want to prevent sensitive data from appearing in state files or read operations.
Why isn't my secret data showing up in state?
If you’re using secretDataWo, it’s write-only and won’t be stored in state or returned by read operations. This is intentional for security. Use secretData if you need the value tracked in state.
How do I update a write-only secret?
Increment the secretDataWoVersion integer each time you change secretDataWo. This triggers Pulumi to recognize the update since the write-only field itself isn’t tracked in state.
Can I use both secretData and secretDataWo?
No, you can only set one or the other, not both. Choose secretData for state-tracked secrets or secretDataWo for write-only secrets.
Data Encoding & Limits
How do I store base64-encoded binary data?
Set isSecretDataBase64 to true and provide base64-encoded data. You can use functions like filebase64 to encode binary files (e.g., .pfx certificates).
What's the maximum size for secret data?
Secret data must be no larger than 64KiB, whether using secretData or secretDataWo.
Deletion & Lifecycle
What are my options for deleting a secret version?
You have three deletion policies: DELETE (default, permanently deletes), DISABLE (disables but keeps the version), or ABANDON (removes from Pulumi management but leaves the version in GCP).
What properties can't be changed after creation?
The following properties are immutable: project, secret, isSecretDataBase64, secretData, and secretDataWoVersion. Changing these requires replacing the resource.

Using a different cloud?

Explore security guides for other cloud providers: