Read from Secrets Manager in AWS Lambda function - node.js

I am trying to use the Node.js sample code that AWS Secrets Manager provides to read a secret value, and am putting this code inside a Lambda function. However, I can't seem to get into the function that handles the response from getting the secret value.
The Lambda role has AdministratorAccess permissions to rule out it being a permissions issue.
Lambda Code:
exports.handler = async (event) => {
// Load the AWS SDK
var AWS = require('aws-sdk'),
region = "us-east-1",
secretName = "/my-secrets/level1/level2",
secret,
decodedBinarySecret;
var client = new AWS.SecretsManager({
region: region
});
console.log('above')
client.getSecretValue({SecretId: secretName}, function(err, data) {
console.log('in')
if (err) {
throw err;
}
else {
if ('SecretString' in data) {
secret = data.SecretString;
} else {
let buff = new Buffer(data.SecretBinary, 'base64');
decodedBinarySecret = buff.toString('ascii');
}
}
console.log(secret)
});
console.log('below')
};
OUTPUT
2020-03-05T18:51:54.547Z a3101875-a1f4-4b6f-ac62-3c2f93f5941f INFO above
2020-03-05T18:51:54.947Z a3101875-a1f4-4b6f-ac62-3c2f93f5941f INFO below
Because the secret exists, I would expect to see "in" and the secret lines in the output...what is preventing it from getting inside that function?

Change your call to be a promise:
const data = await client.getSecretValue({SecretId: secretName}).promise();
The problem you are running into is that the lambda is ending execution before your callback is executed. AWS Lambda Function Handler in Node.js
The above solution works, but for a full code example, please refer to this link: https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javascriptv3/example_code/secrets/src/secrets_getsecretvalue.js

Related

creating s3 bucket and folders inside it using node js in aws lambda function

I am both new to Node js and AWS. I am trying to create a bucket in S3 using node js in lambda function. Consequently, I am trying to create folders inside this S3 bucket.
I followed all the questions answered before and tried different iterations of code, but none of them seem to be working. Following is my code which is executing without giving any issues, yet the bucket and the folders are not getting created.
const AWS = require('aws-sdk');
let s3Client = new AWS.S3({
accessKeyId: '<access_key_id>',
secretAccessKey: '<secret_access_key>'
});
var params = {
Bucket : 'pshycology06'
};
exports.handler = async (event, context, callback) => {
// call spaces to create the bucket
s3Client.createBucket(params, function(err, data) {
if (err) {
console.log("\r\n[ERROR] : ", err);
} else {
console.log("\r\n[SUCCESS] : data = ",data);
}
});
};
The code for creating folders inside the Lambda function is as following --
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
var s3Client = new AWS.S3({apiVersion: '2006-03-01'});
exports.handler = async (event, context) => {
let params1 = { Bucket: 'travasko', Key: '2/dir1/dir2', Body:'body does not matter' };
s3Client.putObject(params1, function (err, data) {
if (err) {
console.log("Error creating the folder: ", err);
} else {
console.log("Successfully created a folder on S3");
}
});
Both of them doesn't work. I read a lot of documents on this issue and answers previously asked, but none of them are working for me.
The lambda function has a timeout of 1 minute. It has following policies for the IAM role -
1. AmazonRDSFullAccess
2. AmazonS3FullAccess
3. AWSLambdaVPCExecutionRole
The VPC security group is the default one.
Also, when I am trying to create the same bucket using the following AWS CLI command, it creates the bucket.
aws s3api create-bucket --bucket psychology06 --region us-east-1
I am not sure, where am i making a mistake.
Make sure the bucket with same name is not present.Please share log if possible.
You need to chain the .promise() method to your aws-sdk calls and await on them because you are creating async functions.
await s3Client.createBucket(params).promise();
await s3Client.putObject(params1).promise();
Furthermore, S3 doesn't work with directories although you may be thrown over by the way the S3 console looks like when you add / to your filenames. You can read more about it here
As you are new, always try aws cli(not recommended) and then search for search for equivalent sdk function while implementing.As it(your code) is async it won't wait until the call back function executes , so you can try something like below.(This is not actual solution , it just tells how to wait until the call back does it's work.)
'use strict'
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
var s3Client = new AWS.S3({ apiVersion: '2006-03-01' });
exports.handler = async (event, context) => {
let params1 = { Bucket: 'travasko', Key: '2/dir1/dir2', Body: 'body does not matter' };
try {
let obj = await something(params1);
callback(null, obj);
}
catch (err) {
callback('error', err)
}
}
async function something(params1) {
return new Promise(async (resolve, reject) => {
await s3Client.putObject(params1, function (err, data) {
if (err) {
console.log('Error creating the folder:', err);
reject('error during putObject');
} else {
console.log('success' + JSON.stringify(data));
resolve('success');
}
});
});
}
To your question in the comments :
Hi Vinit , let me give you little background , the question you have asked is very generic. Firstly VPC is something which you create where you will have your organization private and public subnets that are used to run your ec2 or any hosted services (non-managed services by aws). But as lambda is managed service it runs in aws vpc , they usually take your code and lambda configurations and execute the code.Now coming to your question if we attach vpc in your lambda configurations ( only if your lambda needs to use services hosted in your vpc, else don't use it) then as we discussed lambda runs in aws vpc , so during cold start it created an ENI(think of it as elastic IP) and tries to communicate with your VPC. Before Re-invent an ENI was created for each lambda that was the reason it takes time for the first time and lambda used to time out even though your execution takes lesser time. Now after re-invent EIP's are created per subnet per security group. So now coming to your question when u have attached vpc, if lambda execution is taking more time or not working as expected, then you have to see how your vpc(configs, routes, subnets) is set up and it's very hard to answer as so many parameters are involved unless we debug. So short answer do not attach vpc if your function(code) does not need to talk to any of your own managed instances in vpc (usually private subnet
) etc.
Since, you are using async functionality. Thus, you have to use await on calling "s3Client.createBucket". Then resolve the received promise.
For creating folders, use trailing "/". For example "pshycology06/travasko/".
Do post error logs if these doesn't work.

I get 'Error [ConfigError]: Missing region in config' each time I attempt AWS lambda function call from NodeJS

I'm new to AWS lambda functions and was given some sample code (below -- in the actual code I fill in the placeholders correctly, of course). But each time I call the lambda function, I get
Error [ConfigError]: Missing region in config
at Request.VALIDATE_REGION (/Users/abc/Documents/projects/bot/node_modules/aws-sdk/lib/event_listeners.js:92:45)
Even though the region is set in aws.config.update. I'm not sure what to do to fix this. I've tried removing the call to aws.config.update, removing the reference to region, but nothing helps.
I'd also be keen to know if there's a way to load the credentials from the shared file in ~/.aws/credentials, instead of having to enter them directly here.
Thanks for any help!
const aws = require('aws-sdk');
// do this only in dev, in prod you will not need to put your keys...
aws.config.update({accessKeyId: '<YOU ACCESS KEY>', secretAccessKey: '<YOUR SECRET>', region:'ap-northeast-1'});
const lambda = new aws.Lambda();
function invokeLambda(options) {
return new Promise((resolve, reject) => {
lambda.invoke(options, (err, data) => {
if (err) {
return reject(err);
}
return resolve(data);
});
});
}
const sampleCall = async () => {
try {
const options = {
FunctionName: '<NAME OF THE FUNCTION YOU WANT TO INVOKE>',
Payload: '<The JSON.stringify of the object you want to provide as parameter>',
}
const result = await invokeLambda(options);
console.log(result);
} catch (err) {
console.error(err);
}
};
You can configure the region before using any services.
like this:
var AWS = require('aws-sdk');
AWS.config.update({region:'us-east-1'});
const lambda = new aws.Lambda();
AWS services such as lambda has the ability to assume an IAM Role. Because the applications does not need to store credentials.
here is what you should do:
The IAM role is attached to the lambda
Permissions are then attached to the IAM role
Basically you can attach the same policy that your IAM user is using to the IAM role. After doing that, you can remove credentials from the code.
Reference:
https://aws.amazon.com/blogs/security/how-to-create-an-aws-iam-policy-to-grant-aws-lambda-access-to-an-amazon-dynamodb-table/
Hey No need for configure the whole AWS setttings. if you want to keep it unconfigured and not for all of your request to use the region.
The lambda constructor receive 'region' param just set it to your desired region.
Example:
const lambda = new Lambda({
region: 'us-east-1'
});
lambda.invoke({ FunctionName: 'your-function-name'},
function(err, data) {
if(err) {
console.log(err, err.stack);
}
else {
console.log(data);
}
}
);

AWS Lambda :: How to test my code on my local ubuntu machine?

I have this Lambda function code which is invoked by an SQS.
SQS triggers my Lambda function ( in nodeJS).
Lambda will also send out an SES email. Is there a way I can test this on my local Ubuntu rather than always using AWS web console?
Any help is appreciated.
Here is my Lambda NodeJS code: This code works only on AWS Lambda. When I run
$node index.js , it does not send out SES email.
var aws = require("aws-sdk");
var nodemailer = require("nodemailer");
aws.config.loadFromPath('aws_config.json');
var ses = new aws.SES();
var s3 = new aws.S3();
// Set the region
aws.config.update({region: 'us-west-2'});
exports.handler = function (event, context, callback) {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'SQS event processed.',
input: event,
}),
};
console.log('event: ', JSON.stringify(event.Records));
result = JSON.stringify(event.Records)
result = result.replace(/(^\[)/, '');
result = result.replace(/(\]$)/, '');
var resultObj = JSON.parse(result);
var idCustomer = resultObj.body;
console.log('===SENDING EMAIL====');
// Create sendEmail paramssd
var params = {
Destination: {
/* required */
CcAddresses: [
'XXXXX#gmail.com',
/* more items */
]
},
Message: {
/* required s*/
Body: {
/* required */
Html: {
Charset: "UTF-8",
Data: "BODY:"
},
Text: {
Charset: "UTF-8",
Data: "TEXT_FORMAT_BODY"
}
},
Subject: {
Charset: 'UTF-8',
Data: idCustomer
}
},
Source: 'xxxx#eeeee.com', /* required */
ReplyToAddresses: [
'wwwwww#wwwwwwwww.com',
/* more items */
],
};
// Create the promise and SES service object
var sendPromise = new aws.SES({apiVersion: '2010-12-01'}).sendEmail(params).promise();
// Handle promise's fulfilled/rejected states s
sendPromise.then(
function (data) {
console.log("Successfully sent using SES");
console.log(data.MessageId);
}).catch(
function (err) {
console.log("An Error occured while senting using using SES");
console.error(err, err.stack);
});
};
You should definetely take a look at SAM LOCAL. It is a tool developed by the AWS team specifically for testing lambdas.
https://github.com/awslabs/aws-sam-cli
Publishes a version of your function from the current snapshot of
$LATEST. That is, AWS Lambda takes a snapshot of the function code and
configuration information from $LATEST and publishes a new version.
The code and configuration cannot be modified after publication. For
information about the versioning feature, see
It is easy to use, you just type
sam local invoke --event event.json
And behind the scenes it will run a docker cotnainer for your lambda and call it.
Regarding your SES, you should put a small if(SAM_LOCAL) condition in the code and call the real one only if not in local mode. Note that SAM_LOCAL is env variable set by the SAM LOCAL tool when you run a function locally.
Good luck !
If you want to use aws as a backend - serverless framework is probably what you looking for https://serverless.com/ If you want to test your code without executing lambda on aws backend take a look at localastack framework https://github.com/localstack/localstack

Cannot get proxy parameter working with lambda integration

I have an api endpoint in API gateway defined as:
/guide/{proxy+}
which has get method and request paths defined as proxy.
Then I use lambda proxy which has and URL path parameters as:
proxy - method.request.path.proxy
I my lambda function I want to get the S3 file from a bucket based on that path, so I have:
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
exports.handler = async (event, context, callback) => {
var bucketName = 'my-bucket';
var keyName = 'guide/' + event.proxy;
var fileContent;
let data = await getS3File(bucketName, keyName)
let response = {
"statusCode": 200,
"body": data.Body
};
callback(null, response);
};
async function getS3File(bucket, key) {
const params = {
Bucket: bucket,
Key: key
};
return s3.getObject(params, (err) => {
if (err) {
// handle errors
}
}).promise();
}
Unfortunately, when I test this in test in api gateway, when I pass path as:
en/guide.pdf I get the following response:
{
"errorMessage": "The specified key does not exist.",
"errorType": "NoSuchKey",
}
The file is 100% there, when I test it within lambda (as a test), it gets downloaded. Why is so? Cannot figure it out.
NOTE: I do not want to select Use Lambda Proxy integration checkbox, because otherwise I loose some options then.

AWS SES create template with lambda function always return null

so on my first time learning AWS stuff (it is a beast), I'm trying to create e-mail templates, I have this lambda function:
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({ region: "us-east-1" });
exports.handler = async (event, context, callback) => {
// Create createTemplate params
var params = {
Template: {
TemplateName: "notification" /* required */,
HtmlPart: "HTML_CONTENT",
SubjectPart: "SUBJECT_LINE",
TextPart: "sending emails with aws lambda"
}
};
// Create the promise and SES service object
const templatePromise = new AWS.SES({ apiVersion: "2010-12-01" })
.createTemplate(params)
.promise();
// Handle promise's fulfilled/rejected states
templatePromise
.then((data) => {
console.log(data);
callback(null, JSON.stringify(data) );
// also tried callback(null, data);
}, (err) => {
console.error(err, err.stack);
callback(JSON.stringify(err) );
});
as far as I am understanding, this function should return me a template? an object, anything? when I use the lambda test functionality I always got null in the request response
does anyone know what I am doing wrong here?
edit: and It is not creating the e-mail template, I check the SES Panel - email templates and it is empty
edit2: if I try to return a string eg: callback(null, "some success message"); it does return the string, so my guess is something wrong with the SES, but this function is exactly what we have in the AWS docs, so I assume it should just work..
Try not to resolve the Promise and change your code to just returning it as-is:
return await templatePromise;
which should present you some more detail of what is really going wrong in your code - it might be some hidden access issue - so you might need to adjust the role your lambda function is using. createTemplate on the other side should not return much in case of successful execution but just create the template.
Also try to follow the following try/catch pattern when using async (as described here in more detail: https://aws.amazon.com/de/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/)
exports.handler = async (event) => {
try {
data = await lambda.getAccountSettings().promise();
}
catch (err) {
console.log(err);
return err;
}
return data;
};

Resources