简体   繁体   中英

Node.js: Awaiting a Require

I'm new the Node.js and I've been working with a sample project by a third party provider and I'm trying to use Azure Key Vault to store configuration values.

I'm having trouble getting a process to wait before executing the rest. I'll try to detail as much as I know.

The sample project has a file named agent.js which is the start page/file. On line 16 ( agent_config = require('./config/config.js')[process.env.LP_ACCOUNT][process.env.LP_USER] ) it calls a config file with values. I'm trying to set these value using Key Vault. I've tried many combinations of calling functions, and even implementing async / await but the value for agent_config always contains a [Promise] object and not the data returned by Key Vault.

If I'm right, this is because the Key Vault itself uses async / await too and the config file returns before the Key Vault values are returned.

How can Key Vault be added/implemented in a situation like this?

Here's what I've tried:

First updated agent.js to

let agent_config = {};
try {
  agent_config = require('./config/config.js')['123']['accountName'];
} catch (ex) {
  log.warn(`[agent.js] Error loading config: ${ex}`)
}

console.log(agent_config);

Test 1

./config/config.js

const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://' + '{my vault}' + '.vault.azure.net/' || process.env['KEY_VAULT_URI'];

function getValue(secretName, secretVersion) {
  msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' }).then((credentials) => {
    const client = new KeyVault.KeyVaultClient(credentials);
    client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
      function (response) {
        return response.Value;
      });
  });
}

module.exports = {
    '123': {
        'accountName': {
            accountId: getValue('mySecretName', '')
         }
     }
};

Results

{ accountsId: undefined }

Test 2

Made getValue an async function and wrapped it around another function (tried without the wrapping and didn't work either)

./config/config.js

const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://' + '{my vault}' + '.vault.azure.net/' || process.env['KEY_VAULT_URI'];

async function getValue(secretName, secretVersion) {
  msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' }).then((credentials) => {
    const client = new KeyVault.KeyVaultClient(credentials);
    client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
      function (response) {
        return response.Value;
      });
  });
}

async function config() {
    module.exports = {
        '123': {
            'accountName': {
                accountId: await getValue('mySecretName', '')
             }
         }
    };
}

config();

Results

{}

Test 3

Made getValue an async function and wrapped it around another function (tried without the wrapping and didn't work either)

./config/config.js

const KeyVault = require('azure-keyvault');
const msRestAzure = require('ms-rest-azure');
const KEY_VAULT_URI = 'https://' + '{my vault}' + '.vault.azure.net/' || process.env['KEY_VAULT_URI'];

async function getValue(secretName, secretVersion) {
  return msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
    .then((credentials) => {
      const client = new KeyVault.KeyVaultClient(credentials);
      return client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
        function (response) {
          return response.Value;
        });
    });
}

module.exports = {
    '123': {
        'accountName': {
            accountId: getValue('mySecretName', '')
        }
    }
};

config();

Results

{ accountId: { <pending> } }

Other

I've tried many others ways like module.exports = async (value) =< {...} (found through other questions/solutions without success.

I'm starting to think I need to do some "waiting" on agent.js but I haven't found good info on this.

Any help would be great!

One issue is that your getValue function is not returning anything as your returns need to be explicit.

(and without the promise being returned, there's nothing to await on)

async function getValue(secretName, secretVersion) {
  return msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
    .then((credentials) => {
      const client = new KeyVault.KeyVaultClient(credentials);
      return client.getSecret(KEY_VAULT_URI, secretName, secretVersion).then(
        function (response) {
          return response.Value;
        });
    });
}

You could also get away with less explicit returns using arrow functions..

const getValue = async (secretName, secretVersion) => 
  msRestAzure.loginWithAppServiceMSI({ resource: 'https://vault.azure.net' })
    .then(credentials => {
      const client = new KeyVault.KeyVaultClient(credentials);
      return client.getSecret(KEY_VAULT_URI, secretName, secretVersion)
        .then(response => response.Value);
    });

Introducing the Azure Key Vault read, which is async, means your whole config read is async. There' nothing you can do to get around that. This will mean that the code that uses the config will need to handle it appropriately. You start by exporting an async function that will return the config..

async function getConfig() {
  return {
    '123': {
      'accountName': {
        accountId: await getValue('mySecretName', '')
      }
    }
  };
}

module.exports = getConfig;

In your agent code you call that function. This will mean that your agent code will need to be wrapped in a function too, so maybe something like this..

const Bot = require('./bot/bot.js');
const getConfig = require('./config/config.js');

getConfig().then(agentConfig => {
    const agent = new Bot(agentConfig);

    agent.on(Bot.const.CONNECTED, data => {
        log.info(`[agent.js] CONNECTED ${JSON.stringify(data)}`);
    });
});

The package azure-keyvault has been deprecated in favor of the new packages to deal with Keyvault keys, secrets and certificates separately. For your scenario, you can use the new @azure/keyvault-secrets package to talk to Key Vault and the new @azure/identity package to create the credential.

const { SecretClient } = require("@azure/keyvault-secrets");
const { DefaultAzureCredential } = require("@azure/identity");

async function getValue(secretName, secretVersion) {
  const credential = new DefaultAzureCredential();
  const client = new SecretClient(KEY_VAULT_URI, credential);
  const secret = await client.getSecret(secretName);
  return secret.value;
}

The DefaultAzureCredential assumes that you have set the below env 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

To try other credentials, see the readme for @azure/identity If you are moving from the older azure-keyvault package, checkout the migration guide to understand the major changes

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM