diff --git a/secret-manager/createSecretWithCmek.js b/secret-manager/createSecretWithCmek.js new file mode 100644 index 0000000000..28d8e727ed --- /dev/null +++ b/secret-manager/createSecretWithCmek.js @@ -0,0 +1,56 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(parent, secretId, kmsKeyName) { + // [START secretmanager_create_secret_with_cmek] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'projects/my-project'; + // const secretId = 'my-secret-with-cmek'; + // const kmsKeyName = 'projects/my-project/locations/global/keyRings/my-keyring/cryptoKeys/my-key'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function createSecretWithCmek() { + // Create the secret with automatic replication and CMEK + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + replication: { + automatic: { + customerManagedEncryption: { + kmsKeyName: kmsKeyName, + }, + }, + }, + }, + }); + + console.log(`Created secret ${secret.name} with CMEK key ${kmsKeyName}`); + } + + createSecretWithCmek(); + // [END secretmanager_create_secret_with_cmek] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/createSecretWithExpiration.js b/secret-manager/createSecretWithExpiration.js new file mode 100644 index 0000000000..0aaaa9228b --- /dev/null +++ b/secret-manager/createSecretWithExpiration.js @@ -0,0 +1,61 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(parent, secretId) { + // [START secretmanager_create_secret_with_expiration] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const parent = 'projects/my-project'; + // const secretId = 'my-secret'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function createSecretWithExpiration() { + // Calculate expiration time (1 hour from now) + const expireTime = new Date(); + expireTime.setHours(expireTime.getHours() + 1); + + // Create the secret with automatic replication and expiration time + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + replication: { + automatic: {}, + }, + expireTime: { + seconds: Math.floor(expireTime.getTime() / 1000), + nanos: (expireTime.getTime() % 1000) * 1000000, + }, + }, + }); + + console.log( + `Created secret ${secret.name} with expiration time ${expireTime.toISOString()}` + ); + } + + createSecretWithExpiration(); + // [END secretmanager_create_secret_with_expiration] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/createSecretWithRotation.js b/secret-manager/createSecretWithRotation.js new file mode 100644 index 0000000000..ad8f36dfd6 --- /dev/null +++ b/secret-manager/createSecretWithRotation.js @@ -0,0 +1,76 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(parent, secretId, topicName) { + // [START secretmanager_create_secret_with_rotation] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const parent = 'projects/my-project'; + // const secretId = 'my-rotating-secret'; + // const topicName = 'projects/my-project/topics/my-rotation-topic'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function createSecretWithRotation() { + // Set rotation period to 24 hours + const rotationPeriodHours = 24; + + // Calculate next rotation time (24 hours from now) + const nextRotationTime = new Date(); + nextRotationTime.setHours(nextRotationTime.getHours() + 24); + + // Create the secret with rotation configuration + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + replication: { + automatic: {}, + }, + topics: [ + { + name: topicName, + }, + ], + rotation: { + nextRotationTime: { + seconds: Math.floor(nextRotationTime.getTime() / 1000), + nanos: (nextRotationTime.getTime() % 1000) * 1000000, + }, + rotationPeriod: { + seconds: rotationPeriodHours * 3600, + nanos: 0, + }, + }, + }, + }); + + console.log( + `Created secret ${secret.name} with rotation period ${rotationPeriodHours} hours and topic ${topicName}` + ); + } + + createSecretWithRotation(); + // [END secretmanager_create_secret_with_rotation] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/createSecretWithTopic.js b/secret-manager/createSecretWithTopic.js new file mode 100644 index 0000000000..706a86d899 --- /dev/null +++ b/secret-manager/createSecretWithTopic.js @@ -0,0 +1,57 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(parent, secretId, topicName) { + // [START secretmanager_create_secret_with_topic] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const parent = 'projects/my-project'; + // const secretId = 'my-secret-with-notifications'; + // const topicName = 'projects/my-project/topics/my-secret-topic'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function createSecretWithTopic() { + // Create the secret with topic configuration + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + replication: { + automatic: {}, + }, + topics: [ + { + name: topicName, + }, + ], + }, + }); + + console.log(`Created secret ${secret.name} with topic ${topicName}`); + } + + createSecretWithTopic(); + // [END secretmanager_create_secret_with_topic] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/createSecretWithUserManagedReplicationPolicy.js b/secret-manager/createSecretWithUserManagedReplicationPolicy.js new file mode 100644 index 0000000000..720d480c21 --- /dev/null +++ b/secret-manager/createSecretWithUserManagedReplicationPolicy.js @@ -0,0 +1,62 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(parent, secretId, locations, ttl) { + // [START secretmanager_create_ummr_secret] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const parent = 'projects/my-project'; + // const secretId = 'my-new-secret'; + // const locations = ['us-east1', 'europe-west1']; + // const ttl = 7776000; // Optional: 90 days in seconds + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function createUmmrSecret() { + // Create the secret configuration + const secretConfig = { + replication: { + userManaged: { + replicas: locations.map(location => ({location})), + }, + }, + ttl: { + seconds: ttl, + }, + }; + + // Create the secret + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: secretConfig, + }); + + console.log(`Created secret: ${secret.name}`); + } + + createUmmrSecret(); + // [END secretmanager_create_ummr_secret] +} + +const args = process.argv.slice(2); +const locations = args[2] ? args[2].split(',') : []; +main(args[0], args[1], locations, args[3]).catch(console.error); diff --git a/secret-manager/deleteSecretExpiration.js b/secret-manager/deleteSecretExpiration.js new file mode 100644 index 0000000000..6a9f57ea5d --- /dev/null +++ b/secret-manager/deleteSecretExpiration.js @@ -0,0 +1,50 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(name = 'projects/my-project/secrets/my-secret') { + // [START secretmanager_delete_secret_expiration] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const name = 'projects/my-project/secrets/my-secret'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function deleteSecretExpiration() { + // Update the secret with an empty expireTime field to remove the expiration + const [secret] = await client.updateSecret({ + secret: { + name: name, + // No expireTime field specified, which will clear it + }, + updateMask: { + paths: ['expire_time'], + }, + }); + + console.log(`Removed expiration from secret ${secret.name}`); + } + + await deleteSecretExpiration(); + // [END secretmanager_delete_secret_expiration] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/deleteSecretRotation.js b/secret-manager/deleteSecretRotation.js new file mode 100644 index 0000000000..e9a9b38f69 --- /dev/null +++ b/secret-manager/deleteSecretRotation.js @@ -0,0 +1,49 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(name = 'projects/my-project/secrets/my-secret') { + // [START secretmanager_delete_secret_rotation] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const name = 'projects/my-project/secrets/my-secret'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function deleteSecretRotation() { + // Update the secret with the rotation field removed + const [secret] = await client.updateSecret({ + secret: { + name: name, + }, + updateMask: { + paths: ['rotation'], + }, + }); + + console.log(`Removed rotation from secret ${secret.name}`); + } + + deleteSecretRotation(); + // [END secretmanager_delete_secret_rotation] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/detachTagBinding.js b/secret-manager/detachTagBinding.js new file mode 100644 index 0000000000..9bd85dd623 --- /dev/null +++ b/secret-manager/detachTagBinding.js @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(name, tagValue) { + // [START secretmanager_detach_tag_binding] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const name = 'projects/my-project/secrets/my-secret'; + // const tagValue = 'tagValues/123456789012'; + + // Import the Resource Manager and Secret Manager libraries + const {TagBindingsClient} = require('@google-cloud/resource-manager').v3; + + // Create the Resource Manager client + const rmClient = new TagBindingsClient(); + + // Build the resource name of the parent secret + const parent = `//secretmanager.googleapis.com/${name}`; + + async function detachTag() { + // Find the binding name for the given tag value + let bindingName = null; + const iterable = rmClient.listTagBindingsAsync( + { + parent: parent, + pageSize: 50, + }, + {autoPaginate: false} + ); + + for await (const binding of iterable) { + if (binding.tagValue === tagValue) { + bindingName = binding.name; + break; + } + } + + if (bindingName === null) { + console.log(`Tag binding for value ${tagValue} not found on ${name}.`); + return; + } + + // Delete the tag binding + const [operation] = await rmClient.deleteTagBinding({ + name: bindingName, + }); + + // Wait for the operation to complete + await operation.promise(); + console.log(`Detached tag value ${tagValue} from ${name}`); + } + + detachTag(); + // [END secretmanager_detach_tag_binding] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/listSecretVersionsWithFilter.js b/secret-manager/listSecretVersionsWithFilter.js new file mode 100644 index 0000000000..1018e912fb --- /dev/null +++ b/secret-manager/listSecretVersionsWithFilter.js @@ -0,0 +1,47 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(parent = 'projects/my-project/secrets/my-secret') { + // [START secretmanager_list_secret_versions_with_filter] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const parent = 'projects/my-project/secrets/my-secret'; + const filterStr = 'state=DISABLED'; + + // Imports the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Instantiates a client + const client = new SecretManagerServiceClient(); + + async function listSecretVersionsWithFilter() { + const [versions] = await client.listSecretVersions({ + parent: parent, + filter: filterStr, + }); + + versions.forEach(version => { + console.log(`Found version: ${version.name}`); + }); + } + + listSecretVersionsWithFilter(); + // [END secretmanager_list_secret_versions_with_filter] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/listSecretsWithFilter.js b/secret-manager/listSecretsWithFilter.js new file mode 100644 index 0000000000..56a98c6e41 --- /dev/null +++ b/secret-manager/listSecretsWithFilter.js @@ -0,0 +1,52 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId) { + // [START secretmanager_list_secrets_with_filter] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + const filterStr = 'labels.secretmanager=rocks'; + + // Imports the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Instantiates a client + const client = new SecretManagerServiceClient(); + + // Build the resource name of the parent project + const parent = `projects/${projectId}`; + + // List all secrets + async function listSecretsWithFilter() { + const [secrets] = await client.listSecrets({ + parent: parent, + filter: filterStr, + }); + + // Print each secret + for (const secret of secrets) { + console.log(`Found secret: ${secret.name}`); + } + } + + listSecretsWithFilter(); + // [END secretmanager_list_secrets_with_filter] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/listTagBindings.js b/secret-manager/listTagBindings.js new file mode 100644 index 0000000000..59eec7a71e --- /dev/null +++ b/secret-manager/listTagBindings.js @@ -0,0 +1,63 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(name = 'projects/my-project/secrets/my-secret') { + // [START secretmanager_list_tag_bindings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const name = 'projects/my-project/secrets/my-secret'; + + // Import the Resource Manager and Secret Manager libraries + const {TagBindingsClient} = require('@google-cloud/resource-manager').v3; + + // Create the Resource Manager client + const client = new TagBindingsClient(); + + // Build the resource name of the parent secret + const parent = `//secretmanager.googleapis.com/${name}`; + + async function listTagBindings() { + // List all tag bindings + let foundBindings = false; + + // Use paginate to handle any pagination in the response + const iterable = client.listTagBindingsAsync( + { + parent: parent, + pageSize: 10, + }, + {autoPaginate: false} + ); + + console.log(`Tag bindings for ${name}:`); + + for await (const binding of iterable) { + console.log(`- Tag Value: ${binding.tagValue}`); + foundBindings = true; + } + + if (!foundBindings) { + console.log(`No tag bindings found for ${name}.`); + } + } + + listTagBindings(); + // [END secretmanager_list_tag_bindings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/createRegionalSecretWithCmek.js b/secret-manager/regional_samples/createRegionalSecretWithCmek.js new file mode 100644 index 0000000000..a7157255e8 --- /dev/null +++ b/secret-manager/regional_samples/createRegionalSecretWithCmek.js @@ -0,0 +1,57 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, locationId, secretId, kmsKeyName) { + // [START secretmanager_create_secret_with_cmek] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const locationId = 'my-location'; + // const secretId = 'my-secret-with-cmek'; + // const kmsKeyName = 'projects/my-project/locations/global/keyRings/my-keyring/cryptoKeys/my-key'; + + // Import the Secret Manager library + const parent = `projects/${projectId}/locations/${locationId}`; + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager sever + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + // Instantiates a client + const client = new SecretManagerServiceClient(options); + + async function createRegionalSecretWithCmek() { + // Create the secret with automatic replication and CMEK + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + customerManagedEncryption: { + kmsKeyName: kmsKeyName, + }, + }, + }); + + console.log(`Created secret ${secret.name} with CMEK key ${kmsKeyName}`); + } + + createRegionalSecretWithCmek(); + // [END secretmanager_create_secret_with_cmek] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/createRegionalSecretWithExpiration.js b/secret-manager/regional_samples/createRegionalSecretWithExpiration.js new file mode 100644 index 0000000000..6bf434bbe4 --- /dev/null +++ b/secret-manager/regional_samples/createRegionalSecretWithExpiration.js @@ -0,0 +1,64 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, locationId, secretId) { + // [START secretmanager_create_regional_secret_with_expire_time] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const project = 'my-project'; + // const locationId = 'my-location'; + // const secretId = 'my-secret'; + + const parent = `projects/${projectId}/locations/${locationId}`; + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiates a client + const client = new SecretManagerServiceClient(options); + + async function createRegionalSecretWithExpiration() { + // Calculate expiration time (1 hour from now) + const expireTime = new Date(); + expireTime.setHours(expireTime.getHours() + 1); + + // Create the secret with automatic replication and expiration time + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + expireTime: { + seconds: Math.floor(expireTime.getTime() / 1000), + nanos: (expireTime.getTime() % 1000) * 1000000, + }, + }, + }); + + console.log( + `Created secret ${secret.name} with expiration time ${expireTime.toISOString()}` + ); + } + + createRegionalSecretWithExpiration(); + // [END secretmanager_create_regional_secret_with_expire_time] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/createRegionalSecretWithRotation.js b/secret-manager/regional_samples/createRegionalSecretWithRotation.js new file mode 100644 index 0000000000..b5907d45b6 --- /dev/null +++ b/secret-manager/regional_samples/createRegionalSecretWithRotation.js @@ -0,0 +1,81 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, secretId, locationId, topicName) { + // [START secretmanager_create_regional_secret_with_rotation] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const secretId = 'my-secret-with-rotation'; + // const locationId = 'us-central1'; + // const topicName = 'projects/my-project/topics/my-topic'; + + // Build the resource name of the parent project with location + const parent = `projects/${projectId}/locations/${locationId}`; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiate a client with the regional endpoint + const client = new SecretManagerServiceClient(options); + + async function createRegionalSecretWithRotation() { + // Set rotation period to 24 hours + const rotationPeriodHours = 24; + + // Calculate next rotation time (24 hours from now) + const nextRotationTime = new Date(); + nextRotationTime.setHours(nextRotationTime.getHours() + 24); + + // Create the secret with rotation configuration + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + topics: [ + { + name: topicName, + }, + ], + rotation: { + nextRotationTime: { + seconds: Math.floor(nextRotationTime.getTime() / 1000), + nanos: (nextRotationTime.getTime() % 1000) * 1000000, + }, + rotationPeriod: { + seconds: rotationPeriodHours * 3600, // Convert hours to seconds + nanos: 0, + }, + }, + }, + }); + + console.log( + `Created secret ${secret.name} with rotation period ${rotationPeriodHours} hours and topic ${topicName}` + ); + } + + await createRegionalSecretWithRotation(); + // [END secretmanager_create_regional_secret_with_rotation] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/createRegionalSecretWithTopic.js b/secret-manager/regional_samples/createRegionalSecretWithTopic.js new file mode 100644 index 0000000000..7912f3cc29 --- /dev/null +++ b/secret-manager/regional_samples/createRegionalSecretWithTopic.js @@ -0,0 +1,62 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, secretId, locationId, topicName) { + // [START secretmanager_create_regional_secret_with_topic] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const secretId = 'my-secret-with-topic'; + // const locationId = 'us-central1'; + // const topicName = 'projects/my-project/topics/my-topic'; + + // Build the resource name of the parent project with location + const parent = `projects/${projectId}/locations/${locationId}`; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiate a client with the regional endpoint + const client = new SecretManagerServiceClient(options); + + async function createRegionalSecretWithTopic() { + // Create the secret with a topic for notifications + const [secret] = await client.createSecret({ + parent: parent, + secretId: secretId, + secret: { + topics: [ + { + name: topicName, + }, + ], + }, + }); + + console.log(`Created secret ${secret.name} with topic ${topicName}`); + } + + createRegionalSecretWithTopic(); + // [END secretmanager_create_regional_secret_with_topic] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/deleteRegionalSecretExpiration.js b/secret-manager/regional_samples/deleteRegionalSecretExpiration.js new file mode 100644 index 0000000000..77c91e64e9 --- /dev/null +++ b/secret-manager/regional_samples/deleteRegionalSecretExpiration.js @@ -0,0 +1,59 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, secretId, locationId) { + // [START secretmanager_delete_regional_secret_expiration] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const secretId = 'my-secret'; + // const locationId = 'us-central1'; + + // Construct the secret name from the component parts + const name = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiate a client with the regional endpoint + const client = new SecretManagerServiceClient(options); + + async function deleteRegionalSecretExpiration() { + // Update the secret to remove the expiration time + const [secret] = await client.updateSecret({ + secret: { + name: name, + // No expireTime field specified, which will clear it + }, + updateMask: { + paths: ['expire_time'], + }, + }); + + console.log(`Removed expiration from secret ${secret.name}`); + } + + deleteRegionalSecretExpiration(); + // [END secretmanager_delete_regional_secret_expiration] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/deleteRegionalSecretRotation.js b/secret-manager/regional_samples/deleteRegionalSecretRotation.js new file mode 100644 index 0000000000..0972ea1609 --- /dev/null +++ b/secret-manager/regional_samples/deleteRegionalSecretRotation.js @@ -0,0 +1,58 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, secretId, locationId) { + // [START secretmanager_delete_regional_secret_rotation] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const secretId = 'my-secret-with-rotation'; + // const locationId = 'us-central1'; + + // Construct the secret name from the component parts + const name = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiate a client with the regional endpoint + const client = new SecretManagerServiceClient(options); + + async function deleteRegionalSecretRotation() { + // Update the secret to remove the rotation configuration + const [secret] = await client.updateSecret({ + secret: { + name: name, + }, + updateMask: { + paths: ['rotation'], + }, + }); + + console.log(`Removed rotation from secret ${secret.name}`); + } + + await deleteRegionalSecretRotation(); + // [END secretmanager_delete_regional_secret_rotation] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/detachRegionalTag.js b/secret-manager/regional_samples/detachRegionalTag.js new file mode 100644 index 0000000000..9049232711 --- /dev/null +++ b/secret-manager/regional_samples/detachRegionalTag.js @@ -0,0 +1,85 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, locationId, secretId, tagValue) { + // [START secretmanager_detach_regional_tag] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const locationId = 'us-central1'; + // const secretId = 'my-secret'; + // const tagValue = 'tagValues/123456789012'; + + // Import the Resource Manager library + const {TagBindingsClient} = require('@google-cloud/resource-manager').v3; + + // Set up the endpoint for the regional resource manager + const rmEndpoint = `${locationId}-cloudresourcemanager.googleapis.com`; + + // Create the Tag Bindings client with the regional endpoint + const tagBindingsClient = new TagBindingsClient({ + apiEndpoint: rmEndpoint, + }); + + // Build the resource name for the regional secret + const secretName = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Format the parent resource for the tag bindings request + const parent = `//secretmanager.googleapis.com/${secretName}`; + + async function detachRegionalTag() { + // Find the binding with the specified tag value + let bindingName = null; + const iterable = tagBindingsClient.listTagBindingsAsync( + { + parent: parent, + pageSize: 50, + }, + {autoPaginate: false} + ); + + for await (const binding of iterable) { + if (binding.tagValue === tagValue) { + bindingName = binding.name; + break; + } + } + + if (bindingName === null) { + console.log( + `Tag binding for value ${tagValue} not found on ${secretName}.` + ); + return; + } + + // Delete the tag binding + const [operation] = await tagBindingsClient.deleteTagBinding({ + name: bindingName, + }); + + // Wait for the operation to complete + await operation.promise(); + + console.log(`Detached tag value ${tagValue} from ${secretName}`); + } + + return detachRegionalTag(); + // [END secretmanager_detach_regional_tag] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/listRegionalSecretVersionsWithFilter.js b/secret-manager/regional_samples/listRegionalSecretVersionsWithFilter.js new file mode 100644 index 0000000000..4d5cb9a485 --- /dev/null +++ b/secret-manager/regional_samples/listRegionalSecretVersionsWithFilter.js @@ -0,0 +1,55 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, locationId, secretId) { + // [START secretmanager_list_regional_secret_versions_with_filter] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const locationId = 'my-location'; + // const secretId = 'my-secret'; + const filterStr = 'state=DISABLED'; + + const parent = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Imports the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager sever + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiates a client + const client = new SecretManagerServiceClient(options); + + async function listRegionalSecretVersionsWithFilter() { + const [versions] = await client.listSecretVersions({ + parent: parent, + filter: filterStr, + }); + + versions.forEach(version => { + console.log(`Found version: ${version.name}`); + }); + } + + listRegionalSecretVersionsWithFilter(); + // [END secretmanager_list_regional_secret_versions_with_filter] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/listRegionalSecretsWithFilter.js b/secret-manager/regional_samples/listRegionalSecretsWithFilter.js new file mode 100644 index 0000000000..0244ade587 --- /dev/null +++ b/secret-manager/regional_samples/listRegionalSecretsWithFilter.js @@ -0,0 +1,54 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, locationId) { + // [START secretmanager_list_regional_secrets_with_filter] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const locationId = 'my-location'; + const filterStr = 'labels.secretmanager=rocks'; + + const parent = `projects/${projectId}/locations/${locationId}`; + + // Imports the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager sever + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiates a client + const client = new SecretManagerServiceClient(options); + + async function listRegionalSecretsWithFilter() { + const [secrets] = await client.listSecrets({ + parent: parent, + filter: filterStr, + }); + + secrets.forEach(secret => { + console.log(`Found secret: ${secret.name}`); + }); + } + + listRegionalSecretsWithFilter(); + // [END secretmanager_list_regional_secrets_with_filter] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/listRegionalTagBindings.js b/secret-manager/regional_samples/listRegionalTagBindings.js new file mode 100644 index 0000000000..6bea108a0f --- /dev/null +++ b/secret-manager/regional_samples/listRegionalTagBindings.js @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, locationId, secretId) { + // [START secretmanager_list_regional_secret_tag_bindings] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const locationId = 'us-central1'; + // const secretId = 'my-regional-secret'; + + // Import the Resource Manager library + const {TagBindingsClient} = require('@google-cloud/resource-manager').v3; + + // Set up the endpoint for the regional resource manager + const rmEndpoint = `${locationId}-cloudresourcemanager.googleapis.com`; + + // Create the Tag Bindings client with the regional endpoint + const tagBindingsClient = new TagBindingsClient({ + apiEndpoint: rmEndpoint, + }); + + // Build the resource name for the regional secret + const name = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Format the parent resource for the tag bindings request + const parent = `//secretmanager.googleapis.com/${name}`; + + async function listRegionalSecretTagBindings() { + console.log(`Tag bindings for ${name}:`); + let foundBindings = false; + + // List the tag bindings + const iterable = tagBindingsClient.listTagBindingsAsync( + { + parent: parent, + pageSize: 10, + }, + {autoPaginate: false} + ); + + // Iterate through the results + for await (const binding of iterable) { + console.log(`- Tag Value: ${binding.tagValue}`); + foundBindings = true; + } + + if (!foundBindings) { + console.log(`No tag bindings found for ${name}.`); + } + } + + listRegionalSecretTagBindings(); + // [END secretmanager_list_regional_secret_tag_bindings] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/updateRegionalSecretExpiration.js b/secret-manager/regional_samples/updateRegionalSecretExpiration.js new file mode 100644 index 0000000000..cfa1f8282f --- /dev/null +++ b/secret-manager/regional_samples/updateRegionalSecretExpiration.js @@ -0,0 +1,68 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, secretId, locationId) { + // [START secretmanager_update_regional_secret_expiration] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const secretId = 'my-secret'; + // const locationId = 'us-central1'; + + // Construct the secret name from the component parts + const name = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Imports the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiates a client with the regional endpoint + const client = new SecretManagerServiceClient(options); + + async function updateRegionalSecretExpiration() { + // Calculate new expiration time (2 hours from now) + const newExpireTime = new Date(); + newExpireTime.setHours(newExpireTime.getHours() + 2); + + // Update the secret with the new expiration time + const [secret] = await client.updateSecret({ + secret: { + name: name, + expireTime: { + seconds: Math.floor(newExpireTime.getTime() / 1000), + nanos: (newExpireTime.getTime() % 1000) * 1000000, + }, + }, + updateMask: { + paths: ['expire_time'], + }, + }); + + console.log( + `Updated secret ${secret.name} expiration time to ${newExpireTime.toISOString()}` + ); + } + + updateRegionalSecretExpiration(); + // [END secretmanager_update_regional_secret_expiration] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/regional_samples/updateRegionalSecretRotation.js b/secret-manager/regional_samples/updateRegionalSecretRotation.js new file mode 100644 index 0000000000..dc17899550 --- /dev/null +++ b/secret-manager/regional_samples/updateRegionalSecretRotation.js @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(projectId, secretId, locationId) { + // [START secretmanager_update_regional_secret_rotation_period] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const projectId = 'my-project'; + // const secretId = 'my-secret'; + // const locationId = 'us-central1'; + + // Construct the secret name from the component parts + const name = `projects/${projectId}/locations/${locationId}/secrets/${secretId}`; + + // Set updated rotation period to 48 hours + const newRotationPeriodHours = 48; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Adding the endpoint to call the regional secret manager server + const options = {}; + options.apiEndpoint = `secretmanager.${locationId}.rep.googleapis.com`; + + // Instantiate a client with the regional endpoint + const client = new SecretManagerServiceClient(options); + + async function updateRegionalSecretRotationPeriod() { + // Update the secret with the new rotation period + const [result] = await client.updateSecret({ + secret: { + name: name, + rotation: { + rotationPeriod: { + seconds: newRotationPeriodHours * 3600, // Convert hours to seconds + nanos: 0, + }, + }, + }, + updateMask: { + paths: ['rotation.rotation_period'], + }, + }); + + // Get the rotation period in hours for display + const rotationHours = result.rotation.rotationPeriod.seconds / 3600; + + console.log( + `Updated secret ${result.name} rotation period to ${rotationHours} hours` + ); + } + + updateRegionalSecretRotationPeriod(); + // [END secretmanager_update_regional_secret_rotation_period] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index f7a3ad684b..6d7c9c0321 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -27,6 +27,9 @@ const resourcemanagerTagValueClient = new TagValuesClient(); let projectId; const locationId = process.env.GCLOUD_LOCATION || 'us-central1'; +const kmsKeyName = process.env.GOOGLE_CLOUD_KMS_KEY_NAME; +const regionalKmsKeyName = process.env.GOOGLE_CLOUD_REGIONAL_KMS_KEY_NAME; +const topicName = process.env.GOOGLE_CLOUD_TOPIC_NAME; const secretId = v4(); const payload = 'my super secret data'; const iamUser = 'user:sethvargo@google.com'; @@ -319,6 +322,97 @@ describe('Secret Manager samples', () => { throw err; } } + + try { + await client.deleteSecret({ + name: `${secret.name}-ummr`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await client.deleteSecret({ + name: `${secret.name}-cmek`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-cmek`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await client.deleteSecret({ + name: `${secret.name}-expiry`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-expiry`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await client.deleteSecret({ + name: `${secret.name}-rotation`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-rotation`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await client.deleteSecret({ + name: `${secret.name}-topic`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + + try { + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-topic`, + }); + } catch (err) { + if (!err.message.includes('NOT_FOUND')) { + throw err; + } + } + // Wait for 20 seconds before deleting the tag value await new Promise(resolve => setTimeout(resolve, 20000)); const [deleteValueOperation] = @@ -819,4 +913,521 @@ describe('Secret Manager samples', () => { ); assert.match(output, new RegExp('Created Tag Binding')); }); + + it('lists secrets with filter', async () => { + const output = execSync(`node listSecretsWithFilter.js ${projectId}`); + assert.match(output, new RegExp(`Found secret: ${secret.name}`)); + }); + + it('lists regional secrets with filter', async () => { + const output = execSync( + `node regional_samples/listRegionalSecretsWithFilter.js ${projectId} ${locationId}` + ); + assert.match(output, new RegExp(`Found secret: ${regionalSecret.name}`)); + }); + + it('list secret versions with filter', async () => { + const [versionForFilter] = await client.addSecretVersion({ + parent: secret.name, + payload: { + data: Buffer.from(payload), + }, + }); + + await client.disableSecretVersion({ + name: versionForFilter.name, + }); + const output = execSync( + `node listSecretVersionsWithFilter.js ${secret.name}` + ); + assert.match(output, new RegExp(`Found version: ${versionForFilter.name}`)); + }); + + it('list regional secret versions with filter', async () => { + const [regionalVersionForFilter] = await regionalClient.addSecretVersion({ + parent: regionalSecret.name, + payload: { + data: Buffer.from(payload), + }, + }); + await regionalClient.disableSecretVersion({ + name: regionalVersionForFilter.name, + }); + const output = execSync( + `node regional_samples/listRegionalSecretVersionsWithFilter.js ${projectId} ${locationId} ${secretId}` + ); + assert.match( + output, + new RegExp(`Found version: ${regionalVersionForFilter.name}`) + ); + }); + + it('lists tag bindings', async () => { + await client.createSecret({ + parent: `projects/${projectId}`, + secretId: `${secretId}-tag-binding`, + secret: { + replication: { + automatic: {}, + }, + tags: { + [tagKey]: tagValue, + }, + }, + }); + const output = execSync( + `node listTagBindings.js ${secret.name}-tag-binding` + ); + assert.match( + output, + new RegExp(`Tag bindings for ${secret.name}-tag-binding:`) + ); + assert.match(output, new RegExp(`- Tag Value: ${tagValue}`)); + await client.deleteSecret({ + name: `${secret.name}-tag-binding`, + }); + }); + + it('lists regional tag bindings', async () => { + const parent = `projects/${projectId}/locations/${locationId}`; + await regionalClient.createSecret({ + parent: parent, + secretId: `${secretId}-regional-tag-binding`, + secret: { + tags: { + [tagKey]: tagValue, + }, + }, + }); + const output = execSync( + `node regional_samples/listRegionalTagBindings.js ${projectId} ${locationId} ${secretId}-regional-tag-binding` + ); + assert.match( + output, + new RegExp( + `Tag bindings for ${parent}/secrets/${secretId}-regional-tag-binding:` + ) + ); + assert.match(output, new RegExp(`- Tag Value: ${tagValue}`)); + await regionalClient.deleteSecret({ + name: `${parent}/secrets/${secretId}-regional-tag-binding`, + }); + }); + + it('detach tag bindings', async () => { + await client.createSecret({ + parent: `projects/${projectId}`, + secretId: `${secretId}-detach-tag-binding`, + secret: { + replication: { + automatic: {}, + }, + tags: { + [tagKey]: tagValue, + }, + }, + }); + const output = execSync( + `node detachTagBinding.js ${secret.name}-detach-tag-binding ${tagValue}` + ); + assert.match( + output, + new RegExp( + `Detached tag value ${tagValue} from ${secret.name}-detach-tag-binding` + ) + ); + await client.deleteSecret({ + name: `${secret.name}-detach-tag-binding`, + }); + }); + + it('detach tags from regional secrets', async () => { + const parent = `projects/${projectId}/locations/${locationId}`; + await regionalClient.createSecret({ + parent: parent, + secretId: `${secretId}-detach-regional-tag-binding`, + secret: { + tags: { + [tagKey]: tagValue, + }, + }, + }); + const output = execSync( + `node regional_samples/detachRegionalTag.js ${projectId} ${locationId} ${secretId}-detach-regional-tag-binding ${tagValue}` + ); + assert.match( + output, + new RegExp( + `Detached tag value ${tagValue} from ${parent}/secrets/${secretId}-detach-regional-tag-binding` + ) + ); + await regionalClient.deleteSecret({ + name: `${parent}/secrets/${secretId}-detach-regional-tag-binding`, + }); + }); + + it('create secret with user managed replication policy', async () => { + const parent = `projects/${projectId}`; + const locations = ['us-east1', 'us-east5']; + const ttl = 900; + const output = execSync( + `node createSecretWithUserManagedReplicationPolicy.js ${parent} ${secretId}-ummr ${locations} ${ttl}` + ); + assert.match(output, new RegExp(`Created secret: ${secret.name}`)); + }); + + it('create secret with customer managed enc key', async () => { + const parent = `projects/${projectId}`; + const output = execSync( + `node createSecretWithCmek.js ${parent} ${secretId}-cmek ${kmsKeyName}` + ); + assert.match( + output, + new RegExp( + `Created secret ${secret.name}-cmek with CMEK key ${kmsKeyName}` + ) + ); + }); + + it('create regional secret with customer managed enc key', async () => { + const output = execSync( + `node regional_samples/createRegionalSecretWithCmek.js ${projectId} ${locationId} ${secretId}-regional-cmek ${regionalKmsKeyName}` + ); + assert.match( + output, + new RegExp( + `Created secret ${regionalSecret.name}-regional-cmek with CMEK key ${regionalKmsKeyName}` + ) + ); + }); + + it('create secret with expiry time set', async () => { + const parent = `projects/${projectId}`; + const output = execSync( + `node createSecretWithExpiration.js ${parent} ${secretId}-expiry` + ); + assert.match(output, new RegExp(`Created secret ${secret.name}-expiry`)); + }); + + it('create regional secret with expiry time set', async () => { + const output = execSync( + `node regional_samples/createRegionalSecretWithExpiration.js ${projectId} ${locationId} ${secretId}-regional-expiry` + ); + assert.match( + output, + new RegExp(`Created secret ${regionalSecret.name}-regional-expiry`) + ); + }); + + it('update secret with new expiry time', async () => { + const parent = `projects/${projectId}`; + const expireTime = new Date(); + expireTime.setHours(expireTime.getHours() + 1); + await client.createSecret({ + parent: parent, + secretId: `${secretId}-update-expiry`, + secret: { + replication: { + automatic: {}, + }, + expireTime: { + seconds: Math.floor(expireTime.getTime() / 1000), + nanos: (expireTime.getTime() % 1000) * 1000000, + }, + }, + }); + const output = execSync( + `node updateSecretExpiration.js ${parent}/secrets/${secretId}-update-expiry` + ); + assert.match( + output, + new RegExp(`Updated secret ${secret.name}-update-expiry`) + ); + await client.deleteSecret({ + name: `${secret.name}-update-expiry`, + }); + }); + + it('update regional secret with new expiry time', async () => { + const parent = `projects/${projectId}/locations/${locationId}`; + const expireTime = new Date(); + expireTime.setHours(expireTime.getHours() + 1); + await regionalClient.createSecret({ + parent: parent, + secretId: `${secretId}-regional-update-expiry`, + secret: { + expireTime: { + seconds: Math.floor(expireTime.getTime() / 1000), + nanos: (expireTime.getTime() % 1000) * 1000000, + }, + }, + }); + const output = execSync( + `node regional_samples/updateRegionalSecretExpiration.js ${projectId} ${secretId}-regional-update-expiry ${locationId}` + ); + assert.match( + output, + new RegExp(`Updated secret ${regionalSecret.name}-regional-update-expiry`) + ); + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-update-expiry`, + }); + }); + + it('delete secret expiry time', async () => { + const parent = `projects/${projectId}`; + const expireTime = new Date(); + expireTime.setHours(expireTime.getHours() + 1); + await client.createSecret({ + parent: parent, + secretId: `${secretId}-delete-expiry`, + secret: { + replication: { + automatic: {}, + }, + expireTime: { + seconds: Math.floor(expireTime.getTime() / 1000), + nanos: (expireTime.getTime() % 1000) * 1000000, + }, + }, + }); + const output = execSync( + `node deleteSecretExpiration.js ${parent}/secrets/${secretId}-delete-expiry` + ); + assert.match( + output, + new RegExp(`Removed expiration from secret ${secret.name}`) + ); + await client.deleteSecret({ + name: `${secret.name}-delete-expiry`, + }); + }); + + it('delete regional secret expiry time', async () => { + const parent = `projects/${projectId}/locations/${locationId}`; + const expireTime = new Date(); + expireTime.setHours(expireTime.getHours() + 1); + await regionalClient.createSecret({ + parent: parent, + secretId: `${secretId}-regional-delete-expiry`, + secret: { + expireTime: { + seconds: Math.floor(expireTime.getTime() / 1000), + nanos: (expireTime.getTime() % 1000) * 1000000, + }, + }, + }); + const output = execSync( + `node regional_samples/deleteRegionalSecretExpiration.js ${projectId} ${secretId}-regional-delete-expiry ${locationId}` + ); + assert.match( + output, + new RegExp( + `Removed expiration from secret ${regionalSecret.name}-regional-delete-expiry` + ) + ); + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-delete-expiry`, + }); + }); + + it('create secret with rotation time set', async () => { + const parent = `projects/${projectId}`; + const output = execSync( + `node createSecretWithRotation.js ${parent} ${secretId}-rotation ${topicName}` + ); + assert.match(output, new RegExp(`Created secret ${secret.name}-rotation`)); + }); + + it('create regional secret with rotation time set', async () => { + const output = execSync( + `node regional_samples/createRegionalSecretWithRotation.js ${projectId} ${secretId}-regional-rotation ${locationId} ${topicName}` + ); + assert.match( + output, + new RegExp(`Created secret ${regionalSecret.name}-regional-rotation`) + ); + }); + + it('update secret with new rotation time', async () => { + const parent = `projects/${projectId}`; + const rotationPeriodHours = 24; + const nextRotationTime = new Date(); + nextRotationTime.setHours(nextRotationTime.getHours() + 24); + await client.createSecret({ + parent: parent, + secretId: `${secretId}-update-rotation`, + secret: { + replication: { + automatic: {}, + }, + topics: [ + { + name: topicName, + }, + ], + rotation: { + nextRotationTime: { + seconds: Math.floor(nextRotationTime.getTime() / 1000), + nanos: (nextRotationTime.getTime() % 1000) * 1000000, + }, + rotationPeriod: { + seconds: rotationPeriodHours * 3600, + nanos: 0, + }, + }, + }, + }); + const output = execSync( + `node updateSecretRotation.js ${parent}/secrets/${secretId}-update-rotation` + ); + assert.match( + output, + new RegExp(`Updated secret ${secret.name}-update-rotation`) + ); + await client.deleteSecret({ + name: `${secret.name}-update-rotation`, + }); + }); + + it('update regional secret with new rotation time', async () => { + const parent = `projects/${projectId}/locations/${locationId}`; + const rotationPeriodHours = 24; + const nextRotationTime = new Date(); + nextRotationTime.setHours(nextRotationTime.getHours() + 24); + + await regionalClient.createSecret({ + parent: parent, + secretId: `${secretId}-regional-update-rotation`, + secret: { + topics: [ + { + name: topicName, + }, + ], + rotation: { + nextRotationTime: { + seconds: Math.floor(nextRotationTime.getTime() / 1000), + nanos: (nextRotationTime.getTime() % 1000) * 1000000, + }, + rotationPeriod: { + seconds: rotationPeriodHours * 3600, // Convert hours to seconds + nanos: 0, + }, + }, + }, + }); + const output = execSync( + `node regional_samples/updateRegionalSecretRotation.js ${projectId} ${secretId}-regional-update-rotation ${locationId}` + ); + assert.match( + output, + new RegExp( + `Updated secret ${regionalSecret.name}-regional-update-rotation` + ) + ); + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-update-rotation`, + }); + }); + + it('delete secret rotation time', async () => { + const parent = `projects/${projectId}`; + const rotationPeriodHours = 24; + const nextRotationTime = new Date(); + nextRotationTime.setHours(nextRotationTime.getHours() + 24); + await client.createSecret({ + parent: parent, + secretId: `${secretId}-delete-rotation`, + secret: { + replication: { + automatic: {}, + }, + topics: [ + { + name: topicName, + }, + ], + rotation: { + nextRotationTime: { + seconds: Math.floor(nextRotationTime.getTime() / 1000), + nanos: (nextRotationTime.getTime() % 1000) * 1000000, + }, + rotationPeriod: { + seconds: rotationPeriodHours * 3600, + nanos: 0, + }, + }, + }, + }); + const output = execSync( + `node deleteSecretRotation.js ${parent}/secrets/${secretId}-delete-rotation` + ); + assert.match( + output, + new RegExp(`Removed rotation from secret ${secret.name}`) + ); + await client.deleteSecret({ + name: `${secret.name}-delete-rotation`, + }); + }); + + it('delete regional secret rotation time', async () => { + const parent = `projects/${projectId}/locations/${locationId}`; + const rotationPeriodHours = 24; + const nextRotationTime = new Date(); + nextRotationTime.setHours(nextRotationTime.getHours() + 24); + + await regionalClient.createSecret({ + parent: parent, + secretId: `${secretId}-regional-delete-rotation`, + secret: { + topics: [ + { + name: topicName, + }, + ], + rotation: { + nextRotationTime: { + seconds: Math.floor(nextRotationTime.getTime() / 1000), + nanos: (nextRotationTime.getTime() % 1000) * 1000000, + }, + rotationPeriod: { + seconds: rotationPeriodHours * 3600, // Convert hours to seconds + nanos: 0, + }, + }, + }, + }); + const output = execSync( + `node regional_samples/deleteRegionalSecretRotation.js ${projectId} ${secretId}-regional-delete-rotation ${locationId}` + ); + assert.match( + output, + new RegExp( + `Removed rotation from secret ${regionalSecret.name}-regional-delete-rotation` + ) + ); + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-delete-rotation`, + }); + }); + + it('create secret with topic set', async () => { + const parent = `projects/${projectId}`; + const output = execSync( + `node createSecretWithTopic.js ${parent} ${secretId}-topic ${topicName}` + ); + assert.match(output, new RegExp(`Created secret ${secret.name}-topic`)); + }); + + it('create regional secret with topic set', async () => { + const output = execSync( + `node regional_samples/createRegionalSecretWithTopic.js ${projectId} ${secretId}-regional-topic ${locationId} ${topicName}` + ); + assert.match( + output, + new RegExp(`Created secret ${regionalSecret.name}-regional-topic`) + ); + }); }); diff --git a/secret-manager/updateSecretExpiration.js b/secret-manager/updateSecretExpiration.js new file mode 100644 index 0000000000..d304fcdeb0 --- /dev/null +++ b/secret-manager/updateSecretExpiration.js @@ -0,0 +1,60 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(name = 'projects/my-project/secrets/my-secret') { + // [START secretmanager_update_secret_expiration] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const name = 'projects/my-project/secrets/my-secret'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function updateSecretExpiration() { + // Calculate new expiration time (2 hours from now) + const newExpireTime = new Date(); + newExpireTime.setHours(newExpireTime.getHours() + 2); + + // Update the expire_time + const [response] = await client.updateSecret({ + secret: { + name: name, + expireTime: { + seconds: Math.floor(newExpireTime.getTime() / 1000), + nanos: (newExpireTime.getTime() % 1000) * 1000000, + }, + }, + updateMask: { + paths: ['expire_time'], + }, + }); + + // Print the updated secret name + console.log( + `Updated secret ${response.name} expiration time to ${newExpireTime.toISOString()}` + ); + } + + updateSecretExpiration(); + // [END secretmanager_update_secret_expiration] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error); diff --git a/secret-manager/updateSecretRotation.js b/secret-manager/updateSecretRotation.js new file mode 100644 index 0000000000..37f2b01cab --- /dev/null +++ b/secret-manager/updateSecretRotation.js @@ -0,0 +1,62 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function main(name = 'projects/my-project/secrets/my-secret') { + // [START secretmanager_update_secret_rotation] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const name = 'projects/my-project/secrets/my-secret'; + + // Import the Secret Manager library + const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); + + // Create the Secret Manager client + const client = new SecretManagerServiceClient(); + + async function updateSecretRotation() { + // Set new rotation period to 48 hours + const newRotationPeriodHours = 48; + + // Update the rotation period + const [result] = await client.updateSecret({ + secret: { + name: name, + rotation: { + rotationPeriod: { + seconds: newRotationPeriodHours * 3600, // Convert hours to seconds + nanos: 0, + }, + }, + }, + updateMask: { + paths: ['rotation.rotation_period'], + }, + }); + + // Calculate rotation hours from the result for display + const rotationHours = result.rotation.rotationPeriod.seconds / 3600; + console.log( + `Updated secret ${result.name} rotation period to ${rotationHours} hours` + ); + } + + updateSecretRotation(); + // [END secretmanager_update_secret_rotation] +} + +const args = process.argv.slice(2); +main(...args).catch(console.error);