From 4afb60ed8659c3cbd8df3541c3a4ef2cd548e1f8 Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Tue, 20 Jan 2026 11:54:59 +0530 Subject: [PATCH 1/7] feat(secretmanager): Adding tags samples --- secret-manager/detachTagBinding.js | 72 ++++++++++ .../listSecretVersionsWithFilter.js | 47 ++++++ secret-manager/listSecretsWithFilter.js | 52 +++++++ secret-manager/listTagBindings.js | 63 ++++++++ .../regional_samples/detachRegionalTag.js | 85 +++++++++++ .../listRegionalSecretVersionsWithFilter.js | 55 +++++++ .../listRegionalSecretsWithFilter.js | 54 +++++++ .../listRegionalTagBindings.js | 72 ++++++++++ secret-manager/test/secretmanager.test.js | 136 ++++++++++++++++++ 9 files changed, 636 insertions(+) create mode 100644 secret-manager/detachTagBinding.js create mode 100644 secret-manager/listSecretVersionsWithFilter.js create mode 100644 secret-manager/listSecretsWithFilter.js create mode 100644 secret-manager/listTagBindings.js create mode 100644 secret-manager/regional_samples/detachRegionalTag.js create mode 100644 secret-manager/regional_samples/listRegionalSecretVersionsWithFilter.js create mode 100644 secret-manager/regional_samples/listRegionalSecretsWithFilter.js create mode 100644 secret-manager/regional_samples/listRegionalTagBindings.js 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/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/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index f7a3ad684b..d4197e0969 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -819,4 +819,140 @@ 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 () => { + await client.disableSecretVersion({ + name: version.name, + }); + const output = execSync( + `node listSecretVersionsWithFilter.js ${secret.name}` + ); + assert.match(output, new RegExp(`Found version: ${version.name}`)); + }); + + it('list regional secret versions with filter', async () => { + await regionalClient.disableSecretVersion({ + name: regionalVersion.name, + }); + const output = execSync( + `node regional_samples/listRegionalSecretVersionsWithFilter.js ${projectId} ${locationId} ${secretId}` + ); + assert.match(output, new RegExp(`Found version: ${regionalVersion.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`, + }); + }); }); From 0a6a9aafc60364d602c265f3bf940fd323ad5a97 Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Tue, 20 Jan 2026 12:40:06 +0530 Subject: [PATCH 2/7] feat(secretmanager): Add secret version --- secret-manager/test/secretmanager.test.js | 24 +++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/secret-manager/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index d4197e0969..dc1f890bf1 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -833,23 +833,39 @@ describe('Secret Manager samples', () => { }); it('list secret versions with filter', async () => { + const [versionForFilter] = await client.addSecretVersion({ + parent: secret.name, + payload: { + data: Buffer.from(payload), + }, + }); + await client.disableSecretVersion({ - name: version.name, + name: versionForFilter.name, }); const output = execSync( `node listSecretVersionsWithFilter.js ${secret.name}` ); - assert.match(output, new RegExp(`Found version: ${version.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: regionalVersion.name, + name: regionalVersionForFilter.name, }); const output = execSync( `node regional_samples/listRegionalSecretVersionsWithFilter.js ${projectId} ${locationId} ${secretId}` ); - assert.match(output, new RegExp(`Found version: ${regionalVersion.name}`)); + assert.match( + output, + new RegExp(`Found version: ${regionalVersionForFilter.name}`) + ); }); it('lists tag bindings', async () => { From b9b8b80a2e2eb55b0c1088de71c57b6ecc0d9c02 Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Tue, 20 Jan 2026 16:17:38 +0530 Subject: [PATCH 3/7] feat(secretmanager): Adding cmek samples --- secret-manager/createSecretWithCmek.js | 56 +++++++++++++++++ ...eSecretWithUserManagedReplicationPolicy.js | 62 +++++++++++++++++++ .../createRegionalSecretWithCmek.js | 57 +++++++++++++++++ secret-manager/test/secretmanager.test.js | 58 +++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 secret-manager/createSecretWithCmek.js create mode 100644 secret-manager/createSecretWithUserManagedReplicationPolicy.js create mode 100644 secret-manager/regional_samples/createRegionalSecretWithCmek.js 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/createSecretWithUserManagedReplicationPolicy.js b/secret-manager/createSecretWithUserManagedReplicationPolicy.js new file mode 100644 index 0000000000..b7f24cd551 --- /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 = null) { + // [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 = '7776000s'; // 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/regional_samples/createRegionalSecretWithCmek.js b/secret-manager/regional_samples/createRegionalSecretWithCmek.js new file mode 100644 index 0000000000..74822ea59f --- /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 = 'projects'; + // 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/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index dc1f890bf1..b646843560 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -27,6 +27,8 @@ 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 secretId = v4(); const payload = 'my super secret data'; const iamUser = 'user:sethvargo@google.com'; @@ -319,6 +321,37 @@ 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 client.deleteSecret({ + name: `${secret.name}-regional-cmek`, + }); + } 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] = @@ -971,4 +1004,29 @@ describe('Secret Manager samples', () => { 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 = '900s'; + 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(`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(`CMEK key ${regionalKmsKeyName}`)); + }); }); From 4512af61d81c07c8e003b04c044a3a5b29f61214 Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Tue, 20 Jan 2026 16:30:13 +0530 Subject: [PATCH 4/7] feat(secretmanager): Add secret version --- secret-manager/test/secretmanager.test.js | 24 +++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/secret-manager/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index d4197e0969..dc1f890bf1 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -833,23 +833,39 @@ describe('Secret Manager samples', () => { }); it('list secret versions with filter', async () => { + const [versionForFilter] = await client.addSecretVersion({ + parent: secret.name, + payload: { + data: Buffer.from(payload), + }, + }); + await client.disableSecretVersion({ - name: version.name, + name: versionForFilter.name, }); const output = execSync( `node listSecretVersionsWithFilter.js ${secret.name}` ); - assert.match(output, new RegExp(`Found version: ${version.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: regionalVersion.name, + name: regionalVersionForFilter.name, }); const output = execSync( `node regional_samples/listRegionalSecretVersionsWithFilter.js ${projectId} ${locationId} ${secretId}` ); - assert.match(output, new RegExp(`Found version: ${regionalVersion.name}`)); + assert.match( + output, + new RegExp(`Found version: ${regionalVersionForFilter.name}`) + ); }); it('lists tag bindings', async () => { From de18831834cf297b43f82e47dd2dc1287fbad5dd Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Tue, 20 Jan 2026 18:14:56 +0530 Subject: [PATCH 5/7] feat(secretmanager): Update formatting --- secret-manager/createSecretWithUserManagedReplicationPolicy.js | 2 +- secret-manager/regional_samples/createRegionalSecretWithCmek.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/secret-manager/createSecretWithUserManagedReplicationPolicy.js b/secret-manager/createSecretWithUserManagedReplicationPolicy.js index b7f24cd551..3b901451b8 100644 --- a/secret-manager/createSecretWithUserManagedReplicationPolicy.js +++ b/secret-manager/createSecretWithUserManagedReplicationPolicy.js @@ -14,7 +14,7 @@ 'use strict'; -async function main(parent, secretId, locations, ttl = null) { +async function main(parent, secretId, locations, ttl) { // [START secretmanager_create_ummr_secret] /** * TODO(developer): Uncomment these variables before running the sample. diff --git a/secret-manager/regional_samples/createRegionalSecretWithCmek.js b/secret-manager/regional_samples/createRegionalSecretWithCmek.js index 74822ea59f..a7157255e8 100644 --- a/secret-manager/regional_samples/createRegionalSecretWithCmek.js +++ b/secret-manager/regional_samples/createRegionalSecretWithCmek.js @@ -19,7 +19,7 @@ async function main(projectId, locationId, secretId, kmsKeyName) { /** * TODO(developer): Uncomment these variables before running the sample. */ - // const projectId = 'projects'; + // 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'; From 8fe44181f9b35723ab7d939f39840757271abe82 Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Wed, 21 Jan 2026 11:50:11 +0530 Subject: [PATCH 6/7] feat(secretmanager): Update formatting --- secret-manager/test/secretmanager.test.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/secret-manager/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index b646843560..9d56a16493 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -343,8 +343,8 @@ describe('Secret Manager samples', () => { } try { - await client.deleteSecret({ - name: `${secret.name}-regional-cmek`, + await regionalClient.deleteSecret({ + name: `${regionalSecret.name}-regional-cmek`, }); } catch (err) { if (!err.message.includes('NOT_FOUND')) { @@ -1020,13 +1020,23 @@ describe('Secret Manager samples', () => { const output = execSync( `node createSecretWithCmek.js ${parent} ${secretId}-cmek ${kmsKeyName}` ); - assert.match(output, new RegExp(`CMEK key ${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(`CMEK key ${regionalKmsKeyName}`)); + assert.match( + output, + new RegExp( + `Created secret ${regionalSecret.name}-regional-cmek with CMEK key ${regionalKmsKeyName}` + ) + ); }); }); From a11e7acbe4a8bea713c31c0a863c4388483f249f Mon Sep 17 00:00:00 2001 From: khilan maradiya Date: Wed, 21 Jan 2026 21:23:48 +0530 Subject: [PATCH 7/7] feat(secretmanager): updating as per comment --- secret-manager/createSecretWithUserManagedReplicationPolicy.js | 2 +- secret-manager/test/secretmanager.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/secret-manager/createSecretWithUserManagedReplicationPolicy.js b/secret-manager/createSecretWithUserManagedReplicationPolicy.js index 3b901451b8..720d480c21 100644 --- a/secret-manager/createSecretWithUserManagedReplicationPolicy.js +++ b/secret-manager/createSecretWithUserManagedReplicationPolicy.js @@ -22,7 +22,7 @@ async function main(parent, secretId, locations, ttl) { // const parent = 'projects/my-project'; // const secretId = 'my-new-secret'; // const locations = ['us-east1', 'europe-west1']; - // const ttl = '7776000s'; // Optional: 90 days in seconds + // const ttl = 7776000; // Optional: 90 days in seconds // Import the Secret Manager library const {SecretManagerServiceClient} = require('@google-cloud/secret-manager'); diff --git a/secret-manager/test/secretmanager.test.js b/secret-manager/test/secretmanager.test.js index 9d56a16493..0816a533b4 100644 --- a/secret-manager/test/secretmanager.test.js +++ b/secret-manager/test/secretmanager.test.js @@ -1008,7 +1008,7 @@ describe('Secret Manager samples', () => { it('create secret with user managed replication policy', async () => { const parent = `projects/${projectId}`; const locations = ['us-east1', 'us-east5']; - const ttl = '900s'; + const ttl = 900; const output = execSync( `node createSecretWithUserManagedReplicationPolicy.js ${parent} ${secretId}-ummr ${locations} ${ttl}` );