Warm tip: This article is reproduced from stackoverflow.com, please click
amazon-web-services node.js aws-sdk-nodejs assume-role

NodeJS: aws-sdk sts.assumeRole is not working

发布于 2020-03-27 15:46:38

I have a weird problem where I can't seem to go anywhere with. I have followed everything online (multiple tutorials etc) but can't get sts.assumeRole to work.

My set-up:

  • Main AWS account (used for billing, IAM etc)
  • Sub AWS account 1 (used for customer 1)
  • Sub AWS account 2 (used for customer 2)
  • etc

I have a bot running in Sub AWS account 1 where I need pricing information from the Main AWS account.

So, I created a role on the Main AWS account to have AWS ce:* access, below:

{
    "Version": "2012-10-17",
    "Statement": [{
            "Effect": "Allow",
            "Action": ["ce:*"],
            "Resource": ["*"]
        }]
}

Trust Relationship

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com",
        "AWS": "arn:aws:iam::SubAWSaccount1-ID:role/instance-profile"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Now, this is the instance-profile attached to the EC2 instance running on the Sub AWS account 1:

"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "VisualEditor1",
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::mainAWSaccount-ID:role/botRole"
    }
  ]
}

Trust relationship

{
  "Version": "2012-10-17",
  "Statement": [
   {
    "Sid": "",
    "Effect": "Allow",
    "Principal": {
      "Service": "ec2.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
   }
  ]
}

Okay, that being out of the way, this is the code that is not working:

var AWS = require('aws-sdk');
var pricingAWS = new AWS.CostExplorer();

var getPricing = function(){

    var costParams = {
        TimePeriod: { Start: "2019-12-01 ", End: "2020-01-01"},
        Granularity: 'MONTHLY',
        Metrics: ['BlendedCost'],
        Filter: {      
            Dimensions: {
                Key: "LINKED_ACCOUNT",
                Values: ["Sub AWS account 2s ID"] //Main AWS account has access to many sub accounts info. So bot in running on Sub account 1, I need info from Sub account 1, 2, 3 etc and Role is on Main account
            }
        }
    };

    var roleToAssume = {
        RoleArn: 'arn:aws:iam::mainAWSaccount-ID:role/botRole',
        RoleSessionName: 'testTestTest',
        DurationSeconds: 900
    };

    var roleCreds = {};

    sts.assumeRole(roleToAssume, function(err, data) {
        if (err) {
            console.log(err);
        } else {
            roleCreds = {
                accessKeyId: data.Credentials.AccessKeyId,
                secretAccessKey: data.Credentials.SecretAccessKey,
                sessionToken: data.Credentials.SessionToken
            };

            pricingAWS.config.update({
                accessKeyId: roleCreds.AccessKeyId,
                secretAccessKey: roleCreds.SecretAccessKey,
                sessionToken: roleCreds.SessionToken
              });
        }
    });

    pricingAWS.getCostAndUsage(costParams, function (err, data) {
        if (err) {
            reject(err);
        }else{
            resolve(data);
        }
    });
};

getPricing();

What I get from the console.log(err)is:

User: arn:aws:sts::SubAWSAccount-1:assumed-role/slack-instance-profile/instanceID is not authorized to perform: ce:GetCostAndUsage on resource: arn:aws:ce:us-east-1:SubAWSAccount-1:/GetCostAndUsage'
Questioner
Shayan Khan
Viewed
206
Arun K 2020-02-04 18:51

to confirm my understanding, you are trying to assume a role in one account from an ec2 instance in another account.

  1. create an IAM role in the child account
  2. allow the newly created role above to assume the role exists in the main account by attaching an IAM policy. Allow the child role to assume the main role via the main role's trust policy (you are doing this already)
  3. In your Nodejs code running in the child ec2 instance, assume the parent role and read the billing reports (you are already doing this)

code fixes

Move the pricingAWS.getCostAndUsage() line inside assume role callback. because the line is called before the credentials are updated :). You can also change everything to async/await as I showed below.

import AWS = require('aws-sdk');
var pricingAWS = new AWS.CostExplorer();
const sts = new AWS.STS()

var getPricing = async function () {

  var costParams = {
    TimePeriod: { Start: "2019-12-01 ", End: "2020-01-01" },
    Granularity: 'MONTHLY',
    Metrics: ['BlendedCost'],
    Filter: {
      Dimensions: {
        Key: "LINKED_ACCOUNT",
        Values: ["Sub AWS account 2s ID"] //Main AWS account has access to many sub accounts info. So bot in running on Sub account 1, I need info from Sub account 1, 2, 3 etc and Role is on Main account
      }
    }
  };

  var roleToAssume = {
    RoleArn: 'arn:aws:iam::mainAWSaccount-ID:role/botRole',
    RoleSessionName: 'testTestTest',
    DurationSeconds: 900
  };

  var roleCreds = {};


  try {

    const data = await sts.assumeRole(roleToAssume).promise();
    const roleCreds = {
      accessKeyId: data.Credentials.AccessKeyId,
      secretAccessKey: data.Credentials.SecretAccessKey,
      sessionToken: data.Credentials.SessionToken
    };

    pricingAWS.config.update({
      accessKeyId: roleCreds.accessKeyId,
      secretAccessKey: roleCreds.secretAccessKey,
      sessionToken: roleCreds.sessionToken
    });

    const costAndUsage = await pricingAWS.getCostAndUsage(costParams).promise()
    return costAndUsage
  } catch (err) {
    console.log('error: ', err);
    throw err;
  }

};

getPricing();

hope this helps.