prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  E2504 enforces incorrect boundary 100-2000, should be 100-20000
  *cfn-lint version: (`cfn-lint --version`)* 0.15.0 (also tested with 0.14.1)
  
  **Description:**
  AWS EC2 Instance with block device mapping does not enforce the correct `Ebs/Iops` boundary of 100-20000 as specified in the [documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-blockdev-template.html)
  
  It looks like it actually enforces 100-2000, as if you set `Iops` to 2000 it passes the linter.
  
  **Sample to reproduce:**
  ```yaml
  Resources:
    Machine:
      Type: AWS::EC2::Instance
      Properties:
        ImageId: "ami-79fd7eee"
        KeyName: "testkey"
        BlockDeviceMappings: 
          - DeviceName: /dev/sdm
            Ebs: 
              VolumeType: io1
              Iops: 3000 
              DeleteOnTermination: false
              VolumeSize: 20
  ```
  
  Output:
  ```bash
  > cfn-lint minimal-stack.yaml
  
  E2504 Property Iops should be Int between 100 to 20000 Resources/Machine/Properties/BlockDeviceMappings/0/Ebs/Iops
  minimal-stack.yaml:11:13
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/cfnlint/rules/resources/ectwo/Ebs.py`
  Content:
  ```
  1 """
  2   Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3 
  4   Permission is hereby granted, free of charge, to any person obtaining a copy of this
  5   software and associated documentation files (the "Software"), to deal in the Software
  6   without restriction, including without limitation the rights to use, copy, modify,
  7   merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
  8   permit persons to whom the Software is furnished to do so.
  9 
  10   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  11   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  12   PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  13   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  14   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  15   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16 """
  17 import re
  18 import six
  19 from cfnlint import CloudFormationLintRule
  20 from cfnlint import RuleMatch
  21 
  22 
  23 class Ebs(CloudFormationLintRule):
  24     """Check if Ec2 Ebs Resource Properties"""
  25     id = 'E2504'
  26     shortdesc = 'Check Ec2 Ebs Properties'
  27     description = 'See if Ec2 Eb2 Properties are valid'
  28     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-blockdev-template.html'
  29     tags = ['properties', 'ec2', 'ebs']
  30 
  31     def _checkEbs(self, cfn, ebs, path):
  32         matches = []
  33 
  34         if isinstance(ebs, dict):
  35             volume_types_obj = cfn.get_values(ebs, 'VolumeType')
  36             iops_obj = cfn.get_values(ebs, 'Iops')
  37             if volume_types_obj is not None:
  38                 for volume_type_obj in volume_types_obj:
  39                     volume_type = volume_type_obj.get('Value')
  40                     if isinstance(volume_type, six.string_types):
  41                         if volume_type == 'io1':
  42                             if iops_obj is None:
  43                                 pathmessage = path[:] + ['VolumeType']
  44                                 message = 'VolumeType io1 requires Iops to be specified for {0}'
  45                                 matches.append(
  46                                     RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))
  47                             else:
  48                                 try:
  49                                     if len(iops_obj) == 1:
  50                                         iops = iops_obj[0]['Value']
  51                                         if isinstance(iops, (six.string_types, int)) and not iops_obj[0]['Path']:
  52                                             iops_value = int(iops)
  53                                             if iops_value < 100 or iops_value > 2000:
  54                                                 pathmessage = path[:] + ['Iops']
  55                                                 message = 'Property Iops should be Int between 100 to 20000 {0}'
  56                                                 matches.append(
  57                                                     RuleMatch(
  58                                                         pathmessage,
  59                                                         message.format('/'.join(map(str, pathmessage)))))
  60                                 except ValueError:
  61                                     pathmessage = path[:] + ['Iops']
  62                                     message = 'Property Iops should be Int between 100 to 20000 {0}'
  63                                     matches.append(
  64                                         RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))
  65                         elif volume_type:
  66                             if iops_obj is not None:
  67                                 pathmessage = path[:] + ['Iops']
  68                                 message = 'Iops shouldn\'t be defined for type {0} for {1}'
  69                                 matches.append(
  70                                     RuleMatch(
  71                                         pathmessage,
  72                                         message.format(volume_type, '/'.join(map(str, pathmessage)))))
  73 
  74         return matches
  75 
  76     def match(self, cfn):
  77         """Check Ec2 Ebs Resource Parameters"""
  78 
  79         matches = []
  80 
  81         results = cfn.get_resource_properties(['AWS::EC2::Instance', 'BlockDeviceMappings'])
  82         results.extend(cfn.get_resource_properties(['AWS::AutoScaling::LaunchConfiguration', 'BlockDeviceMappings']))
  83         for result in results:
  84             path = result['Path']
  85             for index, properties in enumerate(result['Value']):
  86                 virtual_name = properties.get('VirtualName')
  87                 ebs = properties.get('Ebs')
  88                 if virtual_name:
  89                     # switch to regex
  90                     if not re.match(r'^ephemeral[0-9]$', virtual_name):
  91                         pathmessage = path[:] + [index, 'VirtualName']
  92                         message = 'Property VirtualName should be of type ephemeral(n) for {0}'
  93                         matches.append(
  94                             RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))
  95                 elif ebs:
  96                     matches.extend(self._checkEbs(cfn, ebs, path[:] + [index, 'Ebs']))
  97         return matches
  98
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/cfnlint/rules/resources/ectwo/Ebs.py b/src/cfnlint/rules/resources/ectwo/Ebs.py
  --- a/src/cfnlint/rules/resources/ectwo/Ebs.py
  +++ b/src/cfnlint/rules/resources/ectwo/Ebs.py
  @@ -50,7 +50,7 @@
                                           iops = iops_obj[0]['Value']
                                           if isinstance(iops, (six.string_types, int)) and not iops_obj[0]['Path']:
                                               iops_value = int(iops)
  -                                            if iops_value < 100 or iops_value > 2000:
  +                                            if iops_value < 100 or iops_value > 20000:
                                                   pathmessage = path[:] + ['Iops']
                                                   message = 'Property Iops should be Int between 100 to 20000 {0}'
                                                   matches.append(
  
verification_info: {"golden_diff": "diff --git a/src/cfnlint/rules/resources/ectwo/Ebs.py b/src/cfnlint/rules/resources/ectwo/Ebs.py\n--- a/src/cfnlint/rules/resources/ectwo/Ebs.py\n+++ b/src/cfnlint/rules/resources/ectwo/Ebs.py\n@@ -50,7 +50,7 @@\n                                         iops = iops_obj[0]['Value']\n                                         if isinstance(iops, (six.string_types, int)) and not iops_obj[0]['Path']:\n                                             iops_value = int(iops)\n-                                            if iops_value < 100 or iops_value > 2000:\n+                                            if iops_value < 100 or iops_value > 20000:\n                                                 pathmessage = path[:] + ['Iops']\n                                                 message = 'Property Iops should be Int between 100 to 20000 {0}'\n                                                 matches.append(\n", "issue": "E2504 enforces incorrect boundary 100-2000, should be 100-20000\n*cfn-lint version: (`cfn-lint --version`)* 0.15.0 (also tested with 0.14.1)\r\n\r\n**Description:**\r\nAWS EC2 Instance with block device mapping does not enforce the correct `Ebs/Iops` boundary of 100-20000 as specified in the [documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-blockdev-template.html)\r\n\r\nIt looks like it actually enforces 100-2000, as if you set `Iops` to 2000 it passes the linter.\r\n\r\n**Sample to reproduce:**\r\n```yaml\r\nResources:\r\n  Machine:\r\n    Type: AWS::EC2::Instance\r\n    Properties:\r\n      ImageId: \"ami-79fd7eee\"\r\n      KeyName: \"testkey\"\r\n      BlockDeviceMappings: \r\n        - DeviceName: /dev/sdm\r\n          Ebs: \r\n            VolumeType: io1\r\n            Iops: 3000 \r\n            DeleteOnTermination: false\r\n            VolumeSize: 20\r\n```\r\n\r\nOutput:\r\n```bash\r\n> cfn-lint minimal-stack.yaml\r\n\r\nE2504 Property Iops should be Int between 100 to 20000 Resources/Machine/Properties/BlockDeviceMappings/0/Ebs/Iops\r\nminimal-stack.yaml:11:13\r\n```\r\n\n", "before_files": [{"content": "\"\"\"\n  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport re\nimport six\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\n\n\nclass Ebs(CloudFormationLintRule):\n    \"\"\"Check if Ec2 Ebs Resource Properties\"\"\"\n    id = 'E2504'\n    shortdesc = 'Check Ec2 Ebs Properties'\n    description = 'See if Ec2 Eb2 Properties are valid'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-blockdev-template.html'\n    tags = ['properties', 'ec2', 'ebs']\n\n    def _checkEbs(self, cfn, ebs, path):\n        matches = []\n\n        if isinstance(ebs, dict):\n            volume_types_obj = cfn.get_values(ebs, 'VolumeType')\n            iops_obj = cfn.get_values(ebs, 'Iops')\n            if volume_types_obj is not None:\n                for volume_type_obj in volume_types_obj:\n                    volume_type = volume_type_obj.get('Value')\n                    if isinstance(volume_type, six.string_types):\n                        if volume_type == 'io1':\n                            if iops_obj is None:\n                                pathmessage = path[:] + ['VolumeType']\n                                message = 'VolumeType io1 requires Iops to be specified for {0}'\n                                matches.append(\n                                    RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))\n                            else:\n                                try:\n                                    if len(iops_obj) == 1:\n                                        iops = iops_obj[0]['Value']\n                                        if isinstance(iops, (six.string_types, int)) and not iops_obj[0]['Path']:\n                                            iops_value = int(iops)\n                                            if iops_value < 100 or iops_value > 2000:\n                                                pathmessage = path[:] + ['Iops']\n                                                message = 'Property Iops should be Int between 100 to 20000 {0}'\n                                                matches.append(\n                                                    RuleMatch(\n                                                        pathmessage,\n                                                        message.format('/'.join(map(str, pathmessage)))))\n                                except ValueError:\n                                    pathmessage = path[:] + ['Iops']\n                                    message = 'Property Iops should be Int between 100 to 20000 {0}'\n                                    matches.append(\n                                        RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))\n                        elif volume_type:\n                            if iops_obj is not None:\n                                pathmessage = path[:] + ['Iops']\n                                message = 'Iops shouldn\\'t be defined for type {0} for {1}'\n                                matches.append(\n                                    RuleMatch(\n                                        pathmessage,\n                                        message.format(volume_type, '/'.join(map(str, pathmessage)))))\n\n        return matches\n\n    def match(self, cfn):\n        \"\"\"Check Ec2 Ebs Resource Parameters\"\"\"\n\n        matches = []\n\n        results = cfn.get_resource_properties(['AWS::EC2::Instance', 'BlockDeviceMappings'])\n        results.extend(cfn.get_resource_properties(['AWS::AutoScaling::LaunchConfiguration', 'BlockDeviceMappings']))\n        for result in results:\n            path = result['Path']\n            for index, properties in enumerate(result['Value']):\n                virtual_name = properties.get('VirtualName')\n                ebs = properties.get('Ebs')\n                if virtual_name:\n                    # switch to regex\n                    if not re.match(r'^ephemeral[0-9]$', virtual_name):\n                        pathmessage = path[:] + [index, 'VirtualName']\n                        message = 'Property VirtualName should be of type ephemeral(n) for {0}'\n                        matches.append(\n                            RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))\n                elif ebs:\n                    matches.extend(self._checkEbs(cfn, ebs, path[:] + [index, 'Ebs']))\n        return matches\n", "path": "src/cfnlint/rules/resources/ectwo/Ebs.py"}], "after_files": [{"content": "\"\"\"\n  Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport re\nimport six\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\n\n\nclass Ebs(CloudFormationLintRule):\n    \"\"\"Check if Ec2 Ebs Resource Properties\"\"\"\n    id = 'E2504'\n    shortdesc = 'Check Ec2 Ebs Properties'\n    description = 'See if Ec2 Eb2 Properties are valid'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-blockdev-template.html'\n    tags = ['properties', 'ec2', 'ebs']\n\n    def _checkEbs(self, cfn, ebs, path):\n        matches = []\n\n        if isinstance(ebs, dict):\n            volume_types_obj = cfn.get_values(ebs, 'VolumeType')\n            iops_obj = cfn.get_values(ebs, 'Iops')\n            if volume_types_obj is not None:\n                for volume_type_obj in volume_types_obj:\n                    volume_type = volume_type_obj.get('Value')\n                    if isinstance(volume_type, six.string_types):\n                        if volume_type == 'io1':\n                            if iops_obj is None:\n                                pathmessage = path[:] + ['VolumeType']\n                                message = 'VolumeType io1 requires Iops to be specified for {0}'\n                                matches.append(\n                                    RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))\n                            else:\n                                try:\n                                    if len(iops_obj) == 1:\n                                        iops = iops_obj[0]['Value']\n                                        if isinstance(iops, (six.string_types, int)) and not iops_obj[0]['Path']:\n                                            iops_value = int(iops)\n                                            if iops_value < 100 or iops_value > 20000:\n                                                pathmessage = path[:] + ['Iops']\n                                                message = 'Property Iops should be Int between 100 to 20000 {0}'\n                                                matches.append(\n                                                    RuleMatch(\n                                                        pathmessage,\n                                                        message.format('/'.join(map(str, pathmessage)))))\n                                except ValueError:\n                                    pathmessage = path[:] + ['Iops']\n                                    message = 'Property Iops should be Int between 100 to 20000 {0}'\n                                    matches.append(\n                                        RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))\n                        elif volume_type:\n                            if iops_obj is not None:\n                                pathmessage = path[:] + ['Iops']\n                                message = 'Iops shouldn\\'t be defined for type {0} for {1}'\n                                matches.append(\n                                    RuleMatch(\n                                        pathmessage,\n                                        message.format(volume_type, '/'.join(map(str, pathmessage)))))\n\n        return matches\n\n    def match(self, cfn):\n        \"\"\"Check Ec2 Ebs Resource Parameters\"\"\"\n\n        matches = []\n\n        results = cfn.get_resource_properties(['AWS::EC2::Instance', 'BlockDeviceMappings'])\n        results.extend(cfn.get_resource_properties(['AWS::AutoScaling::LaunchConfiguration', 'BlockDeviceMappings']))\n        for result in results:\n            path = result['Path']\n            for index, properties in enumerate(result['Value']):\n                virtual_name = properties.get('VirtualName')\n                ebs = properties.get('Ebs')\n                if virtual_name:\n                    # switch to regex\n                    if not re.match(r'^ephemeral[0-9]$', virtual_name):\n                        pathmessage = path[:] + [index, 'VirtualName']\n                        message = 'Property VirtualName should be of type ephemeral(n) for {0}'\n                        matches.append(\n                            RuleMatch(pathmessage, message.format('/'.join(map(str, pathmessage)))))\n                elif ebs:\n                    matches.extend(self._checkEbs(cfn, ebs, path[:] + [index, 'Ebs']))\n        return matches\n", "path": "src/cfnlint/rules/resources/ectwo/Ebs.py"}]}