Repository: aws-cloudformation/cfn-lint

Issue:
E3012 "String" recommended for an external template parameter where type is "Number"
*cfn-lint version: 0.18.0*

I define some of my template with sub-templates and everything is packaged with that kind of command line 
```
aws cloudformation package --template-file ./sources/template-source.yaml --s3-bucket my-bucket-build --output-template-file final-template.yaml 
```

In `template-source` I have a resource which type is `AWS::CloudFormation::Stack`.
It's definition looks like that :
``` yaml
    ManagerService:
        Type: AWS::CloudFormation::Stack
        Properties:
            TemplateURL: ../../../common/service.yaml
            Parameters:
                Cluster: !Ref Cluster
                DesiredCount: 1
```

Here is the definition of the parameter in my `service` template :
```yaml
Parameters: 

    Cluster:
        Description: Please provide the ECS Cluster ID that this service should run on
        Type: String
        
    DesiredCount: 
        Description: How many instances of this task should we run across our cluster?
        Type: Number
        Default: 1
```

And cfn-lint return the following error :
```
cfn-lint source-template.yaml
E3012 Property Resources/ManagerService/Properties/Parameters/DesiredCount should be of type String
```

It says that type should be a String, but the type in the template is define as Number.
Am I missing something ?

Thanks for your help


Discussion:
this is an interesting question.  So we don't pull the nested stack templates to check their parameter types.  We use the CloudFormation Spec to determine correctness for this.  The trick here is the Map types are Strings.  I am debating if we should just disable that check for this scenario.

```
"Parameters": {
          "Documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html#cfn-cloudformation-stack-parameters",
          "DuplicatesAllowed": false,
          "PrimitiveItemType": "String",
          "Required": false,
          "Type": "Map",
          "UpdateType": "Mutable"
        },
```
Thanks for the quick answer ! It make sens if the documentation define it as String. But the problem is if we want to ensure the data to match a Number, it still a best practice to keep it as a Number. Otherwise we also could set it as string and use AllowedPattern but it's a bit complexe as the Number type exist.

So yeah, maybe disable that check in the specific cas could be a solution.
I agree with you on this.  I would like @chuckmeyer and @fatbasstard to weigh in on this too.  Going to mark it as a question for now and until we can get their opinions.  

Few more thoughts.
- We won't know what the sub stack types are so we can't really test them.  Basically, I won't know its a number so that is on you to validate (at least with the current features in cfn-lint)
- Has anyone tested what List parameters look like here?
@kddejong List parameters do not pass through to sub stacks successfully. If you have a CommaDelimitedList type in your main stack and you attempt pass it through to a CommaDelimitedList in the sub stack, you will get a failure. Only types that can be coerced to strings can be passed through to a substack, like numbers and booleans.

The only way to pass a list through is to `!Join [',', !Ref MyList]` as you pass it through to the sub stack.

I think configuring `E3012` with `strict: False` would do the trick here.
Any progress on this? I'm using this in vscode-cfn-lint and the same scenario is tripping me up. 
For the moment, I've disabled the check with the follwing `.cfnlintrc` file :
```
include_checks:
- I
configure_rules:
  E3012:
    strict: False
```

Resolving patch:
diff --git a/src/cfnlint/rules/resources/properties/ValuePrimitiveType.py b/src/cfnlint/rules/resources/properties/ValuePrimitiveType.py
--- a/src/cfnlint/rules/resources/properties/ValuePrimitiveType.py
+++ b/src/cfnlint/rules/resources/properties/ValuePrimitiveType.py
@@ -84,7 +84,6 @@ def _value_check(self, value, path, item_type, extra_args):
     def check_primitive_type(self, value, item_type, path):
         """Chec item type"""
         matches = []
-
         if isinstance(value, dict) and item_type == 'Json':
             return matches
         if item_type in ['String']:
@@ -140,7 +139,7 @@ def check_value(self, value, path, **kwargs):
 
         return matches
 
-    def check(self, cfn, properties, specs, path):
+    def check(self, cfn, properties, specs, spec_type, path):
         """Check itself"""
         matches = []
 
@@ -154,14 +153,15 @@ def check(self, cfn, properties, specs, path):
                 else:
                     item_type = None
                 if primitive_type:
-                    matches.extend(
-                        cfn.check_value(
-                            properties, prop, path,
-                            check_value=self.check_value,
-                            primitive_type=primitive_type,
-                            item_type=item_type
+                    if not(spec_type == 'AWS::CloudFormation::Stack' and prop == 'Parameters'):
+                        matches.extend(
+                            cfn.check_value(
+                                properties, prop, path,
+                                check_value=self.check_value,
+                                primitive_type=primitive_type,
+                                item_type=item_type
+                            )
                         )
-                    )
 
         return matches
 
@@ -171,7 +171,7 @@ def match_resource_sub_properties(self, properties, property_type, path, cfn):
 
         if self.property_specs.get(property_type, {}).get('Properties'):
             property_specs = self.property_specs.get(property_type, {}).get('Properties', {})
-            matches.extend(self.check(cfn, properties, property_specs, path))
+            matches.extend(self.check(cfn, properties, property_specs, property_type, path))
 
         return matches
 
@@ -179,6 +179,6 @@ def match_resource_properties(self, properties, resource_type, path, cfn):
         """Check CloudFormation Properties"""
         matches = []
         resource_specs = self.resource_specs.get(resource_type, {}).get('Properties', {})
-        matches.extend(self.check(cfn, properties, resource_specs, path))
+        matches.extend(self.check(cfn, properties, resource_specs, resource_type, path))
 
         return matches


Test patch:
diff --git a/test/fixtures/templates/good/resources/properties/primitive_types.yaml b/test/fixtures/templates/good/resources/properties/primitive_types.yaml
--- a/test/fixtures/templates/good/resources/properties/primitive_types.yaml
+++ b/test/fixtures/templates/good/resources/properties/primitive_types.yaml
@@ -6,7 +6,7 @@ Resources:
     Properties:
       Name: Test
       Cutoff: 0
-      Schedule: 'rate(1 days)'
+      Schedule: "rate(1 days)"
       AllowUnassociatedTargets: false
       Duration: 1
   BaselinePatchDailySSMMaintenanceWindowTarget:
@@ -17,7 +17,13 @@ Resources:
       WindowId: !Ref BaselinePatchDailySSMMaintenanceWindow
       ResourceType: INSTANCE
       Targets:
-      - Key: tag:Patch
-        Values:
-        - Daily
-        - daily
+        - Key: tag:Patch
+          Values:
+            - Daily
+            - daily
+  ManagerService:
+    Type: AWS::CloudFormation::Stack
+    Properties:
+      TemplateURL: ../../../common/service.yaml
+      Parameters:
+        DesiredCount: 1