Create AWS Lambda function to get Users from IAM - node.js

I want to get user details from AWS IAM so I have created a lambda function but there is an error with response code 502. My code is as below.
var AWS = require('aws-sdk');
var iam = new AWS.IAM();
AWS.config.loadFromPath('./config.json');
let getUsers = async (event, callback) => {
var params = {
UserName: "5dc6f49d50498e2907f8ee69"
};
iam.getUser(params, (err, data) => {
if (err) {
callback(err)
} else {
console.log(data)
callback(data)
}
})
};

Since your function already is async you don't need to use the old, outdated callback approach.
The AWS SDK methods provide a .promise() method which you can append to every AWS asynchronous call and, with async, you can simply await on a Promise.
var AWS = require('aws-sdk');
var iam = new AWS.IAM();
AWS.config.loadFromPath('./config.json');
let getUsers = async event => {
var params = {
UserName: "5dc6f49d50498e2907f8ee69"
};
const user = await iam.getUser(params).promise()
console.log(user)
};
Hopefully you have a handler that invokes this code, otherwise this is not going to work.
If you want to export getUsers as your handler function, make sure export it through module.exports first. I.e: module.exports.getUsers = async event.... Double check that your Lambda handler is properly configured on the function itself, where index.getUsers would stand for index being the filename (index.js) and getUsers your exported function.

Related

Lambda function not pushing data into DynamoDB table

I'm running a NodeJS lambda function which is triggered by API Gateway.
My goal is to push the data and then send a status response. I think the lambda stops running before the insertData function finishes its execution, because sometimes it works but in most requests it doesn't.
Could someone lend a hand on this?
Here is my code:
// Set a table name that we can use later on
const tableName = "InterestRates"
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'us-east-1'});
// Create the DynamoDB service object
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
// TODO implement
console.log(">>> Running LoanInterestRates")
if(event['resource'] == '/rates'){
if(event.httpMethod === 'POST'){
return newRate(event);
}
}
};
function insertData(params){
let status;
// Call DynamoDB to add the item to the table
ddb.putItem(params, function(err, data) {
if (err) {
status = "Error";
} else {
status = "Success";
}
});
return status
}
function newRate (event){
const newRate = JSON.parse(event.body);
var params = {
TableName: 'InterestRates',
Item: {
'termlength' : {N: newRate["length"]},
'InterestRate': {S: newRate["rate"]}
}
};
let addNewRate = insertData(params);
return {
statusCode: 200,
body: JSON.stringify({
response: addNewRate
})
}
}
I also tried using Async/Await but it didn't work.
You lambda function is Async but your code is not. You need to await the completion of your function newRate which in turn should also await the function inserData which should also await your DDB request.
I would advise you to do one of two things:
Learn how JS Async nature works and ensure you understand when you need to await.
Use a synchronous programming language like Python/Boto3 where you will not run into such issues.

Is it possible to save a scan data from DynamoDb to a variable in Lambda function?

I am using AWS Lambda function and I am performing read operation from my DynamoDB.
The question is, is it possible to save the data that i retrieved from DynamoDB. Since i want to send one of the values of the field in a write or update operation later.
'use strict';
const aws = require('aws-sdk');
const ddb = new aws.DynamoDB.DocumentClient({region: 'us-east-2'});
const docClient = new aws.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => {
const params = {
TableName: 'parking_lots',
Limit: 10
}
return ddb.scan(params, function(err, data){
if(err){
console.log(err);
}else{
console.log(data);
}
});
};
Something like for example:
const data = ddb.scan(params, function(err, data){
if(err){
console.log(err);
}else{
console.log(data);
}
});
So i can access a data something like this
data.lot_number;
I believe you meant "store" the data in a variable.
It will be easier for you to understand if you use async/await.
'use strict';
const aws = require('aws-sdk');
const ddb = new aws.DynamoDB.DocumentClient({region: 'us-east-2'});
const docClient = new aws.DynamoDB.DocumentClient();
exports.handler = async (event, context) => {
const params = {
TableName: 'parking_lots',
Limit: 10
}
const data = await ddb.scan(params).promise()
// You now have access to data.
console.log(data)
return Promise.resolve()
};
Your SDK command for dynamo returns a json object. You can do anything with that object, including storing it in memory for later use in the same invocation
That data will not still be available the next time the lambda is called. They do not keep state between invocations (technically there is a possibility but you should never rely on it because it exists some times and other times it doesn't and it can really mess up other invocations)

Get secrets in AWS lambda node.js

Can anyone provide a simple, complete node.js lambda function where I can get a secret from secrets manager and use it? I am struggling with the async/await process. I have already tried several suggestions from other posts, but all of them, at the end, can't really use the secret in the main function.
For example, I have a main function and call a second function to retrieve the secret:
xxx = retrieve_secret('mysecret');
Then, in the retrieve_secret function I am able to retrieve the secret, I can print it using console.log, but when I try to use it in the main function, it says "Promise ".
Please, help.
Thanks in advance!
So, after a few days working on it, I was finally able to solve it :)
Here is the code that worked for me:
exports.handler = async (event, context, callback) => {
// Get Secret
var AWS = require('aws-sdk');
var MyPromise = new AWS.SecretsManager();
var Vsecret = await MyPromise.getSecretValue({
SecretId: 'enter-the-secret-id-here'
}).promise();
var MyOpenSecret = JSON.parse(Vsecret.SecretString);
// From here, we can use the secret:
var Vhost = MyOpenSecret.host;
var Vuser = MyOpenSecret.username;
var Vpassword = MyOpenSecret.password;
var Vdatabase = .....
Looking at your question seems you are not able to read response from retrieve_secret('mysecret') method as you have mentioned it return promise, you can read it by using .then() after promise. Try doing this -
xxx.then(res => {
console.log(res)
})
Or here is the code to call get your secret details:
import AWS from "aws-sdk";
getSecretValue(secretName: string): Promise<string> {
const client = new AWS.SecretsManager({
region: '',
accessKeyId: '',
secretAccessKey: '',
});
const secretId = "secretName";
return new Promise((resolve, reject) =>
client.getSecretValue({ SecretId: secretId }, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.SecretString);
}
})
);
}

Node.js Lambda Async return Undefined

Simple call to ec2 Describing Security groups and returning the security group ID. Using Async / await, but when logging the return value, I get undefined. I fully admit I'm coming from Python and I've tried my hardest to wrap my brain around async calls. I thought I had it nailed, but I'm obviously missing something.
'use strict';
// Load Modules
const AWS = require('aws-sdk')
//Set the region
AWS.config.update({region: 'us-west-2'});
// Call AWS Resources
const ec2 = new AWS.EC2();
// Get Security Group ID From Event
const getSgIdFromEvent = async (event) => {
var ec2params = { Filters: [{Name: 'tag:t_whitelist',Values[event['site']]}]};
await ec2.describeSecurityGroups(ec2params, function (err, response) {
if (err) {return console.error(err.message)}
else {
var sgId = response.SecurityGroups[0].GroupId;
return sgId;
};
});
};
// MAIN FUNCTION
exports.handler = (event, context) => {
getSgIdFromEvent(event)
.then(sgId => {console.log(sgId)});
}
"sgId" should return the security group ID. It does print out fine in the original function before the return.
Typically if it is an async call you want you handle it similar to this way without using a callback
// Load Modules
const AWS = require('aws-sdk')
//Set the region
AWS.config.update({ region: 'us-west-2' });
// Call AWS Resources
const ec2 = new AWS.EC2();
// Get Security Group ID From Event
const getSgIdFromEvent = async (event) => {
var ec2params = { Filters: [{ Name: 'tag:t_whitelist', Values[event['site']]}] };
try {
const securityGroupsDesc = await ec2.describeSecurityGroups(ec2params).promise();
const sgId = securityGroupsDesc.SecurityGroups[0].GroupId;
//do something with the returned result
return sgId;
}
catch (error) {
console.log('handle error');
// throw error;
}
});
};
// MAIN FUNCTION
exports.handler = (event, context) => {
getSgIdFromEvent(event)
.then(sgId => { console.log(sgId) });
}
however if it doesn't support async you just use the callback to handle the returned data or error without using async function.However Reading into AWS docs you can find that the function ec2.describeSecurityGroups() returns an AWS Request
which has a method promise() that needs to be invoked to send the request and get a promise returned.Note that the try catch here is not needed but good to have in case error occurs during the process.
As I said in the comment, chance are that describeSecurityGroups doesn't return a Promise. Try transforming it explictly in a Promise instead:
const promiseResponse = await new Promise((res, rej) => {
ec2.describeSecurityGroups(ec2params, function (err, response) {
if (err) {return rej(err.message)}
else {
var sgId = response.SecurityGroups[0].GroupId;
res(sgId);
};
})
});
// promiseResponse is now equal to sgId inside the callback
return promiseResponse; // this will work because the function is async
Note: You can drop the else keyword
Here is the code that worked using async / await. Thanks to #Cristian Traina I realized ec2.describeSecurityGroups wasn't returning a promise, it was returning an AWS.Event.
// Get Security Group ID From Event
const getSgIdFromEvent = async (event) => {
console.log('Getting Security Group ID')
var params = { Filters: [{Name: 'tag:t_whitelist', Values
[event['site']]}]};
const describeSG = await ec2.describeSecurityGroups(params).promise();
return describeSG.SecurityGroups[0].GroupId;
};
// Get Ingress Rules from Security Group
const getSgIngressRules = async (sgId) => {
console.log(`Getting SG Ingress rules for ${sgId}`)
var params = { GroupIds: [ sgId]};
try{
const ingressRules = await ec2.describeSecurityGroups(params).promise();
return ingressRules;
}
catch (error) {
console.log("Something went wrong getting Ingress Ruls");
}
};
// MAIN FUNCTION
exports.handler = (event, context) => {
getSgIdFromEvent(event)
.then(sgId => {return getSgIngressRules(sgId);})
.then(ingressRules => {console.log(ingressRules);});
}
I submitted this as the answer now since the getSgIdFromEvent function I have, is only 8 lines and still using the async/await like I was desiring.
What I was missing was the .promise() on the end of the function and returning that promise.
Thanks for all the responses!

Mocking using aws-sdk-mock's promise support with DocumentClient

I'm trying to write a unit test using aws-sdk-mock's promise support. I'm using DocumentClient.
My code looks like this:
const docClient = new AWS.DynamoDB.DocumentClient();
const getItemPromise = docClient.get(params).promise();
return getItemPromise.then((data) => {
console.log('Success');
return data;
}).catch((err) => {
console.log(err);
});
My mock and unit test looks like this:
const AWS = require('aws-sdk-mock');
AWS.Promise = Promise.Promise;
AWS.mock('DynamoDB.DocumentClient', 'get', function (params, callback)
{
callback(null, { Item: { Key: 'test value } });
});
dynamoStore.getItems('tableName', 'idName', 'id').then((actualResponse) => {
// assertions
done();
});
Runnning my unit test, does not return my test value, it actually bypasses my mock, and calls calls dynamoDb directly. What am I doing wrong? How can I get my mock set up properly?
It's unclear from your code but aws-sdk-mock has this note
NB: The AWS Service needs to be initialised inside the function being tested in order for the SDK method to be mocked
so the following will not mock correctly
var AWS = require('aws-sdk');
var sns = AWS.SNS();
var dynamoDb = AWS.DynamoDB();
exports.handler = function(event, context) {
// do something with the services e.g. sns.publish
}
but this will
var AWS = require('aws-sdk');
exports.handler = function(event, context) {
var sns = AWS.SNS();
var dynamoDb = AWS.DynamoDB();
// do something with the services e.g. sns.publish
}
see more here https://github.com/dwyl/aws-sdk-mock#how-usage
It might be too late for an answer, but I had the same problem and I stumbled upon this question. After a few tries I found a solution that doesn't involve aws-sdk-mock but only plain Sinon, and I hope that sharing it would help someone else. Note that the DynamoDB client is create outside the lambda.
The lambda itself looks like this:
const dynamoDB = new DynamoDB.DocumentClient();
exports.get = async event => {
const params = {
TableName: 'Tasks',
Key: {
id: event.pathParameters.id
}
};
const result = await dynamoDB.get(params).promise();
if (result.Item) {
return success(result.Item);
} else {
return failure({ error: 'Task not found.' });
}
};
And the test for this lambda is:
const sandbox = sinon.createSandbox();
describe('Task', () => {
beforeAll(() => {
const result = { Item: { id: '1', name: 'Go to gym'}};
sandbox.stub(DynamoDB.DocumentClient.prototype, 'get').returns({promise: () => result});
});
afterAll(() => {
sandbox.restore();
});
it('gets a task from the DB', async () => {
// Act
const response = await task.get(getStub);
// Assert
expect(response.statusCode).toEqual(200);
expect(response.body).toMatchSnapshot();
});
});
I like to use Sinon's sandbox to be able to stub a whole lot of different DynamoDB methods and clean up everything in a single restore().
sinon and proxyquire can be used to mock the dynamodb client.
It supports both callback based and async/await based calls.
Refer this link for full details
https://yottabrain.org/nodejs/nodejs-unit-test-dynamodb/
Somewhat related to the question, expanding wyu's solution - i too faced similar issue - for me, below didn't work with aws-sdk-mock
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
let call = function (action, params) {
const dynamoDb = new AWS.DynamoDB.DocumentClient();
return dynamoDb[action](params).promise();
};
where as this worked
let call = function (action, params) {
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
const dynamoDb = new AWS.DynamoDB.DocumentClient();
return dynamoDb[action](params).promise();
};
I had exactly the same problem of mock failing but resolved the issue after following the suggestion by a user who above by moving the following line within the function rather than defining outside:
let sns = new AWS.SNS(.....)

Resources