Repository: aws-cloudformation/cfn-lint

Issue:
Unnecessary !Sub not detected
*cfn-lint version: (`cfn-lint --version`)* 0.5.0

*Description of issue.*

When refactoring code it can quite often be the situation we end up with `!Sub` applied to a string that has no substitutions, e.g. `!Sub foo`. This is less clear to read than the plain string `foo`, so it would be good to detect such cases and complain about them.


Discussion:
Good one.  This shouldn't be hard.  Thanks!

Resolving patch:
diff --git a/src/cfnlint/rules/functions/SubUnneeded.py b/src/cfnlint/rules/functions/SubUnneeded.py
new file mode 100644
--- /dev/null
+++ b/src/cfnlint/rules/functions/SubUnneeded.py
@@ -0,0 +1,60 @@
+"""
+  Copyright 2018 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 six
+from cfnlint import CloudFormationLintRule
+from cfnlint import RuleMatch
+
+
+class SubUnneeded(CloudFormationLintRule):
+    """Check if Sub is using a variable"""
+    id = 'W1020'
+    shortdesc = 'Sub isn\'t needed if it doesn\'t have a variable defined'
+    description = 'Checks sub strings to see if a variable is defined.'
+    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html'
+    tags = ['functions', 'sub']
+
+    def _test_string(self, cfn, sub_string, tree):
+        """Test if a string has appropriate parameters"""
+
+        matches = list()
+        string_params = cfn.get_sub_parameters(sub_string)
+
+        if not string_params:
+            message = 'Fn::Sub isn\'t needed because there are no variables at {0}'
+            matches.append(RuleMatch(
+                tree, message.format('/'.join(map(str, tree)))))
+
+        return matches
+
+    def match(self, cfn):
+        """Check CloudFormation Join"""
+
+        matches = list()
+
+        sub_objs = cfn.search_deep_keys('Fn::Sub')
+
+        for sub_obj in sub_objs:
+            sub_value_obj = sub_obj[-1]
+            tree = sub_obj[:-1]
+            if isinstance(sub_value_obj, six.string_types):
+                matches.extend(self._test_string(cfn, sub_value_obj, tree))
+            elif isinstance(sub_value_obj, list):
+                if len(sub_value_obj) == 2:
+                    sub_string = sub_value_obj[0]
+                    matches.extend(self._test_string(cfn, sub_string, tree + [0]))
+
+        return matches


Test patch:
diff --git a/test/fixtures/results/quickstart/openshift.json b/test/fixtures/results/quickstart/openshift.json
--- a/test/fixtures/results/quickstart/openshift.json
+++ b/test/fixtures/results/quickstart/openshift.json
@@ -3,7 +3,8 @@
         "Rule": {
             "Id": "W7001",
             "Description": "Making sure the mappings defined are used",
-            "ShortDescription": "Check if Mappings are Used"
+            "ShortDescription": "Check if Mappings are Used",
+            "Source": "https://github.com/awslabs/cfn-python-lint"
         },
         "Location": {
             "Start": {
@@ -17,13 +18,14 @@
         },
         "Level": "Warning",
         "Message": "Mapping LinuxAMINameMap not used",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "W3005",
-            "Description": "Check if DependsOn is specified if not needed. A Ref and GetAtt implicitly results in DependsOn behaviour.",
-            "ShortDescription": "Check obsolete DependsOn configuration for Resources"
+            "Description": "Check if DependsOn is specified if not needed. A Ref or a Fn::GetAtt already is an implicit dependency.",
+            "ShortDescription": "Check obsolete DependsOn configuration for Resources",
+            "Source": "https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/"
         },
         "Location": {
             "Start": {
@@ -36,14 +38,15 @@
             }
         },
         "Level": "Warning",
-        "Message": "Obsolete DependsOn on resource (OpenShiftNodeASG), dependency already enforced by \"!Ref\" at Resources/AnsibleConfigServer/Properties/UserData/Fn::Base64/Fn::Join/1/43/Ref/OpenShiftNodeASG",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Message": "Obsolete DependsOn on resource (OpenShiftNodeASG), dependency already enforced by a \"Ref\" at Resources/AnsibleConfigServer/Properties/UserData/Fn::Base64/Fn::Join/1/43/Ref/OpenShiftNodeASG",
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "W3005",
-            "Description": "Check if DependsOn is specified if not needed. A Ref and GetAtt implicitly results in DependsOn behaviour.",
-            "ShortDescription": "Check obsolete DependsOn configuration for Resources"
+            "Description": "Check if DependsOn is specified if not needed. A Ref or a Fn::GetAtt already is an implicit dependency.",
+            "ShortDescription": "Check obsolete DependsOn configuration for Resources",
+            "Source": "https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/"
         },
         "Location": {
             "Start": {
@@ -56,14 +59,15 @@
             }
         },
         "Level": "Warning",
-        "Message": "Obsolete DependsOn on resource (OpenShiftNodeASG), dependency already enforced by \"!Ref\" at Resources/AnsibleConfigServer/Properties/UserData/Fn::Base64/Fn::Join/1/88/Ref/OpenShiftNodeASG",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Message": "Obsolete DependsOn on resource (OpenShiftNodeASG), dependency already enforced by a \"Ref\" at Resources/AnsibleConfigServer/Properties/UserData/Fn::Base64/Fn::Join/1/88/Ref/OpenShiftNodeASG",
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -77,13 +81,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/AnsibleConfigServer/Properties/NetworkInterfaces/0/AssociatePublicIpAddress should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -97,13 +102,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/AnsibleConfigServer/Properties/NetworkInterfaces/0/DeleteOnTermination should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "W3005",
-            "Description": "Check if DependsOn is specified if not needed. A Ref and GetAtt implicitly results in DependsOn behaviour.",
-            "ShortDescription": "Check obsolete DependsOn configuration for Resources"
+            "Description": "Check if DependsOn is specified if not needed. A Ref or a Fn::GetAtt already is an implicit dependency.",
+            "ShortDescription": "Check obsolete DependsOn configuration for Resources",
+            "Source": "https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/"
         },
         "Location": {
             "Start": {
@@ -116,14 +122,36 @@
             }
         },
         "Level": "Warning",
-        "Message": "Obsolete DependsOn on resource (KeyGen), dependency already enforced by \"!Fn:GetAtt\" at Resources/GetRSA/Properties/ServiceToken/Fn::GetAtt/['KeyGen', 'Arn']",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Message": "Obsolete DependsOn on resource (KeyGen), dependency already enforced by a \"Fn:GetAtt\" at Resources/GetRSA/Properties/ServiceToken/Fn::GetAtt/['KeyGen', 'Arn']",
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
+    },
+    {
+        "Rule": {
+            "Id": "W1020",
+            "Description": "Checks sub strings to see if a variable is defined.",
+            "ShortDescription": "Sub isn't needed if it doesn't have a variable defined",
+            "Source": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html"
+        },
+        "Location": {
+            "Start": {
+                "ColumnNumber": 11,
+                "LineNumber": 804
+            },
+            "End": {
+                "ColumnNumber": 18,
+                "LineNumber": 804
+            }
+        },
+        "Level": "Warning",
+        "Message": "Fn::Sub isn't needed because there are no variables at Resources/KeyGen/Properties/Code/S3Key/Fn::Sub",
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -137,13 +165,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/KeyGen/Properties/Timeout should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -157,13 +186,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftEtcdASG/Properties/Tags/0/PropagateAtLaunch should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -177,13 +207,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftEtcdLaunchConfig/Properties/BlockDeviceMappings/0/Ebs/VolumeSize should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -197,13 +228,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftEtcdLaunchConfig/Properties/InstanceMonitoring should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -217,13 +249,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftMasterASG/Properties/Tags/0/PropagateAtLaunch should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "W3005",
-            "Description": "Check if DependsOn is specified if not needed. A Ref and GetAtt implicitly results in DependsOn behaviour.",
-            "ShortDescription": "Check obsolete DependsOn configuration for Resources"
+            "Description": "Check if DependsOn is specified if not needed. A Ref or a Fn::GetAtt already is an implicit dependency.",
+            "ShortDescription": "Check obsolete DependsOn configuration for Resources",
+            "Source": "https://aws.amazon.com/blogs/devops/optimize-aws-cloudformation-templates/"
         },
         "Location": {
             "Start": {
@@ -236,14 +269,15 @@
             }
         },
         "Level": "Warning",
-        "Message": "Obsolete DependsOn on resource (GetRSA), dependency already enforced by \"!Fn:GetAtt\" at Resources/OpenShiftMasterASLaunchConfig/Metadata/AWS::CloudFormation::Init/GetPublicKey/files//root/.ssh/public.key/content/Fn::Join/1/1/Fn::GetAtt/['GetRSA', 'PUB']",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Message": "Obsolete DependsOn on resource (GetRSA), dependency already enforced by a \"Fn:GetAtt\" at Resources/OpenShiftMasterASLaunchConfig/Metadata/AWS::CloudFormation::Init/GetPublicKey/files//root/.ssh/public.key/content/Fn::Join/1/1/Fn::GetAtt/['GetRSA', 'PUB']",
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -257,13 +291,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftMasterASLaunchConfig/Properties/BlockDeviceMappings/0/Ebs/VolumeSize should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -277,13 +312,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftMasterASLaunchConfig/Properties/InstanceMonitoring should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -297,13 +333,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodeASG/Properties/Tags/0/PropagateAtLaunch should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -317,13 +354,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodeSecurityGroup/Properties/SecurityGroupIngress/1/FromPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -337,13 +375,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodeSecurityGroup/Properties/SecurityGroupIngress/1/ToPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -357,13 +396,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodeSecurityGroup/Properties/SecurityGroupIngress/2/FromPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -377,13 +417,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodeSecurityGroup/Properties/SecurityGroupIngress/2/ToPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -397,13 +438,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodesLaunchConfig/Properties/BlockDeviceMappings/0/Ebs/VolumeSize should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -417,13 +459,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodesLaunchConfig/Properties/BlockDeviceMappings/1/Ebs/VolumeSize should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -437,13 +480,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftNodesLaunchConfig/Properties/InstanceMonitoring should be of type Boolean",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -457,13 +501,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftSecurityGroup/Properties/SecurityGroupIngress/1/FromPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -477,13 +522,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftSecurityGroup/Properties/SecurityGroupIngress/1/ToPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -497,13 +543,14 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftSecurityGroup/Properties/SecurityGroupIngress/2/FromPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     },
     {
         "Rule": {
             "Id": "E3012",
             "Description": "Checks resource property values with Primitive Types for values that match those types.",
-            "ShortDescription": "Check resource properties values"
+            "ShortDescription": "Check resource properties values",
+            "Source": "https://github.com/awslabs/cfn-python-lint/blob/master/docs/cfn-resource-specification.md#valueprimitivetype"
         },
         "Location": {
             "Start": {
@@ -517,6 +564,6 @@
         },
         "Level": "Error",
         "Message": "Property Resources/OpenShiftSecurityGroup/Properties/SecurityGroupIngress/2/ToPort should be of type Integer",
-        "Filename": "test/templates/quickstart/openshift.yaml"
+        "Filename": "test/fixtures/templates/quickstart/openshift.yaml"
     }
 ]
diff --git a/test/fixtures/templates/bad/functions/sub_unneeded.yaml b/test/fixtures/templates/bad/functions/sub_unneeded.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/bad/functions/sub_unneeded.yaml
@@ -0,0 +1,8 @@
+---
+AWSTemplateFormatVersion: "2010-09-09"
+Resources:
+  myInstance:
+    Type: AWS::EC2::Instance
+    Properties:
+      # Sub without a variable
+      ImageId: !Sub "ami-123456"
diff --git a/test/rules/functions/test_sub_unneeded.py b/test/rules/functions/test_sub_unneeded.py
new file mode 100644
--- /dev/null
+++ b/test/rules/functions/test_sub_unneeded.py
@@ -0,0 +1,37 @@
+"""
+  Copyright 2018 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.
+"""
+from cfnlint.rules.functions.SubUnneeded import SubUnneeded  # pylint: disable=E0401
+from .. import BaseRuleTestCase
+
+
+class TestSubUnneeded(BaseRuleTestCase):
+    """Test Rules Get Att """
+    def setUp(self):
+        """Setup"""
+        super(TestSubUnneeded, self).setUp()
+        self.collection.register(SubUnneeded())
+        self.success_templates = [
+            'fixtures/templates/good/functions_sub.yaml',
+        ]
+
+    def test_file_positive(self):
+        """Test Positive"""
+        self.helper_file_positive()
+
+    def test_file_negative(self):
+        """Test failure"""
+        self.helper_file_negative('fixtures/templates/bad/functions/sub_unneeded.yaml', 1)