red888
red888

Reputation: 31550

how do I use aws secret manager with nodejs lambda

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')

Upvotes: 35

Views: 52798

Answers (6)

Ashley Frieze
Ashley Frieze

Reputation: 5443

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.

Upvotes: 5

Aditya
Aditya

Reputation: 399

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)}`);

Upvotes: 13

Vakho Jgenti
Vakho Jgenti

Reputation: 405

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);

Upvotes: 10

Nishant Thapliyal
Nishant Thapliyal

Reputation: 1670

A much better way is to do this inside your async lambda function

Example key:val => password:rootPassword

const secret = await secretClient.getSecretValue({SecretId: 'SecretKeyName'}).promise().then((data) => {
        return JSON.parse(data.SecretString);
})

and then access it as secret.password.

Note: wrap around try/catch block to handle errors automatically.

Upvotes: 1

jwerre
jwerre

Reputation: 9594

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/'
});

Upvotes: 2

iwaduarte
iwaduarte

Reputation: 1700

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 ...
    }

Upvotes: 20

Related Questions