Create GCP Secret Manager Secret Versions

The gcp:secretmanager/secretVersion:SecretVersion resource, part of the Pulumi Google Cloud provider, creates a version of a Secret Manager secret containing the actual secret data. This guide focuses on three capabilities: storing plaintext and base64-encoded secrets, write-only values for state protection, and deletion policies for version lifecycle.

Secret versions belong to Secret Manager Secret resources and must reference them by ID. The examples are intentionally small. Combine them with your own Secret resources, IAM policies, and application integration.

Store a secret value with automatic replication

Most deployments store plaintext values like API keys or passwords with automatic replication for availability.

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 secretData property contains the actual secret value (up to 64KiB). The secret property references the parent Secret resource by ID. Automatic replication distributes the secret across Google Cloud regions without manual configuration.

Store secrets without persisting to state

For highly sensitive data, you can prevent secret values from appearing in Pulumi state files.

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 marks the secret as write-only, preventing it from being stored in state. The secretDataWoVersion property triggers updates when you change the secret value. This approach protects against state file exposure while maintaining version control.

Preserve secret versions when removing from stack

Some workflows require secret versions to remain accessible after removing them from infrastructure code.

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. Setting it to “ABANDON” leaves the secret version in Secret Manager rather than deleting it. This supports gradual migrations and compliance requirements where historical secret access must be preserved.

Store binary files as base64-encoded secrets

Certificates, private keys, and other binary files require base64 encoding before storage.

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 and encodes the local file. This enables storing binary data like TLS certificates or encrypted key files.

Beyond these examples

These snippets focus on specific secret version features: plaintext and base64-encoded secret storage, write-only values for state protection, and deletion policies for version lifecycle. They’re intentionally minimal rather than full secret management solutions.

The examples create Secret Manager Secret resources inline and may reference local files for base64 encoding. They focus on configuring the secret version rather than provisioning IAM policies or application integrations.

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

  • Secret rotation and versioning strategies
  • Enabling and disabling versions (enabled property)
  • IAM permissions for secret access
  • Integration with application runtimes (Cloud Run, GKE, Compute Engine)

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 stores the secret value in Terraform state, while secretDataWo is write-only and never appears in state. You can only use one of these fields, not both.
Why isn't my secretDataWo value showing in state?
Write-only fields like secretDataWo are never stored in state for security reasons. This is intentional behavior.
How do I update a secret that uses secretDataWo?
Increment the secretDataWoVersion field to trigger an update. This immutable integer tells Pulumi to apply the new secretDataWo value.
What's the size limit for secret data?
Secret data must be no larger than 64KiB, whether using secretData or secretDataWo.
Updates & Lifecycle
Why does updating my secret cause downtime?
Updating secretData triggers resource replacement (delete then create), which can cause outages. This is because secretData is a force-new field.
How can I update secrets without downtime?
Use createBeforeDestroy in the lifecycle block to create the new secret version before destroying the old one.
Deletion & Encoding
What deletion policies are available for secret versions?

You have three options:

  1. DELETE (default) - Destroys the version
  2. DISABLE - Disables the version but keeps it
  3. ABANDON - Leaves the version in place when removing from Pulumi
How do I store base64-encoded secrets?
Set isSecretDataBase64 to true and provide base64-encoded data in secretData or secretDataWo. This is useful for binary files like certificates.
Can I change the secret or project after creation?
No, both secret and project are immutable. Changing either requires creating a new secret version.

Using a different cloud?

Explore security guides for other cloud providers: