Search code examples
aws-secrets-manageraws-lambda-edge

Utilizing SecretManager in AWS Lambda@Edge


I want to get a secret in my aws L@E function, and in the code I must set a region to my SecretManager instance

// Create a Secrets Manager client
const client = new AWS.SecretsManager({
    region: "us-east-1", // mandatory param
});

but I know that SecretManager also has multi-region support so i don’t understand how it work together with the L@E function. how can I know on runtime what is the best region to use? and also what happens if there is a downtime in one region? in addition to caching the secrets via lambda global variables, is it possible also to optimize the SecretManager usage?

I tried using the process.env.AWS_REGION env variable, but its not supported


Solution

  • In case someone encounters this issue in the future, I got an answer from AWS support. There is no way to tell what is the "best" region to use on runtime via L@E. To protect against region downtime, the best practice is to try accessing a SecretManager instance from a primary region and have a fallback region (where it has been replicated) in case of an error.

    const AWS = require('aws-sdk');
    
    const name = "app-api-key";
    const primarySecretManager = new AWS.SecretsManager({
        region: 'us-east-1',
    });
    const fallbackSecretManager = new AWS.SecretsManager({
        region: "us-east-2",
    });
    
    const getSecrets = async () => {
        let secrets;
        try {
            secrets = await getSecretsInternal(primarySecretManager)
        } catch (e) {
            secrets = await getSecretsInternal(fallbackSecretManager)
        }
        return secrets
    }
    
    const getSecretsInternal = async client => {
        return new Promise((resolve, reject) => {
            client.getSecretValue({ SecretId: name }, (err, data) => {
                if (err) {
                    switch (err.code) {
                        case 'DecryptionFailureException':
                            console.error(`Secrets Manager can't decrypt the protected secret text using the provided KMS key.`)
                            break
                        case 'InternalServiceErrorException':
                            console.error(`An error occurred on the server side.`)
                            break
                        case 'InvalidParameterException':
                            console.error(`You provided an invalid value for a parameter.`)
                            break
                        case 'InvalidRequestException':
                            console.error(`You provided a parameter value that is not valid for the current state of the resource.`)
                            break
                        case 'ResourceNotFoundException':
                            console.error(`We can't find the resource that you asked for.`)
                            break
                    }
                    console.error(err)
                    reject(err)
                    return
                }
    
                // Decrypts secret using the associated KMS CMK.
                // Depending on whether the secret is a string or binary, one of these fields will be populated.
                let secrets;
                if ('SecretString' in data) {
                    secrets = data.SecretString;
                } else {
                    const buff = new Buffer(data.SecretBinary, 'base64');
                    secrets = buff.toString('ascii');
                }
    
                resolve(JSON.parse(secrets))
            })
        })
    }
    
    module.exports = {
        getSecrets,
    }