Repository: aws-cloudformation/cfn-lint

Issue:
E1024 should allow Fn::ImportValue
cfn-lint version: 0.3.3

The following template:

```
---
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  SubnetIndex:
    Description: 'Index of the subnet'
    Type: Number
    MinValue: 0
    MaxValue: 5
  SubnetCount:
    Description: 'To slice the IP address ranges you need to specify how many subnets you want to create in the VPC'
    Type: Number
    MinValue: 1
    MaxValue: 6
Resources:
  Subnet:
    Type: 'AWS::EC2::Subnet'
    Properties:
      CidrBlock: !Select [!Ref SubnetIndex, !Cidr [{'Fn::ImportValue': 'vpc-CidrBlock'}, !Ref SubnetCount, 12]]
      VpcId: 'vpc-123456'
```

Produces the following exceptions and findings:

```
# not sure about this
E0002 Unknown exception while processing rule E1017: too many values to unpack
bug.yml:1:1

# looks like Cidr also supports Fn::ImportValue (at least the template works, documentation says it does not work https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html)
E1024 Cidr ipBlock should be Cidr Range, Ref, or Select for Resources/Subnet/Properties/CidrBlock/Fn::Select/1/Fn::Cidr/0
bug.yml:18:7

# not quiet sure about that, 12 should be a int?
E1024 Cidr sizeMask should be a int for Resources/Subnet/Properties/CidrBlock/Fn::Select/1/Fn::Cidr/2
bug.yml:18:7
```




Discussion:
Sorry for the annoying errors. I’ll take a look and should have some fixes shortly. 
no worries. cfn-lint fan without any python skills :) 

Resolving patch:
diff --git a/src/cfnlint/rules/functions/Cidr.py b/src/cfnlint/rules/functions/Cidr.py
--- a/src/cfnlint/rules/functions/Cidr.py
+++ b/src/cfnlint/rules/functions/Cidr.py
@@ -90,7 +90,8 @@ def match(self, cfn):
 
         supported_functions = [
             'Fn::Select',
-            'Ref'
+            'Ref',
+            'Fn::ImportValue'
         ]
 
         count_parameters = []
@@ -148,7 +149,7 @@ def match(self, cfn):
                                         tree[:] + [2], message.format('/'.join(map(str, tree[:] + [2])))))
                                 if index_key == 'Ref':
                                     size_mask_parameters.append(index_value)
-                    elif not isinstance(count_obj, six.integer_types):
+                    elif not isinstance(size_mask_obj, six.integer_types):
                         message = 'Cidr sizeMask should be a int for {0}'
                         matches.append(RuleMatch(
                             tree[:] + [2], message.format('/'.join(map(str, tree[:] + [2])))))
diff --git a/src/cfnlint/rules/functions/Select.py b/src/cfnlint/rules/functions/Select.py
--- a/src/cfnlint/rules/functions/Select.py
+++ b/src/cfnlint/rules/functions/Select.py
@@ -53,30 +53,30 @@ def match(self, cfn):
                     list_of_objs = select_value_obj[1]
                     if isinstance(index_obj, dict):
                         if len(index_obj) == 1:
-                            for index_key, _ in index_obj:
+                            for index_key, _ in index_obj.items():
                                 if index_key not in ['Ref', 'Fn::FindInMap']:
-                                    message = 'Select index should be int, Ref, FindInMap for {0}'
+                                    message = 'Select index should be an Integer or a function Ref or FindInMap for {0}'
                                     matches.append(RuleMatch(
                                         tree, message.format('/'.join(map(str, tree)))))
                     elif not isinstance(index_obj, six.integer_types):
                         try:
                             int(index_obj)
                         except ValueError:
-                            message = 'Select index should be int, Ref, FindInMap for {0}'
+                            message = 'Select index should be an Integer or a function of Ref or FindInMap for {0}'
                             matches.append(RuleMatch(
                                 tree, message.format('/'.join(map(str, tree)))))
                     if isinstance(list_of_objs, dict):
                         if len(list_of_objs) == 1:
                             for key, _ in list_of_objs.items():
                                 if key not in supported_functions:
-                                    message = 'Key {0} should be a list for {1}'
+                                    message = 'Select should use a supported function of {0}'
                                     matches.append(RuleMatch(
-                                        tree, message.format(key, '/'.join(map(str, tree)))))
+                                        tree, message.format(', '.join(map(str, supported_functions)))))
                         else:
-                            message = 'Select should be a list of 2 elements for {0}'
+                            message = 'Select should use a supported function of {0}'
                             matches.append(RuleMatch(
-                                tree, message.format('/'.join(map(str, tree)))))
-                    else:
+                                tree, message.format(', '.join(map(str, supported_functions)))))
+                    elif not isinstance(list_of_objs, list):
                         message = 'Select should be an array of values for {0}'
                         matches.append(RuleMatch(
                             tree, message.format('/'.join(map(str, tree)))))


Test patch:
diff --git a/test/fixtures/templates/good/functions/cidr.yaml b/test/fixtures/templates/good/functions/cidr.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/good/functions/cidr.yaml
@@ -0,0 +1,19 @@
+---
+AWSTemplateFormatVersion: '2010-09-09'
+Parameters:
+  SubnetIndex:
+    Description: 'Index of the subnet'
+    Type: Number
+    MinValue: 0
+    MaxValue: 5
+  SubnetCount:
+    Description: 'To slice the IP address ranges you need to specify how many subnets you want to create in the VPC'
+    Type: Number
+    MinValue: 1
+    MaxValue: 6
+Resources:
+  Subnet:
+    Type: 'AWS::EC2::Subnet'
+    Properties:
+      CidrBlock: !Select [!Ref SubnetIndex, !Cidr [{'Fn::ImportValue': 'vpc-CidrBlock'}, !Ref SubnetCount, 12]]
+      VpcId: 'vpc-123456'
diff --git a/test/rules/functions/test_cidr.py b/test/rules/functions/test_cidr.py
--- a/test/rules/functions/test_cidr.py
+++ b/test/rules/functions/test_cidr.py
@@ -32,6 +32,10 @@ def test_file_positive(self):
         """Test Positive"""
         self.helper_file_positive()
 
+    def test_file_positive_extra(self):
+        """Test failure"""
+        self.helper_file_positive_template('fixtures/templates/good/functions/cidr.yaml')
+
     def test_file_negative(self):
         """Test failure"""
         self.helper_file_negative('fixtures/templates/bad/functions_cidr.yaml', 10)