Access Secrets-Manager values in Serverless Framework script.
While deploying the application using the Serverless script, we want to access some confidential values like- AWS & encryption/decryption keys, Database details, etc which we don’t want to expose or commit into the codebase.
To achieve this, we will use the AWS JavaScript SDK to access the Secrets-Manager service.
Install the aws-sdk using npm.
Let’s get started. First, create a secrets.js file in the root of your project. [Or at any other place you want to create]. Add the below code in this file.
const AWS = require("aws-sdk");// Create a Secrets Manager client
const client = new AWS.SecretsManager({
region: 'your_aws_region'
});const getAllSecret = () => {
return new Promise((resolve, reject) => {
let secrets = {};
const secretName = '<your_aws_secret_manager_name>';
client.getSecretValue({ SecretId: secretName }, function (err, data) {
if (err) {
console.error('getAllSecret - error: ', err);
reject(err);
}
else {
// Depending on whether the secret is a string or binary, one of these fields will be populated.
if ('SecretString' in data) {
secrets = data.SecretString;
}
else {
let buff = new Buffer(data.SecretBinary, 'base64');
secrets = buff.toString('ascii');
}
secrets = JSON.parse(secrets);
resolve(secrets);
}
});
});
};module.exports.getSecrets = async () => {
const keys = await getAllSecret();
return {
DB_HOSTNAME: keys.DB_HOSTNAME,
DB_USERNAME: keys.DB_USERNAME,
DB_PASSWORD: keys.DB_PASSWORD,
DB_PORT: keys.DB_PORT
}
}
In above code:
- Firstly, we have imported the AWS SDK.
- Created an object of Secrets-Manager service.
- Define a getAllSecret() function that will use the Secrets-Manager’s getSecretValue() function to get the secrets and it takes the secret name [which you have created in AWS Console] as parameter.
- Once the secrets are received, parse them and resolve the promise.
- We put this function inside Promise, so that we can invoke it using the async/await pattern.
- At end, we have exported a function called getSecrets. It will invoke the getAllSecret() and map the result in key-value pairs.
Now, create a serverless.yml file in the root of the project.
app: my-app
service: my-service
custom:
SECRETS: ${file(./secrets.js):getSecrets}
provider:
name: aws
runtime: nodejs14.x
functions:
my-function:
name: my-lambda-function
handler: index.handler
description: Test lambda
memorySize: 128
timeout: 30
environment:
DB_HOSTNAME: ${self:custom.SECRETS.DB_HOSTNAME}
DB_USERNAME: ${self:custom.SECRETS.DB_USERNAME}
DB_PASSWORD: ${self:custom.SECRETS.DB_PASSWORD}
DB_PORT: ${self:custom.SECRETS.DB_PORT}
Note: The indentation should be proper of the yml file otherwise Serverless gives error while start deployment.
It is a simple serverless.yml script that deploy a lambda function. [we made it simple for example purpose]
But, few things, need to be note there are:
- Inside the custom section of the above script, we have defined a custom variable called SECRETS and we have passed the path of our secrets.js to Serverless ${file()} function. Please make sure file path should be correct.
- Using it, Serverless will include the JavaScript file and we can invoke the exported getSecrets() function on the same [this function is accessible because we have exported it] using : symbol in Serverless script.
- No need to worry about async getSecrets() function invocation, Serverless will take care of it before starting the deployment and the coming result will assign to SECRETS custom variable.
- Now, we can use those received secrets from Secrets-Manager and assign their values to the lambda’s environment variables.
- The syntax/way of using the custom variables in script is:
${self:custom.SECRETS.DB_HOSTNAME}
self: Refers to the current script.
custom: Refers to the custom section of the script.
SECRETS: Refers to the SECRETS variable in the custom section.
DB_HOSTNAME: Refers to the key name that we have mapped with the coming value from the AWS Secrets-Manager.
Note: Your IAM user that you will use to deploy the script should have atleast the following permissions to access the AWS Secrets-Manager.
secretsmanager:GetSecretValue
secretsmanager:DescribeSecret
secretsmanager:ListSecrets
That’s it, we have done here, now just go ahead and deploy your Serverless script and you should see the AWS Secrets-Manager values are assigned to your lambda’s environment variables.
To create the secret in Secrets-Manager, please refer to the official AWS documentation: https://docs.aws.amazon.com/secretsmanager/latest/userguide/tutorials_basic.html#tutorial-basic-step1