Create the Key Vault

Key Vault names must be globally unique, so pick a unique name. Vault names must be 3-24 characters long and contain only alphanumeric characters and dashes. Make a note of the vault name you choose, because you need it throughout this exercise.

Pre-Requisite

az provider list --output table
az provider register --namespace Microsoft.KeyVault
az provider register --namespace Microsoft.Web
az provider show -n Microsoft.KeyVault
az provider show -n Microsoft.Web

To create your vault, run the following command in Azure Cloud Shell. Make sure to enter your unique vault name to the --name parameter. When it finishes, you will see JSON output describing the new vault.

Azure CLI

az keyvault create \
    --resource-group "[sandbox resource group name]" \
    --location customize \
    --name <your-unique-vault-name>

Add Permissions

The next command requires you to use the legacy permission systems. Make sure to go into the portal, open up the keyvault, go to access configuration, and select vault access policy.

Azure CLI

az keyvault set-policy --name drJkeyVault --object-id "e96d1428-b041-404....." --secret-permissions all --key-permissions all --certificate-permissions all
Note

--object-id is located in the keyvaults json

Add the secret

Now, add the secret. Our secret is named SecretPassword with a value of reindeer_flotilla. Make sure to replace <your-unique-vault-name> with the vault name you created in the --vault-name parameter.

Azure CLI

az keyvault secret set \
    --name SecretPassword \
    --value reindeer_flotilla \
    --vault-name <your-unique-vault-name>

Create the app

Run the following code in Azure CLI to initialize a new Node.js app, install the needed packages, and open a new file in the editor.

mkdir KeyVaultDemoApp
cd KeyVaultDemoApp
npm init -y
npm install @azure/identity @azure/keyvault-secrets express
touch app.js
code app.js

Add code that loads and uses secrets

To demonstrate good use of Key Vault, your app loads secrets from the vault at startup. To demonstrate that your secrets loaded, create an endpoint that displays the value of the SecretPassword secret.

  1. To set up the app, paste the following code into the editor. This code imports the necessary packages, sets up the port and vault URI configuration, and creates a new object to hold the secret names and values.
// Importing dependencies
    const { DefaultAzureCredential } = require("@azure/identity");
    const { SecretClient } = require("@azure/keyvault-secrets");
    const app = require('express')();
    
    // Initialize port
    const port = process.env.PORT || 3000;
    
    // Create Vault URI from App Settings
    const vaultUri = `https://${process.env.VaultName}.vault.azure.net/`;
    
    // Map of key vault secret names to values
    let vaultSecretsMap = {};
Important

Make sure to save files as you work on them, especially when you're finished. You can save files either through the "..." menu, or the accelerator key (Ctrl+S on Windows and Linux, Cmd+S on macOS).

  1. Next, add the code to authenticate to the vault and load the secrets. You add this code as two separate functions. Insert a couple of blank lines after the code you previously added, and then paste in the following code.
const getKeyVaultSecrets = async () => {
  // Create a key vault secret client
  let secretClient = new SecretClient(vaultUri, new DefaultAzureCredential());
  try {
    // Iterate through each secret in the vault
    listPropertiesOfSecrets = secretClient.listPropertiesOfSecrets();
    while (true) {
      let { done, value } = await listPropertiesOfSecrets.next();
      if (done) {
        break;
      }
      // Only load enabled secrets - getSecret will return an error for disabled secrets
      if (value.enabled) {
        const secret = await secretClient.getSecret(value.name);
        vaultSecretsMap[value.name] = secret.value;
      }
    }
  } catch(err) {
    console.log(err.message)
  }
}
  1. To test whether our secret was loaded, create the Express endpoint. Paste in this code.
app.get('/api/SecretTest', (req, res) => {
  let secretName = 'SecretPassword';
  let response;
  if (secretName in vaultSecretsMap) {
    response = `Secret value: ${vaultSecretsMap[secretName]}\n\nThis is for testing only! Never output a secret to a response or anywhere else in a real app!`;
  } else {
    response = `Error: No secret named ${secretName} was found...`
  }
  res.type('text');
  res.send(response);
});
  1. Call your functions to load the secrets from our vault, then start the app. To complete the app, paste in this last snippet.
(async () =>  {
  await getKeyVaultSecrets();
  app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
  });
})().catch(err => console.log(err));
  1. Save the file & Quit.

Create the App Service plan and app

Creating an App Service app is a two-step process: First create the plan, then the app.

The plan name only needs to be unique within your subscription, so you can use the same name: keyvault-exercise-plan. The app name needs to be globally unique, though, so pick your own.

  1. In Azure Cloud Shell, run the following command to create an App Service plan.
az appservice plan create \
    --name keyvault-exercise-plan \
    --sku FREE \
    --location centralus \
    --resource-group "[sandbox resource group name]"
  1. Next, to create the Web App that uses the App Service plan you created, run the following command. Make sure to replace <your-unique-app-name> with your app's name in the --name parameter.
az webapp create \
    --plan keyvault-exercise-plan \
    --runtime "node|22LTS" \
    --resource-group "[sandbox resource group name]" \
    --name <your-unique-app-name>

Add configuration to the app

To deploy to Azure, follow the App Service best practice of putting the VaultName configuration in an app setting instead of a configuration file. You also set the SCM_DO_BUILD_DURING_DEPLOYMENT setting to true so that App Service restores your app's packages on the server and creates the necessary configuration to run the app. To create the app settings, run this command. Make sure to replace both <your-unique-app-name> with your app's name in the --name parameter, and <your-unique-vault-name> with your vault's name in the --settings parameter.

az webapp config appsettings set \
    --resource-group "[sandbox resource group name]" \
    --name <your-unique-app-name> \
    --settings 'VaultName=<your-unique-vault-name>' 'SCM_DO_BUILD_DURING_DEPLOYMENT=true'

Enable Managed Identity

Enabling managed identity on an app is a one-liner. To enable it on your app, run the following command. Make sure to replace <your-unique-app-name> with your app's name in the --name parameter.

az webapp identity assign \
    --resource-group "[sandbox resource group name]" \
    --name <your-unique-app-name>

From the resulting JSON output, copy the principalId value. PrincipalId is the unique ID of the app's new identity in Microsoft Entra ID, and you're going to use it in the next step.

Grant Access to the Vault

The last step before deploying is to assign Key Vault permissions to your app's managed identity. Make sure to replace both <your-unique-vault-name> with your vault's name in the --name parameter, and enter the principalId value you copied from the previous step as the value for object-id in the following command. To establish Get and List access, run this command.

az keyvault set-policy \
    --secret-permissions get list \
    --name <your-unique-vault-name> \
    --object-id <your-managed-identity-principleid>

Check for secrets

az keyvault secret list --vault-name `<your-unique-vault-name>

Deploy the App and Find out

  1. All your configuration is set, and you're ready to deploy! The following commands publish the site to the pub folder, zip it up into site.zip, and deploy the zip to App Service. Make sure to replace <your-unique-app-name> with your app's name in the --name parameter.
Note

You'll need to cd back to the KeyVaultDemoApp directory if you're not still there.

cd KeyVaultDemoApp
zip site.zip * -x node_modules/
az webapp deploy --src-path site.zip -g "[sandbox resource group name]" -n <your-unique-app-name>
  1. The deployment might take a minute or two to complete. After you get a result that indicates that the site deployed, open https://<your-unique-app-name>.azurewebsites.net/api/SecretTest in a browser. The app takes a moment to start up for the first time on the server, but after it does, you should see the secret value, reindeer_flotilla.