Repository: aws-cloudformation/cfn-lint

Issue:
E3030 case sensitivity caused DB instance destruction!
*cfn-lint version: (`cfn-lint --version`)* 0.29.0

*Description of issue.*

E3030 told me that "Postgres" was not an allowed engine name, so I changed it to "postgres". This caused cloudformation to rebuild my database instance, aka complete data loss. Luckily only discovered this on staging environment.

I think since CloudFormation / RDS seems to allow it, this check should be made case insensitive :)



Discussion:
I think this is also a CloudFormation bug, in that it compares the strings in a case-sensitive manner, when RDS underneath compares them in a case-insensitive manner. If you could file a bug there too that would be great.
Doing some more testing now.  For some reason we called out mysql with multiple spellings.  
```
"AllowedValues": [
        "aurora",
        "aurora-mysql",
        "aurora-postgresql",
        "mariadb",
        "mysql",
        "MySQL",
        "oracle-ee",
        "oracle-se2",
        "oracle-se1",
        "oracle-se",
        "postgres",
        "sqlserver-ee",
        "sqlserver-se",
        "sqlserver-ex",
        "sqlserver-web"
      ]
```
I tested a few other resources (e.g. Lambda) and they all aren't case insensitive.  So this seems particular to RDS.  So I think we should leave the check as is, remove the values from this location and create a new rule that deals with case insensitive comparison.

Resolving patch:
diff --git a/src/cfnlint/rules/resources/rds/InstanceEngine.py b/src/cfnlint/rules/resources/rds/InstanceEngine.py
new file mode 100644
--- /dev/null
+++ b/src/cfnlint/rules/resources/rds/InstanceEngine.py
@@ -0,0 +1,57 @@
+"""
+Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: MIT-0
+"""
+import json
+from cfnlint.rules import CloudFormationLintRule
+from cfnlint.rules import RuleMatch
+
+
+class InstanceEngine(CloudFormationLintRule):
+    """Check if Resources RDS Instance Size is compatible with the RDS type"""
+    id = 'E3040'
+    shortdesc = 'RDS DB Instance Engine is valid'
+    description = 'Check the RDS DB Instance Engine is valid'
+    source_url = 'https: // docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_CreateDBInstance.html'
+    tags = ['resources', 'rds']
+
+    valid_engines = [
+        'aurora',
+        'aurora-mysql',
+        'aurora-postgresql',
+        'mariadb',
+        'mysql',
+        'oracle-ee',
+        'oracle-se2',
+        'oracle-se1',
+        'oracle-se',
+        'postgres',
+        'sqlserver-ee',
+        'sqlserver-se',
+        'sqlserver-ex',
+        'sqlserver-web'
+    ]
+
+    def __init__(self):
+        """Init"""
+        super(InstanceEngine, self).__init__()
+        self.resource_property_types = ['AWS::RDS::DBInstance']
+
+    def check_value(self, value, path):
+        """Check engine"""
+        if value.lower() not in self.valid_engines:
+            message = 'RDS Engine "{0}" must be one of {1}'
+            return [RuleMatch(path, message.format(value, json.dumps(self.valid_engines)))]
+
+        return []
+
+    def match_resource_properties(self, properties, _, path, cfn):
+        """Match for sub properties"""
+        matches = []
+        matches.extend(
+            cfn.check_value(
+                obj=properties, key='Engine',
+                path=path[:],
+                check_value=self.check_value
+            ))
+        return matches


Test patch:
diff --git a/test/fixtures/templates/bad/resources/rds/instance_engine.yaml b/test/fixtures/templates/bad/resources/rds/instance_engine.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/bad/resources/rds/instance_engine.yaml
@@ -0,0 +1,20 @@
+---
+AWSTemplateFormatVersion: "2010-09-09"
+Parameters:
+  snapshotid:
+    Type: String
+Conditions:
+  snapshot: !Not [!Equals [!Ref snapshotid, ""]]
+Resources:
+  # Doesnt check if conditions are used
+  DBInstance1:
+    Type: AWS::RDS::DBInstance
+    Properties:
+      DBInstanceClass: db.m4.xlarge
+      Engine: !If [snapshot, badengine, simple]
+  # Doesnt fail on valid instance type
+  DBInstance2:
+    Type: AWS::RDS::DBInstance
+    Properties:
+      DBInstanceClass: db.r4.xlarge
+      Engine: auroramysql
diff --git a/test/fixtures/templates/good/resources/rds/instance_engine.yaml b/test/fixtures/templates/good/resources/rds/instance_engine.yaml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/templates/good/resources/rds/instance_engine.yaml
@@ -0,0 +1,20 @@
+---
+AWSTemplateFormatVersion: "2010-09-09"
+Parameters:
+  snapshotid:
+    Type: String
+Conditions:
+  snapshot: !Not [!Equals [!Ref snapshotid, ""]]
+Resources:
+  # Doesnt check if conditions are used
+  DBInstance1:
+    Type: AWS::RDS::DBInstance
+    Properties:
+      DBInstanceClass: db.m4.xlarge
+      Engine: !If [snapshot, !Ref aurora-mysql, aurora]
+  # Doesnt fail on valid instance type
+  DBInstance2:
+    Type: AWS::RDS::DBInstance
+    Properties:
+      DBInstanceClass: db.r4.xlarge
+      Engine: oracle-se2
diff --git a/test/unit/rules/resources/rds/test_instance_engine.py b/test/unit/rules/resources/rds/test_instance_engine.py
new file mode 100644
--- /dev/null
+++ b/test/unit/rules/resources/rds/test_instance_engine.py
@@ -0,0 +1,27 @@
+"""
+Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: MIT-0
+"""
+from test.unit.rules import BaseRuleTestCase
+from cfnlint.rules.resources.rds.InstanceEngine import InstanceEngine  # pylint: disable=E0401
+
+
+class TestInstanceEngine(BaseRuleTestCase):
+    """Test RDS Instance Size Configuration"""
+
+    def setUp(self):
+        """Setup"""
+        super(TestInstanceEngine, self).setUp()
+        self.collection.register(InstanceEngine())
+        self.success_templates = [
+            'test/fixtures/templates/good/resources/rds/instance_engine.yaml'
+        ]
+
+    def test_file_positive(self):
+        """Test Positive"""
+        self.helper_file_positive()
+
+    def test_file_negative_alias(self):
+        """Test failure"""
+        self.helper_file_negative(
+            'test/fixtures/templates/bad/resources/rds/instance_engine.yaml', 3)