AWS - How to deny access to resources while allowing a specific role
Tags: aws, iam, cloud, s3
Published: 2019-10-19 22:02:14

---
title: AWS - How to deny access to resources while allowing a specific role
published: true
description: Deny with NotPrincipal or Conditions. The correct way to restrict access to a resource apart from a specific role. The beautiful simplicity of PrincipalARN condition.
tags: aws, iam, cloud, s3
---

#### TL;DR
To correctly use Deny effect together with NotPrincipal, instead of specifying the NotPrincipal, add a StringNotEquals condition for `AWS:PrincipalARN`. [Skip to example.](#allowing-access-only-to-a-single-arn)

# **The wrong way to use `"effect": "Deny"` with `"NotPrincipal"`**

When it comes to granting permissions, it's as easy as allowing the required permissions for a specific role or user. However, it's not so easy when you're working in a shared account where others have the same access to your resources as you do. 

Don't you hate when other users mess with your stuff or even worse - they accidentally expose their credentials and now the whole world has access to your S3 bucket... To avoid worrying, you thought it was a great idea to add a *Deny* policy to your resource, denying access to everyone but yourself.

##### **The following is an example of how not to do it. Try to spot why. [Scroll down for the correct way.](#the-correct-ways)**
```json
{ 
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "NotPrincipal": {
        "AWS": "arn:aws:iam::000000000000:role/<my-user-role>"
      },
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::<my-bucket>/*"
    }
  ]
}
```

Good job, you've locked yourself out of your bucket! Nor you, the root account user nor anyone else will be able to do anything with the bucket, it is essentially rendered useless. While the above policy might look valid, the problem is that when a user/role performs an action, it's principal consists of the following:
```
"arn:aws:sts::000000000000:assumed-role/<my-user-role>/<instance-id>"
"arn:aws:iam::000000000000:role/<my-user-role>"
"arn:aws:iam::000000000000:root"
```
So when you set the *Deny* to ignore your role, it implicitly denied your `assumed-role` and `root` resulting in denying you access to your resource.

Could I not just add my assumed role and root to the exception list? You could... But for example, each Lambda function has a different assumed-role and you can't use wildcard characters in Principal values. I don't know about you, but when [Amazon's official documentation on NotPrincipal](https://docs.amazonaws.cn/en_us/IAM/latest/UserGuide/reference_policies_elements_notprincipal.html) recommends not to use it, I'd rather find a better way.

# **The correct ways**

### **The slightly harder way**

That involves calling `aws sts get-caller-identity` for every user, populating policies with ids that make no sense and doesn't really work with functional roles (unless you plan to invoke sts get-caller-identity from inside your *lambda/ec2/etc*)

I'll just refer you to AWS blog posts [here](https://aws.amazon.com/blogs/security/how-to-restrict-amazon-s3-bucket-access-to-a-specific-iam-role) and [here](https://aws.amazon.com/premiumsupport/knowledge-center/explicit-deny-principal-elements-s3).

### **The easy way (Recommended)**

According to the [AWS Global Condition Key documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-principalarn), there is a key called **aws:PrincipalArn**

![aws:PrincipalArn](https://thepracticaldev.s3.amazonaws.com/i/dmu5jv3cyk8renoxlf9d.PNG)

Which is great, because:
- It is always included in the request content;
- It returns the ARN of the role instead of the assumed-role;
- It supports wildcards;
- Global Condition Keys are available for every action.

There is a mistake in the documentation, as *aws:PrincipalArn* does not work with [ARN operators](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_ARN), instead, it works with [String operators](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html#Conditions_String).

All of this means, that instead of using *"NotPrincipal"*, we can specify a *"StringNotEquals"* condition on *"aws:PrincipalArn"* and specifying our roles ARN. Don't worry if you've never used a Condition before, pick one of the following examples and adapt it to your needs.

##### **Allowing access only to a single ARN**
```json
{ 
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::<my-bucket>/*",
      "Condition": {
        "StringNotEquals": {
          "aws:PrincipalArn": "arn:aws:iam::000000000000:role/<my-user-role>",
        }
      }
    }
  ]
}
```

Let's say we have different roles named:
- *my-project-lambda*
- *my-project-ec2*
- *my-project-console*

We can use a placeholder (\*) to select them all at the same time by using *"StringNotLike"* instead of *"StringNotEquals"*

##### **Using placeholders**
```json
{ 
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::<my-bucket>/*",
      "Condition": {
        "StringNotLike": {
          "aws:PrincipalArn": "arn:aws:iam::000000000000:role/my-project-*",
        }
      }
    }
  ]
}
```

Wanted just to benefit from *"aws:PrincipalArn"* without having to deny anything?  Use it with **allow** in the same way!

##### **Allowing with multiple roles**
```json
{ 
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::<my-bucket>/*",
      "Condition": {
        "StringEquals": {
          "aws:PrincipalArn": [
            "arn:aws:iam::000000000000:role/<my-user-role>",
            "arn:aws:iam::000000000000:role/<my-manager-role>",
            "arn:aws:iam::000000000000:role/<my-audit-role>"
          ]
        }
      }
    }
  ]
}
```

This is most powerful when working in a corporate AWS account. Especially when there is a dedicated team managing Identity-based policies and roles, but developers themselves manage Resource-based policies. Sadly not all resources can have their own policies, a full list of those that can is available [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html).

That is all I wanted to share today, best of luck in your Cloud endeavors and let me know in the comments of all the resources you've locked yourself out of.