Create GCP Regional Secrets

The gcp:secretmanager/regionalSecret:RegionalSecret resource, part of the Pulumi GCP provider, defines the secret container and its configuration within a specific GCP region: encryption, rotation, and lifecycle policies. This guide focuses on four capabilities: regional secret creation with metadata, customer-managed encryption, rotation scheduling, and version lifecycle controls.

Regional secrets are containers that hold configuration but not the actual secret data. Secret values are stored in separate RegionalSecretVersion resources. The examples are intentionally small. Combine them with version resources, IAM policies, and your own KMS keys or Pub/Sub topics.

Create a regional secret with labels and annotations

Most deployments start by creating a secret container in a specific region with organizational metadata.

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

const regional_secret_basic = new gcp.secretmanager.RegionalSecret("regional-secret-basic", {
    secretId: "tf-reg-secret",
    location: "us-central1",
    labels: {
        label: "my-label",
    },
    annotations: {
        key1: "value1",
        key2: "value2",
        key3: "value3",
    },
    deletionProtection: false,
});
import pulumi
import pulumi_gcp as gcp

regional_secret_basic = gcp.secretmanager.RegionalSecret("regional-secret-basic",
    secret_id="tf-reg-secret",
    location="us-central1",
    labels={
        "label": "my-label",
    },
    annotations={
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
    },
    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.NewRegionalSecret(ctx, "regional-secret-basic", &secretmanager.RegionalSecretArgs{
			SecretId: pulumi.String("tf-reg-secret"),
			Location: pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label": pulumi.String("my-label"),
			},
			Annotations: pulumi.StringMap{
				"key1": pulumi.String("value1"),
				"key2": pulumi.String("value2"),
				"key3": pulumi.String("value3"),
			},
			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 regional_secret_basic = new Gcp.SecretManager.RegionalSecret("regional-secret-basic", new()
    {
        SecretId = "tf-reg-secret",
        Location = "us-central1",
        Labels = 
        {
            { "label", "my-label" },
        },
        Annotations = 
        {
            { "key1", "value1" },
            { "key2", "value2" },
            { "key3", "value3" },
        },
        DeletionProtection = false,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.RegionalSecret;
import com.pulumi.gcp.secretmanager.RegionalSecretArgs;
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 regional_secret_basic = new RegionalSecret("regional-secret-basic", RegionalSecretArgs.builder()
            .secretId("tf-reg-secret")
            .location("us-central1")
            .labels(Map.of("label", "my-label"))
            .annotations(Map.ofEntries(
                Map.entry("key1", "value1"),
                Map.entry("key2", "value2"),
                Map.entry("key3", "value3")
            ))
            .deletionProtection(false)
            .build());

    }
}
resources:
  regional-secret-basic:
    type: gcp:secretmanager:RegionalSecret
    properties:
      secretId: tf-reg-secret
      location: us-central1
      labels:
        label: my-label
      annotations:
        key1: value1
        key2: value2
        key3: value3
      deletionProtection: false

The secretId provides a unique identifier within the project, while location pins the secret to a specific region (us-central1 in this example). Labels support resource organization and billing, while annotations store tool-specific metadata without requiring a database. The secret container exists but holds no data until you create a RegionalSecretVersion.

Encrypt secrets with customer-managed KMS keys

Organizations with compliance requirements often control their own encryption keys rather than using Google-managed encryption.

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 regional_secret_with_cmek = new gcp.secretmanager.RegionalSecret("regional-secret-with-cmek", {
    secretId: "tf-reg-secret",
    location: "us-central1",
    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")
regional_secret_with_cmek = gcp.secretmanager.RegionalSecret("regional-secret-with-cmek",
    secret_id="tf-reg-secret",
    location="us-central1",
    customer_managed_encryption={
        "kms_key_name": "kms-key",
    },
    opts = pulumi.ResourceOptions(depends_on=[kms_secret_binding]))
package main

import (
	"fmt"

	"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.NewRegionalSecret(ctx, "regional-secret-with-cmek", &secretmanager.RegionalSecretArgs{
			SecretId: pulumi.String("tf-reg-secret"),
			Location: pulumi.String("us-central1"),
			CustomerManagedEncryption: &secretmanager.RegionalSecretCustomerManagedEncryptionArgs{
				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 regional_secret_with_cmek = new Gcp.SecretManager.RegionalSecret("regional-secret-with-cmek", new()
    {
        SecretId = "tf-reg-secret",
        Location = "us-central1",
        CustomerManagedEncryption = new Gcp.SecretManager.Inputs.RegionalSecretCustomerManagedEncryptionArgs
        {
            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.RegionalSecret;
import com.pulumi.gcp.secretmanager.RegionalSecretArgs;
import com.pulumi.gcp.secretmanager.inputs.RegionalSecretCustomerManagedEncryptionArgs;
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 regional_secret_with_cmek = new RegionalSecret("regional-secret-with-cmek", RegionalSecretArgs.builder()
            .secretId("tf-reg-secret")
            .location("us-central1")
            .customerManagedEncryption(RegionalSecretCustomerManagedEncryptionArgs.builder()
                .kmsKeyName("kms-key")
                .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
  regional-secret-with-cmek:
    type: gcp:secretmanager:RegionalSecret
    properties:
      secretId: tf-reg-secret
      location: us-central1
      customerManagedEncryption:
        kmsKeyName: kms-key
    options:
      dependsOn:
        - ${["kms-secret-binding"]}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The customerManagedEncryption block references a KMS key by name. Before creating the secret, you must grant the Secret Manager service account cryptoKeyEncrypterDecrypter permissions on the KMS key. The dependsOn ensures the IAM binding completes before secret creation.

Schedule automatic rotation notifications

Secrets requiring periodic updates can trigger Pub/Sub notifications at scheduled intervals for external rotation systems.

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

const project = gcp.organizations.getProject({});
const topic = new gcp.pubsub.Topic("topic", {name: "tf-topic"});
const secretsManagerAccess = new gcp.pubsub.TopicIAMMember("secrets_manager_access", {
    topic: topic.name,
    role: "roles/pubsub.publisher",
    member: project.then(project => `serviceAccount:service-${project.number}@gcp-sa-secretmanager.iam.gserviceaccount.com`),
});
const regional_secret_with_rotation = new gcp.secretmanager.RegionalSecret("regional-secret-with-rotation", {
    secretId: "tf-reg-secret",
    location: "us-central1",
    topics: [{
        name: topic.id,
    }],
    rotation: {
        rotationPeriod: "3600s",
        nextRotationTime: "2045-11-30T00:00:00Z",
    },
}, {
    dependsOn: [secretsManagerAccess],
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
topic = gcp.pubsub.Topic("topic", name="tf-topic")
secrets_manager_access = gcp.pubsub.TopicIAMMember("secrets_manager_access",
    topic=topic.name,
    role="roles/pubsub.publisher",
    member=f"serviceAccount:service-{project.number}@gcp-sa-secretmanager.iam.gserviceaccount.com")
regional_secret_with_rotation = gcp.secretmanager.RegionalSecret("regional-secret-with-rotation",
    secret_id="tf-reg-secret",
    location="us-central1",
    topics=[{
        "name": topic.id,
    }],
    rotation={
        "rotation_period": "3600s",
        "next_rotation_time": "2045-11-30T00:00:00Z",
    },
    opts = pulumi.ResourceOptions(depends_on=[secrets_manager_access]))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/pubsub"
	"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
		}
		topic, err := pubsub.NewTopic(ctx, "topic", &pubsub.TopicArgs{
			Name: pulumi.String("tf-topic"),
		})
		if err != nil {
			return err
		}
		secretsManagerAccess, err := pubsub.NewTopicIAMMember(ctx, "secrets_manager_access", &pubsub.TopicIAMMemberArgs{
			Topic:  topic.Name,
			Role:   pulumi.String("roles/pubsub.publisher"),
			Member: pulumi.Sprintf("serviceAccount:service-%v@gcp-sa-secretmanager.iam.gserviceaccount.com", project.Number),
		})
		if err != nil {
			return err
		}
		_, err = secretmanager.NewRegionalSecret(ctx, "regional-secret-with-rotation", &secretmanager.RegionalSecretArgs{
			SecretId: pulumi.String("tf-reg-secret"),
			Location: pulumi.String("us-central1"),
			Topics: secretmanager.RegionalSecretTopicArray{
				&secretmanager.RegionalSecretTopicArgs{
					Name: topic.ID(),
				},
			},
			Rotation: &secretmanager.RegionalSecretRotationArgs{
				RotationPeriod:   pulumi.String("3600s"),
				NextRotationTime: pulumi.String("2045-11-30T00:00:00Z"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			secretsManagerAccess,
		}))
		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 topic = new Gcp.PubSub.Topic("topic", new()
    {
        Name = "tf-topic",
    });

    var secretsManagerAccess = new Gcp.PubSub.TopicIAMMember("secrets_manager_access", new()
    {
        Topic = topic.Name,
        Role = "roles/pubsub.publisher",
        Member = $"serviceAccount:service-{project.Apply(getProjectResult => getProjectResult.Number)}@gcp-sa-secretmanager.iam.gserviceaccount.com",
    });

    var regional_secret_with_rotation = new Gcp.SecretManager.RegionalSecret("regional-secret-with-rotation", new()
    {
        SecretId = "tf-reg-secret",
        Location = "us-central1",
        Topics = new[]
        {
            new Gcp.SecretManager.Inputs.RegionalSecretTopicArgs
            {
                Name = topic.Id,
            },
        },
        Rotation = new Gcp.SecretManager.Inputs.RegionalSecretRotationArgs
        {
            RotationPeriod = "3600s",
            NextRotationTime = "2045-11-30T00:00:00Z",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            secretsManagerAccess,
        },
    });

});
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.pubsub.Topic;
import com.pulumi.gcp.pubsub.TopicArgs;
import com.pulumi.gcp.pubsub.TopicIAMMember;
import com.pulumi.gcp.pubsub.TopicIAMMemberArgs;
import com.pulumi.gcp.secretmanager.RegionalSecret;
import com.pulumi.gcp.secretmanager.RegionalSecretArgs;
import com.pulumi.gcp.secretmanager.inputs.RegionalSecretTopicArgs;
import com.pulumi.gcp.secretmanager.inputs.RegionalSecretRotationArgs;
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 topic = new Topic("topic", TopicArgs.builder()
            .name("tf-topic")
            .build());

        var secretsManagerAccess = new TopicIAMMember("secretsManagerAccess", TopicIAMMemberArgs.builder()
            .topic(topic.name())
            .role("roles/pubsub.publisher")
            .member(String.format("serviceAccount:service-%s@gcp-sa-secretmanager.iam.gserviceaccount.com", project.number()))
            .build());

        var regional_secret_with_rotation = new RegionalSecret("regional-secret-with-rotation", RegionalSecretArgs.builder()
            .secretId("tf-reg-secret")
            .location("us-central1")
            .topics(RegionalSecretTopicArgs.builder()
                .name(topic.id())
                .build())
            .rotation(RegionalSecretRotationArgs.builder()
                .rotationPeriod("3600s")
                .nextRotationTime("2045-11-30T00:00:00Z")
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(secretsManagerAccess)
                .build());

    }
}
resources:
  topic:
    type: gcp:pubsub:Topic
    properties:
      name: tf-topic
  secretsManagerAccess:
    type: gcp:pubsub:TopicIAMMember
    name: secrets_manager_access
    properties:
      topic: ${topic.name}
      role: roles/pubsub.publisher
      member: serviceAccount:service-${project.number}@gcp-sa-secretmanager.iam.gserviceaccount.com
  regional-secret-with-rotation:
    type: gcp:secretmanager:RegionalSecret
    properties:
      secretId: tf-reg-secret
      location: us-central1
      topics:
        - name: ${topic.id}
      rotation:
        rotationPeriod: 3600s
        nextRotationTime: 2045-11-30T00:00:00Z
    options:
      dependsOn:
        - ${secretsManagerAccess}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The rotation block sets when notifications begin (nextRotationTime) and how often they repeat (rotationPeriod). Secret Manager publishes messages to the configured Pub/Sub topics; your systems subscribe to these topics and perform the actual rotation. The topics array can include up to 10 Pub/Sub topic references.

Set time-to-live for automatic expiration

Temporary secrets like short-lived tokens can expire automatically after a fixed duration.

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

const regional_secret_with_ttl = new gcp.secretmanager.RegionalSecret("regional-secret-with-ttl", {
    secretId: "tf-reg-secret",
    location: "us-central1",
    labels: {
        label: "my-label",
    },
    annotations: {
        key1: "value1",
        key2: "value2",
        key3: "value3",
    },
    ttl: "36000s",
});
import pulumi
import pulumi_gcp as gcp

regional_secret_with_ttl = gcp.secretmanager.RegionalSecret("regional-secret-with-ttl",
    secret_id="tf-reg-secret",
    location="us-central1",
    labels={
        "label": "my-label",
    },
    annotations={
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
    },
    ttl="36000s")
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.NewRegionalSecret(ctx, "regional-secret-with-ttl", &secretmanager.RegionalSecretArgs{
			SecretId: pulumi.String("tf-reg-secret"),
			Location: pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label": pulumi.String("my-label"),
			},
			Annotations: pulumi.StringMap{
				"key1": pulumi.String("value1"),
				"key2": pulumi.String("value2"),
				"key3": pulumi.String("value3"),
			},
			Ttl: pulumi.String("36000s"),
		})
		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 regional_secret_with_ttl = new Gcp.SecretManager.RegionalSecret("regional-secret-with-ttl", new()
    {
        SecretId = "tf-reg-secret",
        Location = "us-central1",
        Labels = 
        {
            { "label", "my-label" },
        },
        Annotations = 
        {
            { "key1", "value1" },
            { "key2", "value2" },
            { "key3", "value3" },
        },
        Ttl = "36000s",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.RegionalSecret;
import com.pulumi.gcp.secretmanager.RegionalSecretArgs;
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 regional_secret_with_ttl = new RegionalSecret("regional-secret-with-ttl", RegionalSecretArgs.builder()
            .secretId("tf-reg-secret")
            .location("us-central1")
            .labels(Map.of("label", "my-label"))
            .annotations(Map.ofEntries(
                Map.entry("key1", "value1"),
                Map.entry("key2", "value2"),
                Map.entry("key3", "value3")
            ))
            .ttl("36000s")
            .build());

    }
}
resources:
  regional-secret-with-ttl:
    type: gcp:secretmanager:RegionalSecret
    properties:
      secretId: tf-reg-secret
      location: us-central1
      labels:
        label: my-label
      annotations:
        key1: value1
        key2: value2
        key3: value3
      ttl: 36000s

The ttl property sets a duration from creation time (36000 seconds, or 10 hours). After this period, the secret expires automatically. You can use ttl or expireTime, but not both.

Delay version destruction for recovery windows

Production secrets often need a grace period before permanent deletion to allow recovery from accidental operations.

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

const regional_secret_with_version_destroy_ttl = new gcp.secretmanager.RegionalSecret("regional-secret-with-version-destroy-ttl", {
    secretId: "tf-reg-secret",
    location: "us-central1",
    labels: {
        label: "my-label",
    },
    annotations: {
        key1: "value1",
        key2: "value2",
        key3: "value3",
    },
    versionDestroyTtl: "86400s",
});
import pulumi
import pulumi_gcp as gcp

regional_secret_with_version_destroy_ttl = gcp.secretmanager.RegionalSecret("regional-secret-with-version-destroy-ttl",
    secret_id="tf-reg-secret",
    location="us-central1",
    labels={
        "label": "my-label",
    },
    annotations={
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
    },
    version_destroy_ttl="86400s")
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.NewRegionalSecret(ctx, "regional-secret-with-version-destroy-ttl", &secretmanager.RegionalSecretArgs{
			SecretId: pulumi.String("tf-reg-secret"),
			Location: pulumi.String("us-central1"),
			Labels: pulumi.StringMap{
				"label": pulumi.String("my-label"),
			},
			Annotations: pulumi.StringMap{
				"key1": pulumi.String("value1"),
				"key2": pulumi.String("value2"),
				"key3": pulumi.String("value3"),
			},
			VersionDestroyTtl: pulumi.String("86400s"),
		})
		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 regional_secret_with_version_destroy_ttl = new Gcp.SecretManager.RegionalSecret("regional-secret-with-version-destroy-ttl", new()
    {
        SecretId = "tf-reg-secret",
        Location = "us-central1",
        Labels = 
        {
            { "label", "my-label" },
        },
        Annotations = 
        {
            { "key1", "value1" },
            { "key2", "value2" },
            { "key3", "value3" },
        },
        VersionDestroyTtl = "86400s",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.RegionalSecret;
import com.pulumi.gcp.secretmanager.RegionalSecretArgs;
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 regional_secret_with_version_destroy_ttl = new RegionalSecret("regional-secret-with-version-destroy-ttl", RegionalSecretArgs.builder()
            .secretId("tf-reg-secret")
            .location("us-central1")
            .labels(Map.of("label", "my-label"))
            .annotations(Map.ofEntries(
                Map.entry("key1", "value1"),
                Map.entry("key2", "value2"),
                Map.entry("key3", "value3")
            ))
            .versionDestroyTtl("86400s")
            .build());

    }
}
resources:
  regional-secret-with-version-destroy-ttl:
    type: gcp:secretmanager:RegionalSecret
    properties:
      secretId: tf-reg-secret
      location: us-central1
      labels:
        label: my-label
      annotations:
        key1: value1
        key2: value2
        key3: value3
      versionDestroyTtl: 86400s

The versionDestroyTtl property sets a minimum 24-hour delay between destroy requests and actual deletion. During this window, versions enter a disabled state but remain recoverable. This provides a safety net for production secrets.

Beyond these examples

These snippets focus on specific regional secret features: regional secret creation with metadata, customer-managed encryption, and rotation scheduling and version lifecycle. They’re intentionally minimal rather than full secret management solutions.

The examples may reference pre-existing infrastructure such as KMS keys for customer-managed encryption, Pub/Sub topics for rotation notifications, and IAM permissions for Secret Manager service accounts. They focus on configuring the secret container rather than provisioning everything around it.

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

  • Secret version creation and data storage (requires RegionalSecretVersion resource)
  • IAM access policies for secret consumers
  • Version aliases for stable references
  • Expire time for absolute expiration dates (alternative to TTL)

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

Let's create GCP Regional Secrets

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Expiration & Lifecycle
Can I use both ttl and expireTime to configure secret expiration?
No, only one of ttl or expireTime can be provided. Use ttl for duration-based expiration (e.g., “36000s”) or expireTime for timestamp-based expiration (e.g., “2055-11-30T00:00:00Z”).
What's the minimum TTL for delayed secret version deletion?
The versionDestroyTtl must be at least 24 hours (86400s). Versions go to a disabled state first, then are destroyed after the TTL expires.
How do I enable delayed deletion for secret versions?
Set versionDestroyTtl to at least “86400s” (24 hours). Versions will be disabled first, then destroyed after the TTL expires.
Rotation & Notifications
How do I enable automatic secret rotation?
Configure rotation with rotationPeriod and nextRotationTime, set topics with Pub/Sub topic IDs, and grant the Secret Manager service account roles/pubsub.publisher role. Use dependsOn to ensure IAM binding is created first.
Can I configure rotation without setting up Pub/Sub topics?
No, the topics property must be set to configure rotation. Secret Manager sends Pub/Sub notifications at nextRotationTime.
How many Pub/Sub topics can I configure for notifications?
You can configure up to 10 Pub/Sub topics for control plane operation notifications.
Encryption
How do I configure customer-managed encryption for a regional secret?
Set customerManagedEncryption.kmsKeyName and ensure the Secret Manager service account has roles/cloudkms.cryptoKeyEncrypterDecrypter role on the KMS key. Use dependsOn to enforce IAM binding creation first.
Metadata & Labels
Why aren't my labels or annotations being removed when I delete them from my config?
The labels and annotations fields are non-authoritative, meaning Pulumi only manages the labels/annotations present in your configuration. To see all labels/annotations on the resource, use the effectiveLabels and effectiveAnnotations output properties.
Immutability & Constraints
What properties can't be changed after creating a regional secret?
The following properties are immutable: location, secretId, project, and tags. Changing these requires recreating the resource.
What are the restrictions on version aliases?
Version aliases must start with a letter, can contain letters, numerals, hyphens, and underscores, have a maximum length of 63 characters, cannot be “latest” or “NEW”, and you can assign up to 50 aliases per secret.
What are the label key and value constraints?
Label keys must be 1-63 characters, match the regex [\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}, and have UTF-8 encoding up to 128 bytes. Label values must be 0-63 characters, match [\p{Ll}\p{Lo}\p{N}_-]{0,63}, and have UTF-8 encoding up to 128 bytes. Maximum 64 labels per resource.

Using a different cloud?

Explore security guides for other cloud providers: