Repository: aws-cloudformation/cfn-lint

Issue:
Complex Condition breaks Properties check (E3002)
Properties check breaks on complex Conditions that return multiple sub-properties. Example:

```
AWSTemplateFormatVersion: "2010-09-09"
Description: A sample template
Parameters:
  EnableGeoBlocking:
    Type: String
Conditions:
  EnableGeoBlocking: !Equals [ !Ref EnableGeoBlocking, "true" ]
Resources:
  CloudFrontDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        Enabled: true
        Restrictions:
          GeoRestriction:
            !If
              - EnableGeoBlocking
              -
                RestrictionType: whitelist
                Locations:
                  - BE
                  - LU
                  - NL
              - RestrictionType: none
```

This returns an error on the `!If`:

```
E3002 Expecting an object at Resources/CloudFrontDistribution/Properties/DistributionConfig/Restrictions/GeoRestriction/Fn::If/2
template.yaml:15:11
```


Discussion:
Shoot.  Yea those conditions can be tricky.  I'll take a look at this.

Resolving patch:
diff --git a/src/cfnlint/__init__.py b/src/cfnlint/__init__.py
--- a/src/cfnlint/__init__.py
+++ b/src/cfnlint/__init__.py
@@ -576,22 +576,34 @@ def get_condition_values(self, template, path=[]):
             result = {}
             result['Path'] = path[:] + [index + 1]
             if not isinstance(item, (dict, list)):
+                # Just straight values and pass them through
                 result['Value'] = item
                 matches.append(result)
             elif len(item) == 1:
-                for sub_key, sub_value in item.items():
-                    if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
-                        results = self.get_condition_values(sub_value, result['Path'] + [sub_key])
-                        if isinstance(results, list):
-                            matches.extend(results)
-                    elif sub_key == 'Ref':
-                        if sub_value != 'AWS::NoValue':
-                            result['Value'] = sub_value
-                            result['Path'] += ['Ref']
+                # Checking for conditions inside of conditions
+                if isinstance(item, dict):
+                    for sub_key, sub_value in item.items():
+                        if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
+                            results = self.get_condition_values(sub_value, result['Path'] + [sub_key])
+                            if isinstance(results, list):
+                                matches.extend(results)
+                        elif sub_key == 'Ref':
+                            if sub_value != 'AWS::NoValue':
+                                result['Value'] = sub_value
+                                result['Path'] += ['Ref']
+                                matches.append(result)
+                        else:
+                            # Return entire Item
+                            result['Value'] = item
                             matches.append(result)
-                    else:
-                        result['Value'] = sub_value
-                        matches.append(result)
+                else:
+                    # Return entire Item
+                    result['Value'] = item
+                    matches.append(result)
+            else:
+                # Length longer than 1 means a list or object that should be fully returned
+                result['Value'] = item
+                matches.append(result)
 
         return matches
 


Test patch:
diff --git a/test/module/test_cfn_json.py b/test/module/test_cfn_json.py
--- a/test/module/test_cfn_json.py
+++ b/test/module/test_cfn_json.py
@@ -49,7 +49,7 @@ def setUp(self):
             },
             "vpc": {
                 "filename": 'templates/quickstart/vpc.json',
-                "failures": 1
+                "failures": 0
             }
         }
 
diff --git a/test/module/test_template.py b/test/module/test_template.py
--- a/test/module/test_template.py
+++ b/test/module/test_template.py
@@ -66,3 +66,65 @@ def test_get_valid_refs(self):
         """ Get Valid REFs"""
         refs = self.template.get_valid_refs()
         assert len(refs) == 20
+
+    def test_conditions_return_object_success(self):
+        """Test condition object response and nested IFs"""
+        template = [
+            'isProd',
+            {
+                'Key': 'Environment1',
+                'Value': 'Prod'
+            },
+            {
+                'Fn::If': [
+                    'isDev',
+                    {
+                        'Key': 'Environment2',
+                        'Value': 'Dev'
+                    },
+                    {
+                        "Ref": "AWS::NoValue"
+                    }
+                ]
+            }
+        ]
+
+        results = self.template.get_condition_values(template, [])
+        self.assertEqual(results, [
+            {'Path': [1], 'Value': {'Value': 'Prod', 'Key': 'Environment1'}},
+            {'Path': [2, 'Fn::If', 1], 'Value': {'Value': 'Dev', 'Key': 'Environment2'}}
+        ])
+
+    def test_conditions_return_list_success(self):
+        """Test condition list response"""
+        template = [
+            'PrimaryRegion',
+            [
+                'EN'
+            ],
+            [
+                'BE',
+                'LU',
+                'NL'
+            ]
+        ]
+
+        results = self.template.get_condition_values(template, [])
+        self.assertEqual(results, [
+            {'Value': ['EN'], 'Path': [1]}, {'Value': ['BE', 'LU', 'NL'], 'Path': [2]}
+        ])
+
+    def test_conditions_return_string_success(self):
+        """Test condition object response and nested IFs"""
+        template = [
+            'isProd',
+            {
+                'Ref': 'Sample'
+            },
+            'String'
+        ]
+
+        results = self.template.get_condition_values(template, [])
+        self.assertEqual(results, [
+            {'Path': [1, 'Ref'], 'Value': 'Sample'}, {'Path': [2], 'Value': 'String'}
+        ])
diff --git a/test/templates/bad/conditions.yaml b/test/templates/bad/conditions.yaml
--- a/test/templates/bad/conditions.yaml
+++ b/test/templates/bad/conditions.yaml
@@ -34,12 +34,15 @@ Parameters:
       - prod
       - test
     ConstraintDescription: must specify prod or test.
+  EnableGeoBlocking:
+    Type: String
 Conditions:
   CreateProdResources: !Equals [ !Ref EnvType, prod ]
   BadCondition: String
   TooManyConditions:
     Fn::Equals: [ !Ref EnvType, prod ]
     Fn::Not: !Equals [ !Ref EnvType, prod ]
+  EnableGeoBlocking: !Equals [ !Ref EnableGeoBlocking, "true" ]
 Resources:
   EC2Instance:
     Type: "AWS::EC2::Instance"
@@ -74,6 +77,27 @@ Resources:
       Size: 100
       AvailabilityZone:
         !GetAtt EC2Instance.AvailabilityZone
+  CloudFrontDistribution:
+    Type: "AWS::CloudFront::Distribution"
+    Properties:
+      DistributionConfig:
+        Enabled: true
+        Restrictions:
+          GeoRestriction:
+            !If
+              - EnableGeoBlocking
+              -
+                RestrictionType:
+                  !If
+                    - EnableGeoBlocking
+                    -
+                      - whitelist
+                    - whitelist
+                BadLocations:
+                  - BE
+                  - LU
+                  - NL
+              - RestrictionType: none
 Outputs:
   VolumeId:
     Condition: CreateProdResources
diff --git a/test/templates/good/conditions.yaml b/test/templates/good/conditions.yaml
--- a/test/templates/good/conditions.yaml
+++ b/test/templates/good/conditions.yaml
@@ -8,6 +8,8 @@ Parameters:
     AllowedValues:
       - prod
       - dev
+  EnableGeoBlocking:
+    Type: String
 Conditions:
   isProd:
     "Fn::Equals":
@@ -17,6 +19,11 @@ Conditions:
     "Fn::Equals":
     - !Ref myEnvironment
     - "dev"
+  EnableGeoBlocking: !Equals [ !Ref EnableGeoBlocking, "true" ]
+  PrimaryRegion:
+    "Fn::Equals":
+    - !Ref AWS::Region
+    - 'us-east-1'
 Resources:
   myInstance:
     Type: AWS::EC2::Instance
@@ -35,3 +42,22 @@ Resources:
           - Key: Environment2
             Value: Dev
           - !Ref AWS::NoValue
+  CloudFrontDistribution:
+    Type: "AWS::CloudFront::Distribution"
+    Properties:
+      DistributionConfig:
+        Enabled: true
+        Restrictions:
+          GeoRestriction:
+            !If
+              - EnableGeoBlocking
+              -
+                RestrictionType: whitelist
+                Locations:
+                  !If
+                    - PrimaryRegion
+                    - - EN
+                    - - BE
+                      - LU
+                      - NL
+              - RestrictionType: none