The gcp:secretmanager/secret:Secret resource, part of the Pulumi GCP provider, defines a Secret Manager secret container: its replication strategy, metadata, and encryption configuration. This guide focuses on four capabilities: regional replication control, metadata annotations, version lifecycle management, and customer-managed encryption.
Secrets are containers for versioned secret data. The secret resource itself doesn’t store values; those live in SecretVersion resources. Secrets with customer-managed encryption require Cloud KMS keys and IAM bindings. The examples are intentionally small. Combine them with your own version management and access policies.
Replicate secrets across specific regions
Applications spanning multiple regions need secrets available in each location for low-latency access.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const secret_basic = new gcp.secretmanager.Secret("secret-basic", {
secretId: "secret",
labels: {
label: "my-label",
},
replication: {
userManaged: {
replicas: [
{
location: "us-central1",
},
{
location: "us-east1",
},
],
},
},
deletionProtection: false,
});
import pulumi
import pulumi_gcp as gcp
secret_basic = gcp.secretmanager.Secret("secret-basic",
secret_id="secret",
labels={
"label": "my-label",
},
replication={
"user_managed": {
"replicas": [
{
"location": "us-central1",
},
{
"location": "us-east1",
},
],
},
},
deletion_protection=False)
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 {
_, err := secretmanager.NewSecret(ctx, "secret-basic", &secretmanager.SecretArgs{
SecretId: pulumi.String("secret"),
Labels: pulumi.StringMap{
"label": pulumi.String("my-label"),
},
Replication: &secretmanager.SecretReplicationArgs{
UserManaged: &secretmanager.SecretReplicationUserManagedArgs{
Replicas: secretmanager.SecretReplicationUserManagedReplicaArray{
&secretmanager.SecretReplicationUserManagedReplicaArgs{
Location: pulumi.String("us-central1"),
},
&secretmanager.SecretReplicationUserManagedReplicaArgs{
Location: pulumi.String("us-east1"),
},
},
},
},
DeletionProtection: pulumi.Bool(false),
})
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",
Labels =
{
{ "label", "my-label" },
},
Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
{
UserManaged = new Gcp.SecretManager.Inputs.SecretReplicationUserManagedArgs
{
Replicas = new[]
{
new Gcp.SecretManager.Inputs.SecretReplicationUserManagedReplicaArgs
{
Location = "us-central1",
},
new Gcp.SecretManager.Inputs.SecretReplicationUserManagedReplicaArgs
{
Location = "us-east1",
},
},
},
},
DeletionProtection = false,
});
});
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 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")
.labels(Map.of("label", "my-label"))
.replication(SecretReplicationArgs.builder()
.userManaged(SecretReplicationUserManagedArgs.builder()
.replicas(
SecretReplicationUserManagedReplicaArgs.builder()
.location("us-central1")
.build(),
SecretReplicationUserManagedReplicaArgs.builder()
.location("us-east1")
.build())
.build())
.build())
.deletionProtection(false)
.build());
}
}
resources:
secret-basic:
type: gcp:secretmanager:Secret
properties:
secretId: secret
labels:
label: my-label
replication:
userManaged:
replicas:
- location: us-central1
- location: us-east1
deletionProtection: false
The replication property controls where secret data is stored. User-managed replication lets you specify exact regions via the replicas array. Each replica entry needs only a location. This configuration stores the secret in both us-central1 and us-east1, ensuring regional availability without automatic global replication.
Add client tool metadata with annotations
Client tools often store state information alongside secrets without requiring a separate database.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const secret_with_annotations = new gcp.secretmanager.Secret("secret-with-annotations", {
secretId: "secret",
labels: {
label: "my-label",
},
annotations: {
key1: "someval",
key2: "someval2",
key3: "someval3",
key4: "someval4",
key5: "someval5",
},
replication: {
auto: {},
},
});
import pulumi
import pulumi_gcp as gcp
secret_with_annotations = gcp.secretmanager.Secret("secret-with-annotations",
secret_id="secret",
labels={
"label": "my-label",
},
annotations={
"key1": "someval",
"key2": "someval2",
"key3": "someval3",
"key4": "someval4",
"key5": "someval5",
},
replication={
"auto": {},
})
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 {
_, err := secretmanager.NewSecret(ctx, "secret-with-annotations", &secretmanager.SecretArgs{
SecretId: pulumi.String("secret"),
Labels: pulumi.StringMap{
"label": pulumi.String("my-label"),
},
Annotations: pulumi.StringMap{
"key1": pulumi.String("someval"),
"key2": pulumi.String("someval2"),
"key3": pulumi.String("someval3"),
"key4": pulumi.String("someval4"),
"key5": pulumi.String("someval5"),
},
Replication: &secretmanager.SecretReplicationArgs{
Auto: &secretmanager.SecretReplicationAutoArgs{},
},
})
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_with_annotations = new Gcp.SecretManager.Secret("secret-with-annotations", new()
{
SecretId = "secret",
Labels =
{
{ "label", "my-label" },
},
Annotations =
{
{ "key1", "someval" },
{ "key2", "someval2" },
{ "key3", "someval3" },
{ "key4", "someval4" },
{ "key5", "someval5" },
},
Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
{
Auto = null,
},
});
});
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 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_with_annotations = new Secret("secret-with-annotations", SecretArgs.builder()
.secretId("secret")
.labels(Map.of("label", "my-label"))
.annotations(Map.ofEntries(
Map.entry("key1", "someval"),
Map.entry("key2", "someval2"),
Map.entry("key3", "someval3"),
Map.entry("key4", "someval4"),
Map.entry("key5", "someval5")
))
.replication(SecretReplicationArgs.builder()
.auto(SecretReplicationAutoArgs.builder()
.build())
.build())
.build());
}
}
resources:
secret-with-annotations:
type: gcp:secretmanager:Secret
properties:
secretId: secret
labels:
label: my-label
annotations:
key1: someval
key2: someval2
key3: someval3
key4: someval4
key5: someval5
replication:
auto: {}
Annotations provide key-value metadata that doesn’t affect secret behavior or access control. Unlike labels, annotations are designed for tool-specific state rather than resource organization. The replication property here uses auto mode, which lets Google manage replica placement automatically across regions.
Delay version destruction for recovery windows
Accidental deletions can break production systems; a destruction TTL provides recovery time.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const secret_with_version_destroy_ttl = new gcp.secretmanager.Secret("secret-with-version-destroy-ttl", {
secretId: "secret",
versionDestroyTtl: "2592000s",
replication: {
auto: {},
},
});
import pulumi
import pulumi_gcp as gcp
secret_with_version_destroy_ttl = gcp.secretmanager.Secret("secret-with-version-destroy-ttl",
secret_id="secret",
version_destroy_ttl="2592000s",
replication={
"auto": {},
})
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 {
_, err := secretmanager.NewSecret(ctx, "secret-with-version-destroy-ttl", &secretmanager.SecretArgs{
SecretId: pulumi.String("secret"),
VersionDestroyTtl: pulumi.String("2592000s"),
Replication: &secretmanager.SecretReplicationArgs{
Auto: &secretmanager.SecretReplicationAutoArgs{},
},
})
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_with_version_destroy_ttl = new Gcp.SecretManager.Secret("secret-with-version-destroy-ttl", new()
{
SecretId = "secret",
VersionDestroyTtl = "2592000s",
Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
{
Auto = null,
},
});
});
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 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_with_version_destroy_ttl = new Secret("secret-with-version-destroy-ttl", SecretArgs.builder()
.secretId("secret")
.versionDestroyTtl("2592000s")
.replication(SecretReplicationArgs.builder()
.auto(SecretReplicationAutoArgs.builder()
.build())
.build())
.build());
}
}
resources:
secret-with-version-destroy-ttl:
type: gcp:secretmanager:Secret
properties:
secretId: secret
versionDestroyTtl: 2592000s
replication:
auto: {}
The versionDestroyTtl property sets a grace period before version destruction. When you delete a version, it moves to a disabled state instead of immediate deletion. After the TTL expires (30 days in this example), the version is permanently destroyed. This prevents accidental data loss while still allowing eventual cleanup.
Encrypt secrets with customer-managed keys
Compliance requirements often mandate customer-managed encryption rather than Google-managed keys.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const project = gcp.organizations.getProject({});
const kms_secret_binding = new gcp.kms.CryptoKeyIAMMember("kms-secret-binding", {
cryptoKeyId: "kms-key",
role: "roles/cloudkms.cryptoKeyEncrypterDecrypter",
member: project.then(project => `serviceAccount:service-${project.number}@gcp-sa-secretmanager.iam.gserviceaccount.com`),
});
const secret_with_automatic_cmek = new gcp.secretmanager.Secret("secret-with-automatic-cmek", {
secretId: "secret",
replication: {
auto: {
customerManagedEncryption: {
kmsKeyName: "kms-key",
},
},
},
}, {
dependsOn: [kms_secret_binding],
});
import pulumi
import pulumi_gcp as gcp
project = gcp.organizations.get_project()
kms_secret_binding = gcp.kms.CryptoKeyIAMMember("kms-secret-binding",
crypto_key_id="kms-key",
role="roles/cloudkms.cryptoKeyEncrypterDecrypter",
member=f"serviceAccount:service-{project.number}@gcp-sa-secretmanager.iam.gserviceaccount.com")
secret_with_automatic_cmek = gcp.secretmanager.Secret("secret-with-automatic-cmek",
secret_id="secret",
replication={
"auto": {
"customer_managed_encryption": {
"kms_key_name": "kms-key",
},
},
},
opts = pulumi.ResourceOptions(depends_on=[kms_secret_binding]))
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/kms"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
"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 {
project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
if err != nil {
return err
}
kms_secret_binding, err := kms.NewCryptoKeyIAMMember(ctx, "kms-secret-binding", &kms.CryptoKeyIAMMemberArgs{
CryptoKeyId: pulumi.String("kms-key"),
Role: pulumi.String("roles/cloudkms.cryptoKeyEncrypterDecrypter"),
Member: pulumi.Sprintf("serviceAccount:service-%v@gcp-sa-secretmanager.iam.gserviceaccount.com", project.Number),
})
if err != nil {
return err
}
_, err = secretmanager.NewSecret(ctx, "secret-with-automatic-cmek", &secretmanager.SecretArgs{
SecretId: pulumi.String("secret"),
Replication: &secretmanager.SecretReplicationArgs{
Auto: &secretmanager.SecretReplicationAutoArgs{
CustomerManagedEncryption: &secretmanager.SecretReplicationAutoCustomerManagedEncryptionArgs{
KmsKeyName: pulumi.String("kms-key"),
},
},
},
}, pulumi.DependsOn([]pulumi.Resource{
kms_secret_binding,
}))
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 project = Gcp.Organizations.GetProject.Invoke();
var kms_secret_binding = new Gcp.Kms.CryptoKeyIAMMember("kms-secret-binding", new()
{
CryptoKeyId = "kms-key",
Role = "roles/cloudkms.cryptoKeyEncrypterDecrypter",
Member = $"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-secretmanager.iam.gserviceaccount.com",
});
var secret_with_automatic_cmek = new Gcp.SecretManager.Secret("secret-with-automatic-cmek", new()
{
SecretId = "secret",
Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
{
Auto = new Gcp.SecretManager.Inputs.SecretReplicationAutoArgs
{
CustomerManagedEncryption = new Gcp.SecretManager.Inputs.SecretReplicationAutoCustomerManagedEncryptionArgs
{
KmsKeyName = "kms-key",
},
},
},
}, new CustomResourceOptions
{
DependsOn =
{
kms_secret_binding,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.kms.CryptoKeyIAMMember;
import com.pulumi.gcp.kms.CryptoKeyIAMMemberArgs;
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.inputs.SecretReplicationAutoCustomerManagedEncryptionArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
.build());
var kms_secret_binding = new CryptoKeyIAMMember("kms-secret-binding", CryptoKeyIAMMemberArgs.builder()
.cryptoKeyId("kms-key")
.role("roles/cloudkms.cryptoKeyEncrypterDecrypter")
.member(String.format("serviceAccount:service-%s@gcp-sa-secretmanager.iam.gserviceaccount.com", project.number()))
.build());
var secret_with_automatic_cmek = new Secret("secret-with-automatic-cmek", SecretArgs.builder()
.secretId("secret")
.replication(SecretReplicationArgs.builder()
.auto(SecretReplicationAutoArgs.builder()
.customerManagedEncryption(SecretReplicationAutoCustomerManagedEncryptionArgs.builder()
.kmsKeyName("kms-key")
.build())
.build())
.build())
.build(), CustomResourceOptions.builder()
.dependsOn(kms_secret_binding)
.build());
}
}
resources:
kms-secret-binding:
type: gcp:kms:CryptoKeyIAMMember
properties:
cryptoKeyId: kms-key
role: roles/cloudkms.cryptoKeyEncrypterDecrypter
member: serviceAccount:service-${project.number}@gcp-sa-secretmanager.iam.gserviceaccount.com
secret-with-automatic-cmek:
type: gcp:secretmanager:Secret
properties:
secretId: secret
replication:
auto:
customerManagedEncryption:
kmsKeyName: kms-key
options:
dependsOn:
- ${["kms-secret-binding"]}
variables:
project:
fn::invoke:
function: gcp:organizations:getProject
arguments: {}
The customerManagedEncryption block within auto replication specifies a Cloud KMS key for encryption. The CryptoKeyIAMMember resource grants the Secret Manager service account permission to use your key. The dependsOn ensures IAM bindings exist before creating the secret, preventing permission errors during creation.
Beyond these examples
These snippets focus on specific secret-level features: replication strategies, metadata and lifecycle controls, and customer-managed encryption. They’re intentionally minimal rather than full secrets management solutions.
The examples may reference pre-existing infrastructure such as Cloud KMS keys for encryption and IAM permissions for the Secret Manager service account. They focus on configuring the secret container rather than provisioning encryption infrastructure or storing actual secret values.
To keep things focused, common secret patterns are omitted, including:
- Secret rotation configuration (rotation property)
- Pub/Sub notification topics for lifecycle events
- Version aliases for stable references
- Expiration times and TTL-based lifecycle
These omissions are intentional: the goal is to illustrate how each secret feature is wired, not provide drop-in secrets management modules. See the Secret Manager Secret resource reference for all available configuration options.
Let's create GCP Secret Manager Secrets
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & Immutability
replication property is immutable and cannot be changed after the secret has been created. You must recreate the secret to change replication settings.replication, the following properties cannot be changed: secretId, project, and tags. Modifying these requires recreating the resource.replication.auto) replicates your secret across all GCP regions. User-managed replication (replication.userManaged.replicas) lets you specify exact locations like us-central1 and us-east1.Expiration & Lifecycle
expireTime sets an absolute timestamp for expiration (RFC3339 format), while ttl sets a duration like “3.5s”. You can only specify one, not both.deletionProtection is set to true, Pulumi will prevent destroying the secret. It defaults to false. This protects against accidental deletion.Metadata & Labels
labels and annotations fields are non-authoritative, meaning they only manage what’s in your configuration. To see all labels/annotations (including those set by other clients), use the effectiveLabels and effectiveAnnotations output properties.Encryption & Security
replication.auto.customerManagedEncryption.kmsKeyName to your KMS key. You must grant the Secret Manager service account (service-PROJECT_NUMBER@gcp-sa-secretmanager.iam.gserviceaccount.com) the roles/cloudkms.cryptoKeyEncrypterDecrypter role. Use dependsOn to ensure the IAM binding is created before the secret.Rotation & Notifications
rotation property with nextRotationTime and rotationPeriod. You must also configure topics with up to 10 Pub/Sub topics to receive rotation notifications. Secret Manager sends notifications to these topics at rotation time.