Repository: aws-cloudformation/cfn-lint

Issue:
E3002 received when using Fn::GetAtt in an Fn::If for a property that should be a list
*cfn-lint version: (0.13.2)*

*E3002 false positive when using Fn::If and Fn::GetAtt in an AWS::CodeBuild::Project's EnvironmentVariables*

Please provide as much information as possible:
* Template linting issues:
  * Please provide a CloudFormation sample that generated the issue.

Spec Override:
```json
{
  "ResourceTypes": {
    "Custom::EnvironmentVariables": {
      "Documentation": "https://github.com/IntuitiveWebSolutions/BriteDevelopment/blob/master/CustomResources.md#yaml-parser",
      "Properties": {
        "ServiceToken": {
          "PrimitiveType": "String",
          "Required": true,
          "UpdateType": "Immutable"
        },
        "String": {
          "PrimitiveType": "String",
          "Required": true,
          "UpdateType": "Mutable"
        }
      },
      "Attributes": {
        "Variables": {
          "ItemType": "AWS::CodeBuild::Project.EnvironmentVariable",
          "Type": "List"
        }
      }
    }
  },
  "ResourceSpecificationVersion": "2.11.0"
}
```

Example template:
```yaml
Conditions:
  ConditionTrue:
    Fn::Equals:
      - ""
      - ""

Resources:

  MyCustomEnvironmentVariables:
    Type: Custom::EnvironmentVariables
    Properties:
      ServiceToken:
        Fn::ImportValue: "MyServiceToken"
      String: "envvar1=1, envvar2=2"

  ProjectBuild:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: "My Project"
      Description: "My Description"
      Artifacts:
        Type: CODEPIPELINE
      ServiceRole:
        Fn::ImportValue: "ServiceRole"
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/python:3.7.1
        Type: LINUX_CONTAINER
        EnvironmentVariables:
          Fn::If:
            - ConditionTrue
            - Fn::GetAtt: [MyCustomEnvironmentVariables, Variables]
            - Ref: AWS::NoValue
      Source:
        BuildSpec: buildspec.yaml
        Type: CODEPIPELINE
```
  * If present, please add links to the (official) documentation for clarification.

The Custom::EnvironmentVariables sets the value of the attribute `Variables` with a list of items meeting the definition of `AWS::CodeBuild::Project.EnvironmentVariable` found [here](https://raw.githubusercontent.com/awslabs/cfn-python-lint/master/src/cfnlint/data/CloudSpecs/us-east-1.json) 

* Bug Report:
The above CloudFormation is valid, however, cfn-lint gripes with E3002 when using a conditional statement to set `EnvironmentVariables` to the <List> attribute provided by the `Custom::EnvironmentVariables` resource.

Example usage:
$ cfn-lint -o specs.json example.yaml 
E3002 Property EnvironmentVariables should be of type List for resource ProjectBuild at Resources/ProjectBuild/Properties/Environment/EnvironmentVariables/Fn::If/1
example.yaml:30:11

In cfnlint/rules/resources/properties/Properties.py:100 d_k is "Fn::GetAtt" and d_v is ['MyCustomEnvironmentVariables', 'Variables'] which results in the false positive being emitted. 





Discussion:

Resolving patch:
diff --git a/src/cfnlint/rules/resources/properties/Properties.py b/src/cfnlint/rules/resources/properties/Properties.py
--- a/src/cfnlint/rules/resources/properties/Properties.py
+++ b/src/cfnlint/rules/resources/properties/Properties.py
@@ -80,7 +80,7 @@ def primitivetypecheck(self, value, primtype, proppath):
     def check_list_for_condition(self, text, prop, parenttype, resourcename, propspec, path):
         """Checks lists that are a dict for conditions"""
         matches = []
-        if len(text[prop]) == 1:
+        if len(text[prop]) == 1:  # pylint: disable=R1702
             for sub_key, sub_value in text[prop].items():
                 if sub_key in cfnlint.helpers.CONDITION_FUNCTIONS:
                     if len(sub_value) == 3:
@@ -98,11 +98,26 @@ def check_list_for_condition(self, text, prop, parenttype, resourcename, propspe
                                 if len(if_v) == 1:
                                     for d_k, d_v in if_v.items():
                                         if d_k != 'Ref' or d_v != 'AWS::NoValue':
-                                            message = 'Property {0} should be of type List for resource {1} at {2}'
-                                            matches.append(
-                                                RuleMatch(
-                                                    condition_path,
-                                                    message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))
+                                            if d_k == 'Fn::GetAtt':
+                                                resource_name = None
+                                                if isinstance(d_v, list):
+                                                    resource_name = d_v[0]
+                                                elif isinstance(d_v, six.string_types):
+                                                    resource_name = d_v.split('.')[0]
+                                                if resource_name:
+                                                    resource_type = self.cfn.template.get('Resources', {}).get(resource_name, {}).get('Type')
+                                                    if not (resource_type.startswith('Custom::')):
+                                                        message = 'Property {0} should be of type List for resource {1} at {2}'
+                                                        matches.append(
+                                                            RuleMatch(
+                                                                condition_path,
+                                                                message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))
+                                            else:
+                                                message = 'Property {0} should be of type List for resource {1} at {2}'
+                                                matches.append(
+                                                    RuleMatch(
+                                                        condition_path,
+                                                        message.format(prop, resourcename, ('/'.join(str(x) for x in condition_path)))))
                                 else:
                                     message = 'Property {0} should be of type List for resource {1} at {2}'
                                     matches.append(


Test patch:
diff --git a/test/fixtures/templates/good/resource_properties.yaml b/test/fixtures/templates/good/resource_properties.yaml
--- a/test/fixtures/templates/good/resource_properties.yaml
+++ b/test/fixtures/templates/good/resource_properties.yaml
@@ -313,6 +313,11 @@ Parameters:
     Default: "foo.com, bar.com"
 Conditions:
   HasSingleClusterInstance: !Equals [!Ref 'AWS::Region', 'us-east-1']
+  Conditions:
+    ConditionTrue:
+      Fn::Equals:
+        - ""
+        - ""
 Resources:
   CPUAlarmHigh:
     Properties:
@@ -667,3 +672,31 @@ Resources:
       GroupDescription: test
       SecurityGroupIngress:
         Fn::FindInMap: [ runtime, us-east-1, production ]
+  MyCustomEnvironmentVariables:
+    Type: Custom::EnvironmentVariables
+    Properties:
+      ServiceToken:
+        Fn::ImportValue: "MyServiceToken"
+      String: "envvar1=1, envvar2=2"
+
+  ProjectBuild:
+    Type: AWS::CodeBuild::Project
+    Properties:
+      Name: "My Project"
+      Description: "My Description"
+      Artifacts:
+        Type: CODEPIPELINE
+      ServiceRole:
+        Fn::ImportValue: "ServiceRole"
+      Environment:
+        ComputeType: BUILD_GENERAL1_SMALL
+        Image: aws/codebuild/python:3.7.1
+        Type: LINUX_CONTAINER
+        EnvironmentVariables:
+          Fn::If:
+            - ConditionTrue
+            - Fn::GetAtt: [MyCustomEnvironmentVariables, Variables]
+            - Ref: AWS::NoValue
+      Source:
+        BuildSpec: buildspec.yaml
+        Type: CODEPIPELINE