From ac23a730e0c2ba4b120c082c3369663b76c2278d Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Mon, 22 Nov 2021 14:38:50 -0800 Subject: [PATCH] [App Config] Secret reference sample update (#18775) * secret reference sample updated * remove comment * update comments * Maor's feedback * try catch error from sample * remove and --- .../samples-dev/secretReference.ts | 110 ++++++++++++------ .../samples/v1/javascript/secretReference.js | 110 ++++++++++++------ .../v1/typescript/src/secretReference.ts | 110 ++++++++++++------ .../test/public/secretReference.spec.ts | 2 +- 4 files changed, 220 insertions(+), 112 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts b/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts index 616fc5391b19..1b87147dba2b 100644 --- a/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts +++ b/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts @@ -13,7 +13,7 @@ import { ConfigurationSetting, parseSecretReference } from "@azure/app-configuration"; -import { SecretClient } from "@azure/keyvault-secrets"; +import { parseKeyVaultSecretIdentifier, SecretClient } from "@azure/keyvault-secrets"; import { DefaultAzureCredential } from "@azure/identity"; // Load the .env file if it exists @@ -22,67 +22,103 @@ dotenv.config(); export async function main() { console.log(`Running secretReference sample`); - const secretReference: ConfigurationSetting = { - key: `secret${new Date().getTime()}`, - value: { - secretId: `secret-key${Math.ceil(100 + Math.random() * 900)}` - }, - isReadOnly: false, - contentType: secretReferenceContentType - }; + const key = `secret${new Date().getTime()}`; + + // setup method creates + // - a secret using `@azure/keyvault-secrets` + // - a corresponding secret reference config setting with `@azure/app-configuration` + await setup(key); + + console.log(`Get the added secretReference from App Config with key: ${key}`); + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + const getResponse = await appConfigClient.getConfigurationSetting({ + key + }); + // You can use the `isSecretReference` global method to check if the content type is secretReferenceContentType ("application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8") + + const parsedSecretReference = parseSecretReference(getResponse); + + // Get the name and vaultUrl from the secretId + const { name: secretName, vaultUrl } = parseKeyVaultSecretIdentifier( + parsedSecretReference.value.secretId + ); + + const secretClient = new SecretClient(vaultUrl, new DefaultAzureCredential()); + try { + // Read the secret we created + const secret = await secretClient.getSecret(secretName); + console.log(`Get the secret from keyvault key: ${secretName}, value: ${secret.value}`); + } catch (err) { + const error = err as { code: string; statusCode: number }; + if (error.code === "SecretNotFound" && error.statusCode === 404) { + throw new Error( + `\n Secret is not found, make sure the secret ${parsedSecretReference.value.secretId} is present in your keyvault account;\n Original error - ${error}` + ); + } else { + throw err; + } + } + + console.log(`Deleting the secret from keyvault`); + await secretClient.beginDeleteSecret(secretName); + + await cleanupSampleValues([key], appConfigClient); +} + +async function setup(key: string) { if ( !process.env["AZURE_TENANT_ID"] || !process.env["AZURE_CLIENT_ID"] || !process.env["AZURE_CLIENT_SECRET"] || - !process.env["KEYVAULT_URI"] + !process.env["KEYVAULT_URI"] || + !process.env["APPCONFIG_CONNECTION_STRING"] ) { console.log( - `At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and KEYVAULT_URI variables is not present, + `At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, APPCONFIG_CONNECTION_STRING and KEYVAULT_URI variables is not present, please add the missing ones in your environment and rerun the sample.` ); return; } + // DefaultAzureCredential expects the following three environment variables: // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant // - AZURE_CLIENT_SECRET: The client secret for the registered application - const credential = new DefaultAzureCredential(); - const url = process.env["KEYVAULT_URI"] || ""; - - const secretClient = new SecretClient(url, credential); + const secretClient = new SecretClient(process.env["KEYVAULT_URI"], new DefaultAzureCredential()); + const secretName = `secret-${Date.now()}`; // Create a secret - console.log( - `Create a keyvault secret with key: ${secretReference.value.secretId} and value: "MySecretValue"` - ); - await secretClient.setSecret(secretReference.value.secretId, "MySecretValue"); + console.log(`Create a keyvault secret with key: ${secretName} and value: "MySecretValue"`); + const secret = await secretClient.setSecret(secretName, "MySecretValue"); + + if (!secret.properties.id) { + throw new Error("Something went wrong - secret id is undefined"); + } + // creates the secret reference config setting + await createConfigSetting(key, secret.properties.id); +} + +async function createConfigSetting(key: string, secretId: string) { // Set the following environment variable or edit the value on the following line. const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; const appConfigClient = new AppConfigurationClient(connectionString); - await cleanupSampleValues([secretReference.key], appConfigClient); + const secretReference: ConfigurationSetting = { + key, + value: { secretId }, + isReadOnly: false, + contentType: secretReferenceContentType + }; + + await cleanupSampleValues([key], appConfigClient); console.log( - `Add a new secretReference with key: ${secretReference.key} and secretId: ${secretReference.value.secretId}` + `Add a new secretReference with key: ${key} and secretId: ${secretReference.value.secretId}` ); await appConfigClient.addConfigurationSetting(secretReference); - - console.log(`Get the added secretReference from App Config with key: ${secretReference.key}`); - const getResponse = await appConfigClient.getConfigurationSetting({ - key: secretReference.key - }); - - // You can use the `isSecretReference` global method to check if the content type is secretReferenceContentType ("application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8") - const parsedSecretReference = parseSecretReference(getResponse); - // Read the secret we created - const secret = await secretClient.getSecret(parsedSecretReference.value.secretId); - console.log(`Get the secret from keyvault key: ${secret.name}, value: ${secret.value}`); - - console.log(`Deleting the secret from keyvault`); - await secretClient.beginDeleteSecret(parsedSecretReference.value.secretId); - - await cleanupSampleValues([secretReference.key], appConfigClient); } async function cleanupSampleValues(keys: string[], client: AppConfigurationClient) { diff --git a/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js b/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js index 2650dc3210de..f7a9edaddac3 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js +++ b/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js @@ -9,7 +9,7 @@ const { secretReferenceContentType, parseSecretReference } = require("@azure/app-configuration"); -const { SecretClient } = require("@azure/keyvault-secrets"); +const { parseKeyVaultSecretIdentifier, SecretClient } = require("@azure/keyvault-secrets"); const { DefaultAzureCredential } = require("@azure/identity"); // Load the .env file if it exists @@ -18,65 +18,101 @@ dotenv.config(); async function main() { console.log(`Running secretReference sample`); - const secretReference = { - key: `secret${new Date().getTime()}`, - value: { - secretId: `secret-key${Math.ceil(100 + Math.random() * 900)}` - }, - isReadOnly: false, - contentType: secretReferenceContentType - }; + const key = `secret${new Date().getTime()}`; + + // setup method creates + // - a secret using `@azure/keyvault-secrets` + // - a corresponding secret reference config setting with `@azure/app-configuration` + await setup(key); + + console.log(`Get the added secretReference from App Config with key: ${key}`); + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + const getResponse = await appConfigClient.getConfigurationSetting({ + key + }); + // You can use the `isSecretReference` global method to check if the content type is secretReferenceContentType ("application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8") + + const parsedSecretReference = parseSecretReference(getResponse); + + // Get the name and vaultUrl from the secretId + const { name: secretName, vaultUrl } = parseKeyVaultSecretIdentifier( + parsedSecretReference.value.secretId + ); + + const secretClient = new SecretClient(vaultUrl, new DefaultAzureCredential()); + try { + // Read the secret we created + const secret = await secretClient.getSecret(secretName); + console.log(`Get the secret from keyvault key: ${secretName}, value: ${secret.value}`); + } catch (err) { + const error = err; + if (error.code === "SecretNotFound" && error.statusCode === 404) { + throw new Error( + `\n Secret is not found, make sure the secret ${parsedSecretReference.value.secretId} is present in your keyvault account;\n Original error - ${error}` + ); + } else { + throw err; + } + } + + console.log(`Deleting the secret from keyvault`); + await secretClient.beginDeleteSecret(secretName); + + await cleanupSampleValues([key], appConfigClient); +} + +async function setup(key) { if ( !process.env["AZURE_TENANT_ID"] || !process.env["AZURE_CLIENT_ID"] || !process.env["AZURE_CLIENT_SECRET"] || - !process.env["KEYVAULT_URI"] + !process.env["KEYVAULT_URI"] || + !process.env["APPCONFIG_CONNECTION_STRING"] ) { - console.log(`At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and KEYVAULT_URI variables is not present, + console.log(`At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, APPCONFIG_CONNECTION_STRING and KEYVAULT_URI variables is not present, please add the missing ones in your environment and rerun the sample.`); return; } + // DefaultAzureCredential expects the following three environment variables: // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant // - AZURE_CLIENT_SECRET: The client secret for the registered application - const credential = new DefaultAzureCredential(); - const url = process.env["KEYVAULT_URI"] || ""; - - const secretClient = new SecretClient(url, credential); + const secretClient = new SecretClient(process.env["KEYVAULT_URI"], new DefaultAzureCredential()); + const secretName = `secret-${Date.now()}`; // Create a secret - console.log( - `Create a keyvault secret with key: ${secretReference.value.secretId} and value: "MySecretValue"` - ); - await secretClient.setSecret(secretReference.value.secretId, "MySecretValue"); + console.log(`Create a keyvault secret with key: ${secretName} and value: "MySecretValue"`); + const secret = await secretClient.setSecret(secretName, "MySecretValue"); + + if (!secret.properties.id) { + throw new Error("Something went wrong - secret id is undefined"); + } + // creates the secret reference config setting + await createConfigSetting(key, secret.properties.id); +} + +async function createConfigSetting(key, secretId) { // Set the following environment variable or edit the value on the following line. const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; const appConfigClient = new AppConfigurationClient(connectionString); - await cleanupSampleValues([secretReference.key], appConfigClient); + const secretReference = { + key, + value: { secretId }, + isReadOnly: false, + contentType: secretReferenceContentType + }; + + await cleanupSampleValues([key], appConfigClient); console.log( - `Add a new secretReference with key: ${secretReference.key} and secretId: ${secretReference.value.secretId}` + `Add a new secretReference with key: ${key} and secretId: ${secretReference.value.secretId}` ); await appConfigClient.addConfigurationSetting(secretReference); - - console.log(`Get the added secretReference from App Config with key: ${secretReference.key}`); - const getResponse = await appConfigClient.getConfigurationSetting({ - key: secretReference.key - }); - - // You can use the `isSecretReference` global method to check if the content type is secretReferenceContentType ("application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8") - const parsedSecretReference = parseSecretReference(getResponse); - // Read the secret we created - const secret = await secretClient.getSecret(parsedSecretReference.value.secretId); - console.log(`Get the secret from keyvault key: ${secret.name}, value: ${secret.value}`); - - console.log(`Deleting the secret from keyvault`); - await secretClient.beginDeleteSecret(parsedSecretReference.value.secretId); - - await cleanupSampleValues([secretReference.key], appConfigClient); } async function cleanupSampleValues(keys, client) { diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts b/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts index 1330aa023655..4c15bce44c1d 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts @@ -11,7 +11,7 @@ import { ConfigurationSetting, parseSecretReference } from "@azure/app-configuration"; -import { SecretClient } from "@azure/keyvault-secrets"; +import { parseKeyVaultSecretIdentifier, SecretClient } from "@azure/keyvault-secrets"; import { DefaultAzureCredential } from "@azure/identity"; // Load the .env file if it exists @@ -20,67 +20,103 @@ dotenv.config(); export async function main() { console.log(`Running secretReference sample`); - const secretReference: ConfigurationSetting = { - key: `secret${new Date().getTime()}`, - value: { - secretId: `secret-key${Math.ceil(100 + Math.random() * 900)}` - }, - isReadOnly: false, - contentType: secretReferenceContentType - }; + const key = `secret${new Date().getTime()}`; + + // setup method creates + // - a secret using `@azure/keyvault-secrets` + // - a corresponding secret reference config setting with `@azure/app-configuration` + await setup(key); + + console.log(`Get the added secretReference from App Config with key: ${key}`); + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + const getResponse = await appConfigClient.getConfigurationSetting({ + key + }); + // You can use the `isSecretReference` global method to check if the content type is secretReferenceContentType ("application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8") + + const parsedSecretReference = parseSecretReference(getResponse); + + // Get the name and vaultUrl from the secretId + const { name: secretName, vaultUrl } = parseKeyVaultSecretIdentifier( + parsedSecretReference.value.secretId + ); + + const secretClient = new SecretClient(vaultUrl, new DefaultAzureCredential()); + try { + // Read the secret we created + const secret = await secretClient.getSecret(secretName); + console.log(`Get the secret from keyvault key: ${secretName}, value: ${secret.value}`); + } catch (err) { + const error = err as { code: string; statusCode: number }; + if (error.code === "SecretNotFound" && error.statusCode === 404) { + throw new Error( + `\n Secret is not found, make sure the secret ${parsedSecretReference.value.secretId} is present in your keyvault account;\n Original error - ${error}` + ); + } else { + throw err; + } + } + + console.log(`Deleting the secret from keyvault`); + await secretClient.beginDeleteSecret(secretName); + + await cleanupSampleValues([key], appConfigClient); +} + +async function setup(key: string) { if ( !process.env["AZURE_TENANT_ID"] || !process.env["AZURE_CLIENT_ID"] || !process.env["AZURE_CLIENT_SECRET"] || - !process.env["KEYVAULT_URI"] + !process.env["KEYVAULT_URI"] || + !process.env["APPCONFIG_CONNECTION_STRING"] ) { console.log( - `At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and KEYVAULT_URI variables is not present, + `At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, APPCONFIG_CONNECTION_STRING and KEYVAULT_URI variables is not present, please add the missing ones in your environment and rerun the sample.` ); return; } + // DefaultAzureCredential expects the following three environment variables: // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant // - AZURE_CLIENT_SECRET: The client secret for the registered application - const credential = new DefaultAzureCredential(); - const url = process.env["KEYVAULT_URI"] || ""; - - const secretClient = new SecretClient(url, credential); + const secretClient = new SecretClient(process.env["KEYVAULT_URI"], new DefaultAzureCredential()); + const secretName = `secret-${Date.now()}`; // Create a secret - console.log( - `Create a keyvault secret with key: ${secretReference.value.secretId} and value: "MySecretValue"` - ); - await secretClient.setSecret(secretReference.value.secretId, "MySecretValue"); + console.log(`Create a keyvault secret with key: ${secretName} and value: "MySecretValue"`); + const secret = await secretClient.setSecret(secretName, "MySecretValue"); + + if (!secret.properties.id) { + throw new Error("Something went wrong - secret id is undefined"); + } + // creates the secret reference config setting + await createConfigSetting(key, secret.properties.id); +} + +async function createConfigSetting(key: string, secretId: string) { // Set the following environment variable or edit the value on the following line. const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; const appConfigClient = new AppConfigurationClient(connectionString); - await cleanupSampleValues([secretReference.key], appConfigClient); + const secretReference: ConfigurationSetting = { + key, + value: { secretId }, + isReadOnly: false, + contentType: secretReferenceContentType + }; + + await cleanupSampleValues([key], appConfigClient); console.log( - `Add a new secretReference with key: ${secretReference.key} and secretId: ${secretReference.value.secretId}` + `Add a new secretReference with key: ${key} and secretId: ${secretReference.value.secretId}` ); await appConfigClient.addConfigurationSetting(secretReference); - - console.log(`Get the added secretReference from App Config with key: ${secretReference.key}`); - const getResponse = await appConfigClient.getConfigurationSetting({ - key: secretReference.key - }); - - // You can use the `isSecretReference` global method to check if the content type is secretReferenceContentType ("application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8") - const parsedSecretReference = parseSecretReference(getResponse); - // Read the secret we created - const secret = await secretClient.getSecret(parsedSecretReference.value.secretId); - console.log(`Get the secret from keyvault key: ${secret.name}, value: ${secret.value}`); - - console.log(`Deleting the secret from keyvault`); - await secretClient.beginDeleteSecret(parsedSecretReference.value.secretId); - - await cleanupSampleValues([secretReference.key], appConfigClient); } async function cleanupSampleValues(keys: string[], client: AppConfigurationClient) { diff --git a/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts b/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts index d763796b34e2..d1fc8cbb5413 100644 --- a/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts @@ -33,7 +33,7 @@ describe("AppConfigurationClient - SecretReference", () => { return { value: { secretId: `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName("name-2")}` - }, // TODO: It's a URL in .NET, should we leave it as a string input? + }, isReadOnly: false, key: recorder.getUniqueName("name-3"), label: "label-s",