how do I use aws secret manager with nodejs lambda

26,719

Solution 1

You need wait for the async call to finish.

Inside your main handler you will have something like:

// inside your main handler
exports.handler =  async function(event, context) {
    var secret = await getSecret('mySecret')
    console.log('mysecret: ' + secret )

    return ...
    }

Solution 2

Here is a more simple example if someone will need to resolve this issue:

const result = await client
  .getSecretValue({
    SecretId: AWSConfig.secretName,
  })
  .promise();

const parsedResult = JSON.parse(result.SecretString);

Solution 3

There is a one more easier way to read from secret manager it.

let secretManager = new SecretsManager({ region: 'region-name' });
const data = await secretManager.getSecretValue({ SecretId: 'secretid' }).promise();
console.log(`data is: ${JSON.stringify(data)}`);

Solution 4

The aws-sdk provides two means of getting values back from APIs. You can use the native callback mechanism, as shown above, or you can, instead, use .promise() on the end of the call chain, to convert the API call to its promise equivalent.

E.g.

const data = await (secretManager.getSecret({ SecretId }).promise();

If you're using await then your function needs to be async as do all the functions calling it, unless they choose to use Promise's then/catch etc.

Solution 5

I've created a Synchronous solution which you can find here: https://github.com/jwerre/secrets

With this package you can load all your secrets inside of a particular namespace like so:

const config = require('@jwerre/secrets').configSync({
    region: 'us-east-1',
    env: 'production',
    namespace: 'my-namespace',
});

This will retrieve all your secrets which may not be exactly what you want. If you want a single secret you can do it like this:

const config = require('@jwerre/secrets').secretSync({
    region: 'us-west-2'
    id: '/my-co/apis/'
});
Share:
26,719

Related videos on Youtube

red888
Author by

red888

Updated on July 09, 2022

Comments

  • red888
    red888 almost 2 years

    I tried to wrap the example code snippet to get secrets in a function and then call it but it does not appear to be working. I suspect I am calling it asynchronously and I need to call it synchronously? I just want a function I can call to get a secret value and put it in a var.

    this is the function:

    //outside exports.handler = (event, context, callback) => {
    function getSecret(secretName) {
      // Load the AWS SDK
      var AWS = require('aws-sdk'),
          region = process.env.AWS_REGION,
          secretName = secretName,
          secret,
          decodedBinarySecret;
    
      // Create a Secrets Manager client
      var client = new AWS.SecretsManager({
          region: region
      });
    
      // In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
      // See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
      // We rethrow the exception by default.
    
      client.getSecretValue({SecretId: secretName}, function(err, data) {
          if (err) {
              if (err.code === 'DecryptionFailureException')
                  // Secrets Manager can't decrypt the protected secret text using the provided KMS key.
                  // Deal with the exception here, and/or rethrow at your discretion.
                  throw err;
              else if (err.code === 'InternalServiceErrorException')
                  // An error occurred on the server side.
                  // Deal with the exception here, and/or rethrow at your discretion.
                  throw err;
              else if (err.code === 'InvalidParameterException')
                  // You provided an invalid value for a parameter.
                  // Deal with the exception here, and/or rethrow at your discretion.
                  throw err;
              else if (err.code === 'InvalidRequestException')
                  // You provided a parameter value that is not valid for the current state of the resource.
                  // Deal with the exception here, and/or rethrow at your discretion.
                  throw err;
              else if (err.code === 'ResourceNotFoundException')
                  // We can't find the resource that you asked for.
                  // Deal with the exception here, and/or rethrow at your discretion.
                  throw err;
          }
          else {
              // Decrypts secret using the associated KMS CMK.
              // Depending on whether the secret is a string or binary, one of these fields will be populated.
              if ('SecretString' in data) {
                  return data.SecretString;
              } else {
                  let buff = new Buffer(data.SecretBinary, 'base64');
                  return buff.toString('ascii');
              }
        }
      });
    }
    

    Then I call it

    // inside exports.handler = (event, context, callback) => {
    var secret = getSecret('mySecret')
    console.log('mysecret: ' + secret )
    

    The secret var is always undefined

    EDIT: Async only works with promises so I had to make my function async and return a promise:

    async function mySecrets(secretName) {
        // Load the AWS SDK
        var AWS = require('aws-sdk'),
            region = process.env.AWS_REGION,
            secretName = secretName,
            secret,
            decodedBinarySecret;
    
        // Create a Secrets Manager client
        var client = new AWS.SecretsManager({
            region: region
        });
    
        return new Promise((resolve,reject)=>{
            client.getSecretValue({SecretId: secretName}, function(err, data) {
    
                // In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
                // See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
                // We rethrow the exception by default.
                if (err) {
                    reject(err);
                }
                else {
                    // Decrypts secret using the associated KMS CMK.
                    // Depending on whether the secret is a string or binary, one of these fields will be populated.
                    if ('SecretString' in data) {
                        resolve(data.SecretString);
                    } else {
                        let buff = new Buffer(data.SecretBinary, 'base64');
                        resolve(buff.toString('ascii'));
                    }
                }
            });
        });
    }
    
    .....
    // inside handler
    exports.handler = async (event) => {
    ....
    var value = await mySecrets('mysecret')
    
  • red888
    red888 over 4 years
    Could I also user a promise instead somehow?
  • iwaduarte
    iwaduarte about 4 years
    yep asyc or promises same same.
  • Peter
    Peter about 3 years
    after two days of countless tries to make this work ==> this did the trick!
  • Jamie
    Jamie almost 3 years
    perfect! I think everyone looks for the shorter method straight away.
  • insoftservice
    insoftservice over 2 years
    @iwaduarte could you please provide complete code with async and await. where i can provide SecretString and other required param dynamically
  • insoftservice
    insoftservice over 2 years
    what is /my-co/apis is it secret name?
  • jwerre
    jwerre over 2 years
    Yes indeed. I use slashed for my secrets so I can separate them for different kinds of deployment. For example /production/lambda1/mysql/username or /staging/api/config/aws/secrete. Checkout the documentation in the readme.
  • iwaduarte
    iwaduarte over 2 years
    The code is complete. If you need additional help you should create a new question and put the link here.
  • insoftservice
    insoftservice over 2 years
  • ZEE
    ZEE over 2 years
    This is bad practice, awaiting for secret manager in the handler means, awaiting for it in every lambda call, which is not what you need, especially when you use the secrets for the db client which you need to initiate outside the handler.
  • iwaduarte
    iwaduarte over 2 years
    Why is that bad practice ? I fail to see how would do otherwise. The idea is to remove secrets from within application. docs.aws.amazon.com/secretsmanager/latest/userguide/intro.ht‌​ml. If could share the reasons as to why is a bad practice. I would be glad.
  • Dennix
    Dennix about 2 years
    No idea, why the answers above did not work for me and why there multiple articles with much more complex solutions to this problem but thank you. This is the first solution to work for me :)
  • Aditya
    Aditya about 2 years
    Thanks @Dennix, you made my day.