Repository: aws-cloudformation/cfn-lint

Issue:
Incorrect error "E2523 Only one of [VirtualName, Ebs] should be specified for ..."
*cfn-lint version: (`cfn-lint --version`)*

```
$ cfn-lint --version
cfn-lint 0.4.2
```

*Description of issue.*

cfn-lint is outputting an error about a property that we aren't using, `VirtualName`. Not really sure what's up. `VirtualName` is not present _anywhere_ in the template... Here's the error:

```
E2523 Only one of [VirtualName, Ebs] should be specified for ...
```

Here is the relevant part of the template...

```
                "BlockDeviceMappings": [
                    {
                        "DeviceName": {
                            "Fn::Join": [
                                "",
                                [
                                    "/dev/",
                                    {
                                        "Fn::FindInMap": [
                                            "Distro2RootDevice",
                                            {
                                                "Ref": "AmiDistro"
                                            },
                                            "DeviceName"
                                        ]
                                    }
                                ]
                            ]
                        },
                        "Ebs": {
                            "DeleteOnTermination": "true",
                            "VolumeType": "gp2"
                        }
                    }
                ]
```


Discussion:
I'm having troubles repeating this.  I may need some more information for you but I will do some more digging.   Error could use a little bit better wording but you will see this error under two different scenarios:
- Neither Ebs or VirtualName are specified
- Both Ebs and VirtualName are specified

When I put your template snippets into my files I don't get the error.  So trying to play out a few more scenarios that could give you this error.  
The full templates are a bit complex. Check it out here:

* https://github.com/plus3it/terraform-aws-watchmaker/blob/master/modules/lx-instance/watchmaker-lx-instance.template.cfn.json#L1343

You can see the failure in the travis-ci build where I'm trying to integrate cfn-lint...

* https://travis-ci.org/plus3it/terraform-aws-watchmaker/jobs/411697708
I'm guessing it's the former scenario that's triggering, with the conditional second volume... sorry about that, I was trying to simplify the issue report and didn't think about the conditional second volume perhaps triggering the problem...

* Neither Ebs or VirtualName are specified

```
                "BlockDeviceMappings": [
                    {
                        "DeviceName": {
                            "Fn::Join": [
                                "",
                                [
                                    "/dev/",
                                    {
                                        "Fn::FindInMap": [
                                            "Distro2RootDevice",
                                            {
                                                "Ref": "AmiDistro"
                                            },
                                            "DeviceName"
                                        ]
                                    }
                                ]
                            ]
                        },
                        "Ebs": {
                            "DeleteOnTermination": "true",
                            "VolumeType": "gp2"
                        }
                    },
                    {
                        "Fn::If": [
                            "CreateAppVolume",
                            {
                                "DeviceName": "/dev/xvdf",
                                "Ebs": {
                                    "DeleteOnTermination": "true",
                                    "VolumeSize": {
                                        "Ref": "AppVolumeSize"
                                    },
                                    "VolumeType": {
                                        "Ref": "AppVolumeType"
                                    }
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
],
```
Got it.  I can fix that one :) Thanks

Resolving patch:
diff --git a/src/cfnlint/__init__.py b/src/cfnlint/__init__.py
--- a/src/cfnlint/__init__.py
+++ b/src/cfnlint/__init__.py
@@ -647,7 +647,7 @@ def get_values(self, obj, key, path=[]):
                             for result in results:
                                 check_obj = obj.copy()
                                 check_obj[key] = result['Value']
-                                matches.extend(self.get_values(check_obj, key, path[:] + result['Path']))
+                                matches.extend(self.get_values(check_obj, key, result['Path']))
                 if not is_condition:
                     result = {}
                     result['Path'] = path[:]
diff --git a/src/cfnlint/rules/resources/properties/Inclusive.py b/src/cfnlint/rules/resources/properties/Inclusive.py
--- a/src/cfnlint/rules/resources/properties/Inclusive.py
+++ b/src/cfnlint/rules/resources/properties/Inclusive.py
@@ -38,35 +38,37 @@ def __init__(self):
         for property_type_spec in self.property_types_specs:
             self.resource_sub_property_types.append(property_type_spec)
 
-    def check(self, properties, inclusions, path):
+    def check(self, properties, inclusions, path, cfn):
         """Check itself"""
         matches = list()
-        for prop in properties:
-            if prop in inclusions:
-                for incl_property in inclusions[prop]:
-                    if incl_property not in properties:
-                        message = 'Parameter {0} should exist with {1} for {2}'
-                        matches.append(RuleMatch(
-                            path + [prop],
-                            message.format(incl_property, prop, '/'.join(map(str, path)))
-                        ))
+        property_sets = cfn.get_values({'Properties': properties}, 'Properties', path)
+        for property_set in property_sets:
+            for prop in property_set['Value']:
+                if prop in inclusions:
+                    for incl_property in inclusions[prop]:
+                        if incl_property not in property_set['Value']:
+                            message = 'Parameter {0} should exist with {1} for {2}'
+                            matches.append(RuleMatch(
+                                path + [prop],
+                                message.format(incl_property, prop, '/'.join(map(str, property_set['Path'])))
+                            ))
 
         return matches
 
-    def match_resource_sub_properties(self, properties, property_type, path, _):
+    def match_resource_sub_properties(self, properties, property_type, path, cfn):
         """Match for sub properties"""
         matches = list()
 
         inclusions = self.property_types_specs.get(property_type, {})
-        matches.extend(self.check(properties, inclusions, path))
+        matches.extend(self.check(properties, inclusions, path, cfn))
 
         return matches
 
-    def match_resource_properties(self, properties, resource_type, path, _):
+    def match_resource_properties(self, properties, resource_type, path, cfn):
         """Check CloudFormation Properties"""
         matches = list()
 
         inclusions = self.resource_types_specs.get(resource_type, {})
-        matches.extend(self.check(properties, inclusions, path))
+        matches.extend(self.check(properties, inclusions, path, cfn))
 
         return matches
diff --git a/src/cfnlint/rules/resources/properties/OnlyOne.py b/src/cfnlint/rules/resources/properties/OnlyOne.py
--- a/src/cfnlint/rules/resources/properties/OnlyOne.py
+++ b/src/cfnlint/rules/resources/properties/OnlyOne.py
@@ -39,38 +39,40 @@ def __init__(self):
         for property_type_spec in self.property_types_specs:
             self.resource_sub_property_types.append(property_type_spec)
 
-    def check(self, properties, onlyoneprops, path):
+    def check(self, properties, onlyoneprops, path, cfn):
         """Check itself"""
         matches = list()
-        for onlyoneprop in onlyoneprops:
-            count = 0
-            for prop in onlyoneprop:
-                if prop in properties:
-                    count += 1
+        property_sets = cfn.get_values({'Properties': properties}, 'Properties', path)
+        for property_set in property_sets:
+            for onlyoneprop in onlyoneprops:
+                count = 0
+                for prop in onlyoneprop:
+                    if prop in property_set['Value']:
+                        count += 1
 
-            if count != 1:
-                message = 'Only one of [{0}] should be specified for {1}'
-                matches.append(RuleMatch(
-                    path,
-                    message.format(', '.join(map(str, onlyoneprop)), '/'.join(map(str, path)))
-                ))
+                if count != 1:
+                    message = 'Only one of [{0}] should be specified for {1}'
+                    matches.append(RuleMatch(
+                        property_set['Path'],
+                        message.format(', '.join(map(str, onlyoneprop)), '/'.join(map(str, property_set['Path'])))
+                    ))
 
         return matches
 
-    def match_resource_sub_properties(self, properties, property_type, path, _):
+    def match_resource_sub_properties(self, properties, property_type, path, cfn):
         """Match for sub properties"""
         matches = list()
 
         onlyoneprops = self.property_types_specs.get(property_type, {})
-        matches.extend(self.check(properties, onlyoneprops, path))
+        matches.extend(self.check(properties, onlyoneprops, path, cfn))
 
         return matches
 
-    def match_resource_properties(self, properties, resource_type, path, _):
+    def match_resource_properties(self, properties, resource_type, path, cfn):
         """Check CloudFormation Properties"""
         matches = list()
 
         onlyoneprops = self.resource_types_specs.get(resource_type, {})
-        matches.extend(self.check(properties, onlyoneprops, path))
+        matches.extend(self.check(properties, onlyoneprops, path, cfn))
 
         return matches


Test patch: