Repository: aws-cloudformation/cfn-lint

Issue:
AutoPublishAlias with a !Ref value marked as false positive
*cfn-lint version: 0.11.1*

I'm running into an issue with `AutoPublishAlias`. When using a `!Ref` as its value, I get the following error message:
```
E0001 Resource with id [SkillFunction] is invalid. 'AutoPublishAlias' must be a string or a Ref to a template parameter
foo.yml:1:1
```

Here's the minimal template to reproduce the issue:
```yaml
---

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  Stage:
    Description: Environment stage (deployment phase)
    Type: String
    AllowedValues:
      - beta
      - prod

Resources:
  SkillFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: '.'
      Handler: main.handler
      Runtime: python3.7
      Timeout: 30
      MemorySize: 128
      AutoPublishAlias: !Ref Stage
```

The error message is actually very helpful (👏 for that), but as you can see, I am using a `!Ref` to a template parameter, so this should not cause a linting error.


Discussion:
Taking a look at it.  Looks like we may be getting this error back from SAM when trying to transform the template.  So need to dig a little deeper. 
https://github.com/awslabs/serverless-application-model/blob/0425efc396b659c8d0dea5b63c3287cb915d6b34/samtranslator/model/sam_resources.py#L144
This looks like an issue specifically with AutoPublishAlias and how SAM is designed. https://github.com/awslabs/serverless-application-model/issues/220
First of, thank you for jumping on the issue so promptly.

Should I open a new issue in the SAM repo then? Not sure how you folks are organized.
Sorry for the delay I think I may be out of my element on this one.  I get the same error from the CLI.  From what I can tell this shouldn't be happening but I'm a little bit lost on why.  I think we should create an incident in https://github.com/awslabs/serverless-application-model and see if they can help

```
2019-01-16 20:37:27 Found credentials in shared credentials file: ~/.aws/credentials
Template provided at 'test.yaml' was invalid SAM Template.
Error: [InvalidResourceException('SkillFunction', "'AutoPublishAlias' must be a string or a Ref to a template parameter")] ('SkillFunction', "'AutoPublishAlias' must be a string or a Ref to a template parameter")
```
Thanks for you help @kddejong. I've opened an issue ☝️ in the SAM repo, hopefully they will be able to fix it.

So if I understand it correctly, once it's 👌 in SAM, it will be 👌 in cfn-lint as well?
Yea sorry I didn't explain that well.  We can't lint a pre-transformed template because there isn't a CloudFormation spec for it.  We pass it to the sam translator and then lint the results.  In this case since they give us back an error we spit it back to you and don't lint the file. 
Alright, got it. Closing the issue then as it's not a cfn-lint error really.
Hey, James from the SAM repo here. We were reviewing https://github.com/awslabs/serverless-application-model/issues/778 and we think this issue should be reopened (I don't seem to have permissions to do it). From the other issue:

> The issue here is that SAM actually needs to resolve the value of certain parameters in order to generate the logical ids of other CloudFormation resources during the transform. AutoPublishAlias is one example where this is true, which is why SAM is failing here. Since these parameters are required when actually deploying the template, it works fine when deploying direct to CloudFormation. However, it fails on validate, because CFN lint (and SAM CLI) are manually calling the SAM transform function without any parameter values in order to do validation.
> 
> The fix for this is either for the SAM translator library to offer a validate() function that accounts for issues like this under the covers, or for CFN lint to call transform with dummy parameter values.

Since SAM doesn't currently offer a `validate()` library function, the alternative fix is for CFN lint to generate dummy parameters when calling `translate()`.

https://github.com/awslabs/cfn-python-lint/blob/f5c3504f593d4311388a047fa4b6f18f792c919a/src/cfnlint/transform.py#L114
@kddejong Can we get this reopened per my comment above?
Trying to figure out what to change here.  https://github.com/aws-cloudformation/cfn-python-lint/blob/master/src/cfnlint/transform.py#L70-L84.  The trick here being I should also Remove the Stage Parameter.

```
if isinstance(resource_dict.get('AutoPublishAlias'), dict):
   resource_dict['AutoPublishAlias'] = 'Alias'
```

results in
```
W2001 Parameter Stage not used.
test.yaml:7:3
```

Even if I provide Parameter values the results are the same.  I have a few options here.  I can validate that the parameter isn't being used any where else and then remove it.  It will take me a little longer to get that written.

@jlhood I wonder if translate should remove the parameter if I provide the value for it... maybe you keep it that way for visibility?

Resolving patch:
diff --git a/src/cfnlint/transform.py b/src/cfnlint/transform.py
--- a/src/cfnlint/transform.py
+++ b/src/cfnlint/transform.py
@@ -38,6 +38,7 @@ def __init__(self, filename, template, region):
         self._filename = filename
         self._template = template
         self._region = region
+        self._parameters = {}
 
         self._managed_policy_map = self.load_managed_policies()
         self._sam_parser = parser.Parser()
@@ -70,6 +71,13 @@ def _replace_local_codeuri(self):
             if resource_type == 'AWS::Serverless::Function':
 
                 Transform._update_to_s3_uri('CodeUri', resource_dict)
+                auto_publish_alias = resource_dict.get('AutoPublishAlias')
+                if isinstance(auto_publish_alias, dict):
+                    if len(auto_publish_alias) == 1:
+                        for k, v in auto_publish_alias.items():
+                            if k == 'Ref':
+                                if v in self._template.get('Parameters'):
+                                    self._parameters[v] = 'Alias'
             if resource_type in ['AWS::Serverless::LayerVersion']:
                 if resource_dict.get('ContentUri'):
                     Transform._update_to_s3_uri('ContentUri', resource_dict)
@@ -104,7 +112,10 @@ def transform_template(self):
             os.environ['AWS_DEFAULT_REGION'] = self._region
 
             self._template = cfnlint.helpers.convert_dict(
-                sam_translator.translate(sam_template=self._template, parameter_values={}))
+                sam_translator.translate(sam_template=self._template, parameter_values=self._parameters))
+
+            for p_name in self._parameters:
+                del self._template['Parameters'][p_name]
 
             LOGGER.info('Transformed template: \n%s', cfnlint.helpers.format_json_string(self._template))
         except InvalidDocumentException as e:


Test patch:
diff --git a/test/fixtures/templates/bad/transform/auto_publish_alias.yaml b/test/fixtures/templates/bad/transform/auto_publish_alias.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/bad/transform/auto_publish_alias.yaml
@@ -0,0 +1,33 @@
+---
+AWSTemplateFormatVersion: '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+
+Parameters:
+  Stage:
+    Description: Environment stage (deployment phase)
+    Type: String
+    AllowedValues:
+      - beta
+      - prod
+
+Resources:
+  SkillFunctionExtraItems:
+    Type: AWS::Serverless::Function
+    Properties:
+      CodeUri: '.'
+      Handler: main.handler
+      Runtime: python3.7
+      Timeout: 30
+      MemorySize: 128
+      AutoPublishAlias:
+        Ref: Stage
+        Extra: Bad
+  SkillFunctionNotRef:
+    Type: AWS::Serverless::Function
+    Properties:
+      CodeUri: '.'
+      Handler: main.handler
+      Runtime: python3.7
+      Timeout: 30
+      MemorySize: 128
+      AutoPublishAlias: !GetAtt SkillFunctionExtraItems.Arn
\ No newline at end of file
diff --git a/test/fixtures/templates/good/transform/auto_publish_alias.yaml b/test/fixtures/templates/good/transform/auto_publish_alias.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/good/transform/auto_publish_alias.yaml
@@ -0,0 +1,22 @@
+---
+  AWSTemplateFormatVersion: '2010-09-09'
+  Transform: AWS::Serverless-2016-10-31
+  
+  Parameters:
+    Stage:
+      Description: Environment stage (deployment phase)
+      Type: String
+      AllowedValues:
+        - beta
+        - prod
+  
+  Resources:
+    SkillFunction:
+      Type: AWS::Serverless::Function
+      Properties:
+        CodeUri: '.'
+        Handler: main.handler
+        Runtime: python3.7
+        Timeout: 30
+        MemorySize: 128
+        AutoPublishAlias: !Ref Stage
\ No newline at end of file
diff --git a/test/module/transform/__init__.py b/test/module/transform/__init__.py
new file mode 100644
--- /dev/null
+++ b/test/module/transform/__init__.py
@@ -0,0 +1,16 @@
+"""
+  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of this
+  software and associated documentation files (the "Software"), to deal in the Software
+  without restriction, including without limitation the rights to use, copy, modify,
+  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""
diff --git a/test/module/transform/test_transform.py b/test/module/transform/test_transform.py
new file mode 100644
--- /dev/null
+++ b/test/module/transform/test_transform.py
@@ -0,0 +1,48 @@
+"""
+  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of this
+  software and associated documentation files (the "Software"), to deal in the Software
+  without restriction, including without limitation the rights to use, copy, modify,
+  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""
+import json
+from cfnlint import Transform
+from cfnlint.decode import cfn_yaml
+from testlib.testcase import BaseTestCase
+
+class TestTransform(BaseTestCase):
+    """Test Transform Parsing """
+
+    def test_parameter_for_autopublish_version(self):
+        """Test Parameter is created for autopublish version run"""
+        filename = 'test/fixtures/templates/good/transform/auto_publish_alias.yaml'
+        region = 'us-east-1'
+        template = cfn_yaml.load(filename)
+        transformed_template = Transform(filename, template, region)
+        transformed_template.transform_template()
+        self.assertDictEqual(transformed_template._parameters, {'Stage': 'Alias'})
+        self.assertDictEqual(
+          transformed_template._template.get('Resources').get('SkillFunctionAliasAlias').get('Properties'),
+          {
+            'Name': 'Alias',
+            'FunctionName': {'Ref': 'SkillFunction'},
+            'FunctionVersion': {'Fn::GetAtt': ['SkillFunctionVersionb3d38083f6', 'Version']}
+          })
+
+    def test_parameter_for_autopublish_version_bad(self):
+        """Test Parameter is created for autopublish version run"""
+        filename = 'test/fixtures/templates/bad/transform/auto_publish_alias.yaml'
+        region = 'us-east-1'
+        template = cfn_yaml.load(filename)
+        transformed_template = Transform(filename, template, region)
+        transformed_template.transform_template()
+        self.assertDictEqual(transformed_template._parameters, {})