AWS IAM: How to achieve Logical OR effect with multiple IAM condition operators?  
Published: 2020-08-31 16:54:34

In AWS IAM (Identity and Access Management) world, it is well known fact that the evaluation logic for :
* condition operators with multiple keys or multiple condition operators is always a logical AND operation:

* conditions with a single key and multiple values is a logical OR operation

The below example will give you the context on the above 2 statements. In the below example you can see that the 2 values for global condition key **aws:SourceIp** are evaluated using OR and the 3 separate condition operators **(DateGreaterThan, DateLessThan, IpAddress)** are evaluated using AND. This effectively means that 
* IpAddress condition operator will be true only if the SourceIP of the request belongs to either 192.0.2.0/24 or 203.0.113.0/24 subnets which is an OR operation.
* Overall condition block will be true only when all 3 condition operators are true which is the same as an AND operation.
```
"Condition" :  {
      "DateGreaterThan" : {
         "aws:CurrentTime" : "2019-07-16T12:00:00Z"
       },
      "DateLessThan": {
         "aws:CurrentTime" : "2019-07-16T15:00:00Z"
       },
       "IpAddress" : {
          "aws:SourceIp" : ["192.0.2.0/24", "203.0.113.0/24"]
      }
}
```

**Problem Statement**
Considering the above in mind, what if you have a requirement to set condition operators which should be evaluated in a logical OR manner. For example, launch an EC2 instance if it has a particular tag called as Env:Dev or if the sourceIP is 192.0.2.0/24. One way to achieve this is to duplicate your IAM statement block and put the 2 condition operators separately in each block but this is a tedious method and complex method which makes the IAM policy messy and you can come very close to hitting IAM Managed Policy limit of 6144 characters (excluding whitespaces) when you have multiple condition operators which involve multiple actions. 

**Solution**
I will begin by sharing a very basic concept of truth tables for Logical AND as well as Logical OR operation and then move on to the solution.
```
Logical AND:
=============
Input1   Input2      Output

True      True        True

False     False       False

True      False       False

False     True        False

Logical OR:
============
Input1   Input2      Output

True      True        True

False     False       False

True      False       True

False     True        True
```

As you can see from above, the Logical AND operation will yield a True output only when both the inputs are true and logical OR will always yield a true output as long as at least one of the inputs is true. IAM policies are also evaluated in the same manner. The Effect mentioned in the IAM statement block will be Allowed or Denied only if the condition is true. Now to perform a logical OR in the condition block, we will have to use the following method:

```
Not [(Not(Condition 1) AND Not(Condition 2)]

------------------------------------------
Let us try to solve the above block in truth table form with values where:
Condition 1 = Input 1
Condition 2 = Input 2

* When Input 1 = True and Input 2 = True:
Not [(Not(True) AND Not(True)] => Not [False AND False] 
=> Not [False] => True

* When Input 1 = True and Input 2 = False:
Not [(Not(True) AND Not(False)] => Not [False AND True] 
=> Not [False] => True

* When Input 1 = False and Input 2 = True:
Not [(Not(False) AND Not(True)] => Not [True AND False] 
=> Not [False] => True

* When Input 1 = False and Input 2 = False:
Not [(Not(False) AND Not(False)] => Not [True AND True] 
=> Not [True] => False

If we put the values in the table, then the table will look like this which matches that of Logical OR table shown above:


Input1   Input2      Output
======   ======      ======
True      True        True

False     False       False

True      False       True

False     True        True

```

Up until now, it was all fundamentals but how do you actually apply the above logic in the condition block? I will explain this below with an example of Effect:Deny and Not condition operators such as StringNotEquals. And I will take the same example as mentioned in the problem statement, that is to launch an EC2 instance if it has a particular tag called as Env:Dev or if the sourceIP is 192.0.2.0/24. Following IAM policy statement should be able to achieve the desired Logical OR effect:

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowToDescribeAll",
            "Effect": "Allow",
            "Action": [
                "ec2:RunInstances"
            ],
            "Resource": "*"
        },
        {
        "Effect": "Deny",
        "Action": "ec2:RunInstances",
        "Resource": "*",
        "Condition": {
            "NotIpAddress": {
                "aws:SourceIp": [
                    "192.0.2.0/24"
                ]
            },
              "StringNotLike": {
                 "aws:RequestTag/Env": [
                        "Dev"
                    ]
            }
}
}
]
}
```

If you are curious why the above policy will work as Logical OR, so let me show you the relation to the method we derived earlier:

**Not [(Not(Condition 1) AND Not(Condition 2)]**

The above IAM policy can also be written as (for understanding the concept only):

**Deny [(NotIpAddress (aws:SourceIP) AND StringNotLike (aws:RequestTag)]**

```
* When awsSourceIP is not 192.0.2.0/24 (meaning it is false) and aws:RequestTag/Env is not Dev (meaning it is false) and the respective condition operators will become True and Effect will be denied:
Deny [(NotIpAddress (False) AND StringNotLike (False)] ==>
Deny [True AND True] ==> Deny [True] => False

* When awsSourceIP is 192.0.2.0/24 (meaning it is true) and aws:RequestTag/Env is not Dev (meaning it is false) then this statement policy block will not apply. This will mean Effect:Allow will take effect and thereby allowing ec2:RunInstances action:
Deny [(NotIpAddress (True) AND StringNotLike (False)] ==>
Deny [False AND True] ==> Deny [False] => True

* When awsSourceIP is not 192.0.2.0/24 (meaning it is false) and aws:RequestTag/Env is Dev (meaning it is true) then this statement policy block will not apply. This will mean Effect:Allow will take effect and thereby allowing ec2:RunInstances action:
Deny [(NotIpAddress (False) AND StringNotLike (True)] ==>
Deny [True AND False] ==> Deny [False] => True

* When awsSourceIP is 192.0.2.0/24 (meaning it is true) and aws:RequestTag/Env is Dev (meaning it is true) then this statement policy block will not apply. This will mean Effect:Allow will take effect and thereby allowing ec2:RunInstances action:
Deny [(NotIpAddress (True) AND StringNotLike (True)] ==>
Deny [False AND False] ==> Deny [False] => True

If we put the values in the table, then the table will look like this which matches that of Logical OR table shown above:


Input1   Input2      Output
======   ======      ======
True      True        True

False     False       False

True      False       True

False     True        True
```

**Conclusion**
IAM Policies can be complicated and using this way you can eliminate the duplicate statement blocks which only differs in the condition statement just because that was the only way till now to achieve the OR effect.