Skip to main content

AWS Basics - IAM Roles

· 6 min read
Rajiv Seelam

Assuming you have understood the basics of IAM Users and Policies, we start with IAM Roles.

An IAM role is an IAM identity that you can create in your account that has specific permissions

This is where AWS starts using different terms to mean same thing in different contexts. In above definition when they say "AWS Identity", they mean anything (users, applications, roles) that can perform "actions" on "resources".

When you create an IAM User you get access keys and that's how you could programmatically perform actions (ex: get list of users). When you create a role, a principal (user/application) can assume the role and perform actions via that role. When a principal assumes a role, they get temporary credentials which are used to make API Calls.

Let's get straight into this concept using an example. We will do following exercise:

  • Create an IAM User with name "Jill" [use iam.createUser]
  • Get credentials for Jill [use createAccessKey]
  • Get list of users as Jill [use iam.listUsers]
  • Create a Policy which allows to get users [use iam.createPolicy]
  • Create a Role [use iam.createRole]
  • Attach policy to the role [use iam.attachRolePolicy]
  • Assume the role [use sts.assumeRole]
  • Get list of users [use iam.listUsers]

Create user (Jill)

const AWS = require("aws-sdk");

var iam = new AWS.IAM();

function createUser(username) {
var params = {
UserName: username
};

iam.getUser(params, (err, data) => {
if (err && err.code === "NoSuchEntity") {
iam.createUser(params, (err, data) => {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
} else {
console.log("Already exists");
}
});
}

createUser("Jill");

You will get a response similar to the following:

Success {
ResponseMetadata: { RequestId: '249610e9-6c53-473e-96e1-03f0534c821f' },
User: {
Path: '/',
UserName: 'Jill',
UserId: 'AIDAXTRL34HMDOFKQ7PDS',
Arn: 'arn:aws:iam::523004273112:user/Jill',
CreateDate: 2019-12-14T15:38:58.000Z,
Tags: []
}
}

Note: Note down the Arn from above response

Next step, create credentials for Jill

function createAccessKey(username) {
iam.createAccessKey({ UserName: username }, (err, data) => {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.AccessKey);
}
});
}

createAccessKey("Jill");

You will see a response similar to following:

Success {
UserName: 'Jill',
AccessKeyId: 'AKIACCCCCTRL34HMMBZE56GY',
SecretAccessKey: 'ccccUqSavfDUAZtDjY6R8AQ6y6jUi7NM3it4C/jf7Y',
}

Create a file with name access-jill.json in the same folder and put following content and replace with your AccessKeyId and SecretAccessKey:

{
"accessKeyId": "AKIACCCCCTRL34HMMBZE56GY",
"secretAccessKey": "ccccUqSavfDUAZtDjY6R8AQ6y6jUi7NM3it4C/jf7Y",
"region": "ap-south-1"
}
Get list of users as Jill
const AWS = require("aws-sdk");
AWS.config.loadFromPath("./access-jill.json");

function getListOfUsers() {
var iam = new AWS.IAM();

iam.listUsers({}, (err, data) => {
if (err) {
console.log(err.message);
} else {
console.log(data);
}
});
}

getListOfUsers();

You will see a response similar to:

User: arn:aws:iam::523004273112:user/Jill is not authorized to perform: iam:ListUsers on resource: arn:aws:iam::523004273112:user/

Jill couldn't get list of users because Jill doesn't have enough permissions.

Create a policy

Let's create a policy which gives permission to get list of users

const AWS = require("aws-sdk");

function createListUsersPolicy() {
var myManagedPolicy = {
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: "iam:ListUsers",
Resource: "*"
}
]
};

var params = {
PolicyDocument: JSON.stringify(myManagedPolicy),
PolicyName: "getListOfUsers"
};

var iam = new AWS.IAM();

iam.createPolicy(params, (err, data) => {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
}

createListUsersPolicy();

You would get a similar response as following, please note down the Arn

Success {
ResponseMetadata: { RequestId: 'cccd8168-f5fd-410b-b3e0-4f04fe56f444' },
Policy: {
...
PolicyName: 'getListOfUsers',
Arn: 'arn:aws:iam::523004999112:policy/getListOfUsers',
...
}
}

Note down the above Arn

Create a Role

Before we get to creating a role, let's take a little detour. When we create a role, basically, we have two intentions:

  • Intent 1: This role has permissions to perform some actions on some resources.
    • In AWS we achieve this using "Policies"
    • So, once we create a role, we attach the policy to a role (instead of a user)
  • Intent 2: Someone should be able to assume this role
    • AWS allows this via another actions called "sts:AssumeRole"
    • In fact, we do attach a policy which allows permission to perform an action (assume a role).

What we are saying in Intent 2 is that we need another policy which should be attached to Role (while creating role). Let's look at that policy:

{
Version: "2012-10-17",
Statement: {
Effect: "Allow",
Action: "sts:AssumeRole",
Principal: {
AWS: ["arn:aws:iam::523004273112:user/Jill"]
}
}
}

In above policy document we are saying: Allow Jill to assume the role we are above to create. Let's create that role which we are making so much fuss about:

const AWS = require("aws-sdk");

function createRole() {
var policyDocument = {
Version: "2012-10-17",
Statement: {
Effect: "Allow",
Action: "sts:AssumeRole",
Principal: {
AWS: ["arn:aws:iam::523004273112:user/Jill"]
}
}
};

var params = {
AssumeRolePolicyDocument: JSON.stringify(policyDocument),
RoleName: "getUsersRoleForJill"
};

var iam = new AWS.IAM();

iam.createRole(params, (err, data) => {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
}

createRole();

We get a response like following:

Success {
ResponseMetadata: { RequestId: '8a061dd0-e832-4d85-9abd-378eef9e50ea' },
Role: {
Path: '/',
RoleName: 'getUsersRoleForJill',
RoleId: 'AROAXTRL34HMG4C63UJXT',
Arn: 'arn:aws:iam::523004273112:role/getUsersRoleForJill',
CreateDate: 2019-12-14T15:57:15.000Z,
AssumeRolePolicyDocument: '%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%7B%22Effect%22%3A%22Allow%22%2C%22Action%22%3A%22sts%3AAssumeRole%22%2C%22Principal%22%3
A%7B%22AWS%22%3A%5B%22arn%3Aaws%3Aiam%3A%3A523004273112%3Auser%2FJill%22%5D%7D%7D%7D',
Tags: []
}
}

Note down the above Arn

Don't let this overwhelm you, we are very close to finish the exercise.

Attach policy to role

const AWS = require("aws-sdk");

function attachPolicyToRole(policyArn, RoleName) {
var params = {
PolicyArn: policyArn,
RoleName: RoleName
};

var iam = new AWS.IAM();

iam.attachRolePolicy(params, (err, data) => {
if (err) console.log(err, err.stack);
else console.log(data);
});
}

attachPolicyToRole(
"arn:aws:iam::523004273112:policy/getListOfUsers",
"getUsersRoleForJill"
);

Let Jill assume the role and try to get users again

The way we will fetch users has to be a bit different. When we created Jill, we got a Access and Secret keys which we used to make function calls via AWS SDK. This time, we have to send temporary credentials which will get when we assume the role. Two Steps:

  • Assume the role -> As part of response you will get credentials
  • Get list of users with above credentials

The full code goes as follows:

const AWS = require("aws-sdk");
AWS.config.loadFromPath("./access-jill.json");

function getUsers(creds) {
var iam = new AWS.IAM(creds);

var params = {};

iam.listUsers(params, (err, data) => {
if (err) {
console.log(err.message);
} else {
console.log(data);
}
});
}

function assumeRole(roleArn, name) {
var params = {
RoleArn: roleArn,
RoleSessionName: name
};

var sts = new AWS.STS();

return sts
.assumeRole(params)
.promise()
.then(data => {
return {
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken
};
})
.catch(err => {});
}

assumeRole(
"arn:aws:iam::523004273112:role/getUsersRoleForJill",
"jillTemp"
).then(creds => {
getUsers(creds);
});

And as part of response we get list of users.

Hopefully you have understood how to use roles. There are variety of ways we can create roles and attach "AssumeRolePolicyDocument" to it.