George Lutz
George Lutz

AWS, of course, provides an expansive set of services to solve big problems quickly. Identity and Access Management (IAM) is often a speed bump though.

IAM Misconfiguration can waste significant time during development. Even when IAM is configured ‘correctly’, it can be disorganized. It can be that someone broadly granted enough access to get through day. Often this results in allowing more permission than necessary, violating least privilege principle.

This article intends to, very quickly, break down the basic building blocks of IAM so you can use the basics effectively and efficiently. This is not an exhaustive definition of IAM — it hopes cover the 20% of IAM that you need 80% of the time.

There are four primary elements of IAM:

  1. Role Identity — attaches to entities such as AWS Services, or external services/users.
  2. User Identity — one specific individual person.
  3. Group Identity — a set of users who need to share the same permissions. Groups just make it easier to manage permissions for users.
  4. Policy — a JSON document which allows or denies one or many permissions on a principal. A principal can be a role, a user, or a group. Since everything is denied by default, the most common case is that a policy will allow an action.

Here’s a sample policy JSON. Each statement separately grants access to something. Only one statement is required per policy. Three statements are shown here just to be instructive.

{
  "Version": "2012–10–17",
  "Statement": [
    {
      "Sid": "FirstStatement",
      "Effect": "Allow",
      "Action": "iam:ChangePassword",
      "Resource": "*"
    },
    {
      "Sid": "SecondStatement",
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    },
    {
      "Sid": "ThirdStatement",
      "Effect": "Allow",
      "Action": [
        "s3:List*",
        "s3:Get*"
      ],
      "Resource": [
        "arn:aws:s3:::confidential-data",
        "arn:aws:s3:::confidential-data/*"
      ],
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

Each statement includes information about a single permission. If a policy includes multiple statements, AWS applies a logical OR across the statements when evaluating them. If multiple policies apply to a request, AWS applies a logical OR across all of those policies when evaluating them.

That seems like a lot to read though. Let’s focus only on the second statement. Then let’s hide the informational Version and Sid attributes to reduce it even more. Here’s the guts of it:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

It is now simple to see that this policy allows for calling any action (or API) in s3 for any resource (bucket) in s3. This policy can be attached to an Identity — a user, a group, or a role. This is called an Identity-based policy.

Granting such broad s3:* access may be unnecessary though. Let’s add more specifics to scope privilege reasonably.

{
  "Statement": [
    {
       "Effect": "Allow",
       "Action": "S3:ListBucket",
       "Resource": "arn:aws:s3:::example_bucket"
    }
   ]
}

This policy grants only access to the S3 ListBucket API on just a single bucket. If that’s all our user, group, or role requires, then this is more appropriate than granting broad s3:* access.

Notice that specific resources are identified by their ARN. Every single resource in AWS has a unique ARN. Sometimes the naming gets ugly, but that’s what makes it unique.

There’s another type of policy called Resource-based policy. Such policies attach directly to resources. They grant permissions in the other direction from Identity-based policies. They grant permissions to principals (or users, groups, roles) in the policy. The principal in the policy is granted the access. I’m unsure why the term principal is introduced here instead of Identity. It seems confusing.

Let’s quickly contrast the two types of policies directly. Identity-based policies grant access from the identity which is attempting to use a resource. Resource-based policies grant access at the resource itself to the identity attempting to use a resource. (If the identity and the resource are in the same account, then either policy will suffice to grant access. Both are not required.)

So as a user or a role, either I can define my own access to a resource or the resource may have set it up for me on its own, which will save me the effort. That’s the difference between Identity and Resource based policies. Let’s finally examine at a real resource-based policy JSON. Below is an S3 bucket policy. The S3 bucket uses it to define which identities or principals (users, groups, roles) can access specific buckets (resources).

{
  "Version": "2012–10–17",
  "Id": "S3 permissions",
  "Statement": [
    {
      "Sid": "Stmt1612615574428073",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::43668977172699:role/role-s3-admin-access"
      },
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::example_bucket",
        "arn:aws:s3:::example_bucket/*"
      ]
    }
  ]
}

The attributes which actually matter are:

  • The Effect is Allow, as usual — simple.
  • The Principal is the identity which is being granted access — in this case, the identity is a role in my account.
  • The Action is a wildcard on S3, which means all APIs are granted.
  • The Resource is a list of resources to which the policy is granting access.

This all means that any resource in AWS which has this role attached to it will have full access to this S3 bucket. For example, an EC2 instance may have this role attached to it. Then anything running on that instance can access this bucket.

One more tip: I used this example intentionally because the S3 bucket resource arn:aws:s3:::example-bucket/* represents only objects in the example-bucket. Any action related to bucket (e.g. ListBucket) does not apply. We must add bucket resource arn:aws:s3:::example_bucket to our policy. (This is extremely non-obvious to me and it cost me about 3 hours one day.)

I hope that this allows you to more easily consume IAM despite a lot of dry terminology: policies, roles, users, groups, identities, statements, actions, effects, principals, and resources.

One final thing to note is that policies may be created in two ways. AWS Managed and Customer Managed. AWS Managed roles come as read-only on every account. There are hundreds of them to cover common needs. You may create your own custom policies though. Identities are all customer defined though: users, groups, and roles are all account-specific entities.

A few AWS managed policies and one Customer Managed policy.

Good luck. And don’t ever again let IAM turn into Identity and Anger Management.

Share this article: