AWS SDK runinstance and IAM roles - amazon-web-services

The code below works when I have added the AWS IAM role "AdministratorAccess" - But it is risky and a bit of overkill... But how do I know and find only the necessary role(s)...It is very confusing and hard to know by when I look at all the possible roles in the console?
try {
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'us-east-2'});
var instanceParams = {
ImageId: 'ami-xxxxxxxxxxxx',
InstanceType: 't2.micro',
KeyName: 'xxxxxxxxxx',
SecurityGroups: ['xxxxxxxxxxxxxxx'],
MinCount: 1,
MaxCount: 1
};
// Create a promise on an EC2 service object
var instancePromise = new AWS.EC2({apiVersion: '2016-11-15'}).runInstances(instanceParams).promise();
// Handle promise's fulfilled/rejected states
instancePromise.then(
function (data) {
console.log(data);
var instanceId = data.Instances[0].InstanceId;
console.log("Created instance", instanceId);
// Add tags to the instance
var tagParams = {
Resources: [instanceId], Tags: [
{
Key: 'Name',
Value: 'SDK Sample'
}
]
};
// Create a promise on an EC2 service object
var tagPromise = new AWS.EC2({apiVersion: '2016-11-15'}).createTags(tagParams).promise();
// Handle promise's fulfilled/rejected states
tagPromise.then(
function (data) {
console.log("Instance tagged");
}).catch(
function (err) {
console.error(err, err.stack);
});
}).catch(
function (err) {
console.error(err, err.stack);
});
}
catch(e){
wl.info('Error: ' + e);
}

Firstly you can see the api's you are calling via the sdk as a hint to what permissions you need i.e ec2:RunInstance and ec2:CreateTags.
You first create a policy then select the service then attach permissions (RunInstances and CreateTags)
You then create a Role with that policy attached.
Then you can attach the role to your Lambda

Related

Creating API key for Usage Plan from AWS Lambda

I would like to create a new api key from lambda. I have usage plan with my Gateway API, created with CF like:
MyApi:
Type: AWS::Serverless::Api
Properties:
Auth:
UsagePlan:
UsagePlanName: MyUsagePlan
CreateUsagePlan: PER_API
...
...
Using this as a reference https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/APIGateway.html
I guess the process in the lambda should be like this:
- createApiKey
- getUsagePlan
- createUsagePlanKey
In the lambda, I have MyApi id and I'm trying to fetch the api:
var apiGateway = new AWS.APIGateway({region: region});
const restApi = await new Promise((resolve, reject) => {
apiGateway.getRestApi({restApiId: MYAPI_ID}, function(err, data) {
if (err) {
console.log('getRestApi err', err, err.stack);
reject(err);
} else {
console.log('getRestApi', data);
resolve(data);
}
});
});
But this gets timed out by my lambda.
If I try to input values manually, it gets timed out as well:
const keyParams = {
keyId: 'xxxxxxxx',
keyType: 'API_KEY',
usagePlanId: 'yyyyyyyy'
};
const apiKey = await new Promise((resolve, reject) => {
apiGateway.createUsagePlanKey(keyParams, function (err, data) {
if (err) {
console.log('createUsagePlanKey err', err, err.stack);
reject(err);
} else {
console.log('createUsagePlanKey', data);
resolve(data);
}
});
});
Why do every function call to api get timed out and nothing gets printed in console.log? Is my approach ok or how should I create the new api key for a user?
Edited: Timeout for lambdas is 10 seconds and they run in VPC
It sounds like you probably haven't configured your VPC to allow your Lambda function to access resources (like the AWS API) that exist outside the VPC. First, is it really necessary to run the function inside a VPC? If not then removing it from the VPC should fix the issue.
If it is necessary to run the function in a VPC, then you will need to place your Lambda function inside a private subnet with a route to a NAT Gateway, or configure a VPC endpoint for the AWS services it needs to access.

access all ec2 cross region via lambda

I have lambda function for auto Ami backup is possible to execute lambda across the region for take automatic backup of all my EC2 working on account.
One lambda function execution for all ec2 across region
var aws = require('aws-sdk');
aws.config.region = 'us-east-1','ap-south-1','eu-central-1';
var ec2 = new aws.EC2();
var now = new Date();
date = now.toISOString().substring(0, 10)
hours = now.getHours()
minutes = now.getMinutes()
exports.handler = function(event, context) {
var instanceparams = {
Filters: [{
Name: 'tag:Backup',
Values: [
'yes'
]
}]
}
ec2.describeInstances(instanceparams, function(err, data) {
if (err) console.log(err, err.stack);
else {
for (var i in data.Reservations) {
for (var j in data.Reservations[i].Instances) {
instanceid = data.Reservations[i].Instances[j].InstanceId;
nametag = data.Reservations[i].Instances[j].Tags
for (var k in data.Reservations[i].Instances[j].Tags) {
if (data.Reservations[i].Instances[j].Tags[k].Key == 'Name') {
name = data.Reservations[i].Instances[j].Tags[k].Value;
}
}
console.log("Creating AMIs of the Instance: ", name);
var imageparams = {
InstanceId: instanceid,
Name: name + "_" + date + "_" + hours + "-" + minutes,
NoReboot: true
}
ec2.createImage(imageparams, function(err, data) {
if (err) console.log(err, err.stack);
else {
image = data.ImageId;
console.log(image);
var tagparams = {
Resources: [image],
Tags: [{
Key: 'DeleteOn',
Value: 'yes'
}]
};
ec2.createTags(tagparams, function(err, data) {
if (err) console.log(err, err.stack);
else console.log("Tags added to the created AMIs");
});
}
});
}
}
}
});
}
where aws.config.region is for region config..it's working for current(in which lambda deploy) region
This line:
var ec2 = new aws.EC2();
connects to the Amazon EC2 service in the region where the Lambda function is running.
You can modify it to connect to another region:
var ec2 = new AWS.EC2({apiVersion: '2006-03-01', region: 'us-west-2'});
Thus, your program could loop through a list of regions (from ec2.describeRegions), creating a new EC2 client for the given region, then running the code you already have.
See: Setting the AWS Region - AWS SDK for JavaScript
In your Lambda Role, you need to add a policy which gives the Lambda function necessary permissions to access the EC2 on different accounts, typically you can add ARN's of EC2 instances you wan't access to or you can specify "*" which gives permissions to all instances.
Also on other accounts where EC2 instances are running you need to add IAM policy which gives access to your Lambda Role, note that you need to provide Lambda role ARN,
In this way your Lambda role will have policy to access EC2 and cross account EC2 will have policy which grant's access to Lambda role.
Without this in place you might need to do heavy lifting of configuring IP's of each EC2 in each account.
Yes and you also need to point EC2 object to a region where the instance is running,
Any code (including a Lambda function) can create a client that connects to a different region.

Adding s3 object tags using a lambda function?

The documentation describes how to tag an s3 object via the console. How do we do it programmatically with a lambda function?
If you are using JavaScript in your Lambda you are good to use: s3.putObjectTagging
Documentation Snippet
/* The following example adds tags to an existing object. */
var params = {
Bucket: "examplebucket",
Key: "HappyFace.jpg",
Tagging: {
TagSet: [
{
Key: "Key3",
Value: "Value3"
},
{
Key: "Key4",
Value: "Value4"
}
]
}
};
s3.putObjectTagging(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
/*
data = {
VersionId: "null"
}
*/
});
You can also use ResourceGroupsTaggingAPI for that:
const resourcegroupstaggingapi = new AWS.ResourceGroupsTaggingAPI();
resourcegroupstaggingapi.tagResources({
ResourceARNList: [
'arn:aws:s3<...>',
'arn:aws:s3<...>'
],
Tags: {
'SomeTagKey': 'Some tag value',
'AnotherTagKey': 'Another tag value'
}
}, (err, result) => {
// callback
});
If you're using AWS SDK for node to interact with S3, it could be done by simply adding the Tagging field to your object that would be put in the S3 bucket.
// Create an object to be sent to S3
var params = {
Body: <Binary String>,
Bucket: "examplebucket",
Key: "HappyFace.jpg",
Tagging: "key1=value1&key2=value2"
};
// Put the params object to s3
s3.putObject(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
console.log(data); // successful response
}
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html
You can create a Lambda function that is able to automatically tag all assets in an Amazon S3 bucket. For example, assume you have an asset like this:
After you execute the Lambda function, it creates tags and applies them to the image.
For more information, click Creating an Amazon Web Services Lambda function that tags digital assets located in Amazon S3 buckets.

Authenticating and Running an EC2 Instance: AWS

I am attempting to create an EC2 instance and then add it to my Auto Scaling group. I am having a lot of issues trying to authenticate. I am looking for a simple way to authenticate a request using my access key to simply start an instance. What I have tried so far:
//Authenticate AWS:
var myCredentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:'us-west-2:IdentityPoolID'
});
var myConfig = new AWS.Config({
credentials: myCredentials, region: 'us-west-2'
});
AWS.config = myConfig
var minInst = 1;
var maxInst = 3;
var ec2 = new AWS.EC2();
//Set up parameters for EC2 Instances:
var params = {
ImageId: 'ami-6e1a0117',
MaxCount: minInst,
MinCount: maxInst,
InstanceInitiatedShutdownBehavior: 'terminate',
InstanceType: 't2.micro',
Monitoring: {
Enabled: false
},
NetworkInterfaces: [{
AssociatePublicIpAddress: true,
DeleteOnTermination: true,
}],
Placement: {
AvailabilityZone: 'us-west-2',
},
SecurityGroupIds: [
'sg-b0307ccd',
],
SecurityGroups: [
'CAB432Assignment2SG',
],
};
ec2.runInstances(params, function(err, data) {
if (err){
console.log(err, err.stack); //An error occurred
}
else{
console.log(data); //Successful Response
}
});
I know this code is wrong. I just don't know to fix it. The error I get is:
CredentialsError: Missing credentials in config
Any help would be greatly appreciated.
Delete this section of code entirely:
//Authenticate AWS:
var myCredentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:'us-west-2:IdentityPoolID'
});
var myConfig = new AWS.Config({
credentials: myCredentials, region: 'us-west-2'
});
AWS.config = myConfig
Change this:
var ec2 = new AWS.EC2();
To this:
var ec2 = new AWS.EC2({region: 'us-west-2'});
Then go read this page in the Setting Credentials in Node.js documentation. In particular, you need to do one of the following:
Add an IAM role to your EC2 instance, if this is running on EC2.
Add an IAM execution role to your Lambda function if this is running on Lambda.
Create either a ~/.aws/credentials file with your keys. This can be done with the aws configure command if you have the AWS CLI installed.
Set the keys as environment variables.

How to call AWS API Gateway Endpoint with Cognito Id (+configuration)?

I want to call an AWS API Gateway Endpoint that is protected with AWS_IAM using the generated JavaScript API SDK.
I have a Cognito UserPool and a Cognito Identity Pool. Both properly synced via ClientId.
I use this code to Sign in and get the Cognito Identity
AWS.config.region = 'us-east-1'; // Region
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXXXXXX' // your identity pool id here
});
AWSCognito.config.region = 'us-east-1';
AWSCognito.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXXXXXX' // your identity pool id here
});
var poolData = {
UserPoolId: 'us-east-1_XXXXXXXX',
ClientId: 'XXXXXXXXXXXXXXXXXXXXXXXX'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var authenticationData = {
Username: 'user',
Password: '12345678',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: 'user',
Pool: userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:XXXXXXXXXXXXXXXXXXXX',
IdentityId: AWS.config.credentials.identityId,
Logins: {
'cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXX': result.idToken.jwtToken
}
});
AWS.config.credentials.get(function (err) {
// now I'm using authenticated credentials
if(err)
{
console.log('error in autheticatig AWS'+err);
}
else
{
console.log(AWS.config.credentials.identityId);
}
});
},
onFailure: function (err) {
alert(err);
}
});
All this succeeds and I have an authorized Cognito Identity now.
Now I try to call the API Gateway Endpoint to execute the Lambda Function it points to.
var apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId, //'ACCESS_KEY',
secretKey: AWS.config.credentials.secretAccessKey, //'SECRET_KEY',
sessionToken: AWS.config.credentials.sessionToken, // 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'us-east-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1
});
var params = {
// This is where any modeled request parameters should be added.
// The key is the parameter name, as it is defined in the API in API Gateway.
};
var body = {
// This is where you define the body of the request,
query: '{person {firstName lastName}}'
};
var additionalParams = {
// If there are any unmodeled query parameters or headers that must be
// sent with the request, add them here.
headers: {},
queryParams: {}
};
apigClient.graphqlPost(params, body, additionalParams)
.then(function (result) {
// Add success callback code here.
console.log(result);
}).catch(function (result) {
// Add error callback code here.
console.log(result);
});
But unfortunately this fails. The OPTIONS request succeeds with 200 but the POST then fails with 403.
I am pretty sure that there is no CORS problem here.
I am pretty sure the problem has to do with IAM Roles and AWS Resource Configurations.
My question is basically, can you please provide me with all the necessary AWS Resource Configurations and IAM Roles that are necessary for this to work please?
Resources I have are
API Gateway - with deployed API Endpoints
Lambda Function - called by the Endpoint
Cognito User Pool - with App synced to the Identity Pool
Cognito Identity Pool - with Authorized and Unauthorized Role mapped to it.
IAM Roles - for the Lambda Function and the Authorized and Unauthorized Role of the Cognito Identity Pool.
But I don't know how these Resources need to be configured properly to get this to work.
Thank you
What access permissions does the role of the Cognito Identity have? Make sure it has access to perform execute-api:Invoke on your API.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:us-east-1:<account>:<rest-api>/*/POST/graphql"
]
}
]
}
You can get the exact resource ARN from the method settings page in the web console.
Even after following everything I was getting the same error. And the reason was I missed the "sessionToken" while initialising the apigClient.
var apigClient = apigClientFactory.newClient({
accessKey: AWS.config.credentials.accessKeyId, //'ACCESS_KEY',
secretKey: AWS.config.credentials.secretAccessKey, //'SECRET_KEY',
sessionToken: AWS.config.credentials.sessionToken, // 'SESSION_TOKEN', //OPTIONAL: If you are using temporary credentials you must include the session token
region: 'us-east-1' // OPTIONAL: The region where the API is deployed, by default this parameter is set to us-east-1 });
//OPTIONAL: If you are using temporary credentials you must include the session token -- is not really optional