AWS IAM roles and policies explained

In this article, I am going to explain the essential parts of IAM and describe how to grant permissions to your users or AWS Lambda functions you wrote.

Users and groups

Those two concepts are easy because almost every computer system that has users allows us to group them and specify permissions of the entire group.

Let’s start with the users. In IAM, the user is someone or something that has credentials and can be authenticated by AWS. We may create user accounts for people and software which need access to AWS.

We may assign those user accounts to groups. When we have a group of users, we may specify that the whole group has access to some service.

Roles

In addition to groups, we have user roles. A role is just another way of granting permission (or a set of permissions) to a user. The critical distinction is the fact that we can give a role not only to a user account but also to many AWS entities such as AWS Lambda, EC2 servers, etc.

When we want to give access rights to a Lambda function, the correct way to do it is to define a role for that function (or use the one created by default when you don’t specify an existing one) and grant permissions to that role.

Similarly, when we run a custom application on an EC2 server, we don’t create a user account for that application and hardcode the credentials in application code (seriously, don’t ever do it). Instead of that, we add the access rights to the IAM role used by that EC2 instance.

Policies and permissions

I used the word “permission” a couple of times, but they don’t exist in AWS. Instead of permissions, we define what actions are allowed (or denied) and what resources can be accessed while performing those actions.

To define permissions, we must create a policy. The policy consists of a set of allowed actions. We may be very specific and describe precisely what can be done and what can be used to do it.

{
  "Version": "2012-10-17",
  "Sid": "ReadOnlyAccessToS3BucketAndDynamoDB",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "s3:ListBucket",
            "s3:GetObject",
            "dynamodb:GetItem"
        ],
        "Resource": [
            "arn:aws:s3:::some-bucket",
            "arn:aws:s3:::some-bucket/*",
            "arn:aws:dynamodb:eu-central-1:1234567890:table/sometable"
        ]
    }
  ]
}

On the other hand, we may define a very lax policy by using the * sign instead of a specific action or resource name. For example, the following policy allows performing every available action on every resource in S3.

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

Just to be clear, don’t copy it. You should always provide access to data on a need-to-know basis and give the least permissive rights that allow getting the job done.

It is crucial to know that if we define both allowed and denied actions, the denied actions take priority, and overwrite permitted actions.

{
  "Version": "2012-10-17",
  "Sid": "DontReadThatOneFile",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "s3:ListBucket",
            "s3:GetObject"
        ],
        "Resource": [
            "arn:aws:s3:::some-bucket",
            "arn:aws:s3:::some-bucket/*"
        ]
        },
    {
        "Effect": "Deny",
        "Action": "s3:GetObject",
        "Resource": [
            "arn:aws:s3:::some-bucket/cant-touch-this"
        ]
    }
  ]
}

When we have a policy, we can give access rights to users (or EC2, lambdas, other AWS services) by assigning that policy to a user account, group, or role. You can do it either using the web interface or via AWS CLI:

# in this example, we attach an existing policy to a role
aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/DontReadThatOneFile --role-name SomeRoleName
Older post

How to be happy at work - lessons learned from "Career superpowers" book

What can you learn from the book "Career superpowers" by James Whittaker

Newer post

Data flow - what functional programming and Unix philosophy can teach us about data streaming

How to write data stream processing code that is easy to maintain