How do AWS Assumed Roles work?

A brief conceptual overview

Martin Jahr
5 min readMay 10, 2019

When I started working with AWS and learning how to create meaningful results, I ran into all the pitfalls — first I tried to click things together in the console app, and lost quickly the overview of what I had done. I got some of that insight back when I got my first bill without knowing that I had spent that much. Painful lesson, which brought me to think about automated provision, structured inventory and integrated cost control.

The third big issue was and is security. Having read the AWS well-architectured framework concept, I knew that this is an important topic that needs to be designed into your structures from the beginning. But of course I ignored that advice the same way as automation and cost control. But now I start with one topic that was somewhat mysterious for me — the delegation of permissions to other services.

How is that working? AWS provides “Assumed Roles” for that purpose.

It took me some time to grasp the exact meaning of that phrase. Having cheated around that concept for quite some time now (there’s so much sample code in the net, right?), I now became to understand the inner structure.

Let’s start at quite at the beginning. A role is a foundational AWS security concept, used to provide access to other AWS resources. It is an identy, this means we can identify it by its name. So it’s a convenient way to bundle different security permissions. We talk about permissions later.

“Assuming a role” resembles simply spoken a wolf in sheep’s clothing. This means another identity (like a user or a service) disguises itself as if it would “be” that role.

However, knowing the unpleasant properties of wolves when it comes to security issues, AWS made it mandatory that the role needs to trust the assuming identity before it can be slipped over.

In technical terms, the following trust relationship enables a service of the named type (EC2 in this case) to assume the role to which this trust relationship is attached to. The assuming identity is called Principal.

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

Why does the sheep trust the wolf, you might wonder. Even that aspect of complicated and potentially dangerous relationships have the AWS designers considered. They ask another instance (you as the security designer in that case) to permit that the instance becomes effective.

Ths means you could decide to assign the permission to use the trust relationship to the ski driver wearing a nordic wool sweater but not to the wolf.

The technical view show that the role should be explicitly stated in your permission, restricting the amount of sheep that you could slip into if allowed. The account is to be referenced in that case, too. (Yes, you could potentially allow the neighbor to steal your sheep too, but this goes beyond the scope of this story.)

{
"Version": "2012–10–17",
"Statement":
[
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::YOURACCOUNT:role/THEROLENAME"
}
]
}
Assume-Role Usage Permission Policy

So, now we can allow the Principal (wolf) to grab the role (sheep skin) when he needs it. To make sure he doesn’t lose that permission, we put it in a permission policy and attach it to the instance that’s planning to use it.

This policy is now somehow “glued” to a service resource. The official speak for this type of policy is “resource-based policy”.

You find the places where you can attach resource-based policies sprinkled over the console app at the proper places (e.g, in the Actions Menu of the EC2 Instances view. The item in question is “Instance Settings — Attach/Replace IAM Role”. When working with CLI and SDK (remember, we’re still into automation), use attach-principal-policy and AttachPolicy respectively.

Now the wolf has passed all fences and wants to actually do something besides just wearing that strange skin. To allow him eating grass (or any other typical thing the actual Role suggests), we need to attach permissions to the role.

You surely have done that already in the IAM part of the console application.

Since we now attach the policy to a role rather than a principal, we enable the role identity to to do whatever the permission is. For that reason, these types of policies are named “identity-based policies”.

In my case, the sheep is just performing some operations on an S3 bucket:

{
"Version": "2012–10–17",
"Statement":
[
{
"Effect": "Allow",
"Resource": "arn:aws:s3:::MYBUCKET",
"Action": "s3:*"
}
]
}
Allow-what-you-initially-wanted-the-CLI-to-do Permission Policy

Phew — not that easy, going into sheeps’ businesses.

To summarize the security structure for Assumed Roles:

  1. Create a role
  2. Create a trust relationship attached to that role (either in the app or programmatically).
  3. Create a permission policy that allows an instance to assume the role, and attach it to the instance.
  4. Create a permission policy that allows the role to do what you need to do, and attach it to the role.

In a another story I show where I applied that knowledge — “How to use AWS CLI without user credentials on EC2”.

Hope you found that useful!

--

--

Martin Jahr

Digital Designer & life-long learner of computers & humans. Now up to create, coach and deliver learning deployment strategies in Germany where things are late.