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 FREEFrequently Asked Questions
Secret Data Management
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.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.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.secretData for state-tracked secrets or secretDataWo for write-only secrets.Data Encoding & Limits
isSecretDataBase64 to true and provide base64-encoded data. You can use functions like filebase64 to encode binary files (e.g., .pfx certificates).secretData or secretDataWo.Deletion & Lifecycle
DELETE (default, permanently deletes), DISABLE (disables but keeps the version), or ABANDON (removes from Pulumi management but leaves the version in GCP).project, secret, isSecretDataBase64, secretData, and secretDataWoVersion. Changing these requires replacing the resource.