Call to IAM API in Lambda times out - node.js

I have a NodeJS lambda function that is trying to list IAM users via the IAM listUsers API method . The call is firing off but never returning until the lambda function itself times out and I'm unsure why. Here is the code:
exports.handler = async (event) => {
console.log('before call');
var ret = await listIAMUsers();
console.log('ret: ' + JSON.stringify(ret));
};
async function listIAMUsers() {
var AWS = require('aws-sdk');
AWS.config.update({region: 'REGION'});
var iam = new AWS.IAM({apiVersion: '2010-05-08'});
var params = {MaxItems: 10};
return iam.listUsers(params).promise();
}

Managing IAM Users. Pay close attention where we initialize the client and add require statements.
For the optimization purpose. it is good to load and initialize the. client while loading the function
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'REGION'});
// Create the IAM service object
var iam = new AWS.IAM({apiVersion: '2010-05-08'});
As JS is single-threaded so you reuse the clients.
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
exports.handler = async (event, context) => {
console.log('before call');
var ret = await listIAMUsers();
console.log('ret: ' + JSON.stringify(ret));
};
async function listIAMUsers() {
// Set the region
AWS.config.update({region: 'REGION'});
// Create the IAM service object
var iam = new AWS.IAM({apiVersion: '2010-05-08'});
var params = {MaxItems: 10};
return iam.listUsers(params).promise();
}
START RequestId: 933ca3dd-2feb-4f98-8f8e-87286f59748d Version: $LATEST
2021-02-22T15:57:14.867Z 933ca3dd-2feb-4f98-8f8e-87286f59748d INFO before call
2021-02-22T15:57:15.560Z 933ca3dd-2feb-4f98-8f8e-87286f59748d INFO ret: {"ResponseMetadata":{"RequestId":"bf65a9f5-e4a6-493b-a6a2-c3081d88f7be"},
"Users":[
{"Path":"/","UserName":"username","UserId":"sadsadsadsa",
"Arn":"arn:aws:iam::1234567890:user/username",
"CreateDate":"2020-11-27T13:09:20.000Z","Tags":[]},
{"Path":"/","UserName":"tempuser","UserId":"adsadasdasdsad",
"Arn":"arn:aws:iam::1234567890:user/tempuser",
"CreateDate":"2021-01-24T21:24:47.000Z","Tags":[]}],"IsTruncated":false}
END RequestId: 933ca3dd-2

There are a few possibilities here:
Lambda Role Permission - Try to check if your lambda role is attached with the correct IAM Policy such as arn:aws:iam::aws:policy/IAMReadOnlyAccess
If your Lambda function is placed within a Public Subnet or Private Subnet without any internet access, you would require arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole attached as well as the internet or aws services access (shown here)

Related

Read from Secrets Manager in AWS Lambda function

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

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

Accessing AWS SSM Parameters in NodeJS

I'm trying to get the ssm parameter inside my nodejs project, is IAM credentials I and wrote a test in my elastic beanstalk instance and works. The problem is inside the project. Any ideas why?
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
var ssm = new AWS.SSM();
var options = {
Name: '/test/test', /* required */
WithDecryption: false
};
var parameterPromise = ssm.getParameter(options).promise();
parameterPromise.then(function(data, err) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
I discovered, the same of this https://github.com/localstack/localstack/issues/1107
need to pass the region in the SSM constructor
var ssm = new AWS.SSM({region: 'us-east-1'});
seems to be a bug
tks

How to Configure AWS X-Ray for node.js app not using Express middleware?

I am trying to integrate AWS X-Ray with my nodejs api hosted on AWS Lambda(serverless).
X-Ray works as intended for api using express middleware and able to see traces on AWS Console.
For async functions without express framework, I am facing issues while integration.
Tried enabling Manual mode, but facing- Lambda not supporting manual mode error.
Referred this - Developing custom solutions for automatic mode section but no luck.
Can someone help me out with this?
'use strict';
const AWSXRay = require('aws-xray-sdk-core');
const Aws = AWSXRay.captureAWS(require('aws-sdk'))
const capturePostgres = require('aws-xray-sdk-postgres');
const { Client } = capturePostgres(require('pg'));
module.exports.test = async (event, context) => {
var ns = AWSXRay.getNamespace();
const segment = newAWSXRay.Segment('Notifications_push');
ns.enter(ns.createContext());
AWSXRay.setSegment(segment_push);
.... };
So, when in Lambda, the SDK creates a placeholder (facade) segment automatically. More in-depth explanation here: https://github.com/aws/aws-xray-sdk-node/issues/148
All you need is:
const AWSXRay = require('aws-xray-sdk-core');
//lets patch the AWS SDK
const Aws = AWSXRay.captureAWS(require('aws-sdk'));
module.exports.test = async (event, context) => {
//All capturing will work out of box
var sqs = new AWS.SQS({apiVersion: '2012-11-05'});
var params = {...}
//no need to add code, just regular SQS call
sqs.sendMessage(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.MessageId);
}
});
//if you want to create subsegments manually simply do
const seg = AWSXRay.getSegment();
const subseg = seg.addSubsegment('mynewsubsegment');
subseg.close();
//no need to close the Lambda segment
};
Additional documentation here: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-tracing.html

Resources