We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Checkov action fails for certain modules due to what seems like datatype issue
Facing an issue when checkov runs this module `"terraform-aws-modules/eks/aws"`. Specifically for `metadata_options` and `remote_access`. Seems like the issue is in the code below where checkov is expecting a list but gets a dictionary so it throws an error, if I remove the indices then it runs ok. Also, it seems that it's linked to the external modules, cause if I run checkov with `--download-external-modules false`, the error is not there.

The ERROR:
```
Traceback (most recent call last):
  File "/usr/local/bin/checkov", line 9, in <module>
    sys.exit(run())
  File "/usr/local/lib/python3.8/site-packages/checkov/main.py", line 330, in run
    scan_reports = runner_registry.run(root_folder=root_folder, external_checks_dir=external_checks_dir,
  File "/usr/local/lib/python3.8/site-packages/checkov/common/runners/runner_registry.py", line 79, in run
    self.runners[0].run(root_folder, external_checks_dir=external_checks_dir, files=files,
  File "/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py", line 147, in run
    self.check_tf_definition(report, root_folder, runner_filter, collect_skip_comments)
  File "/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py", line 292, in check_tf_definition
    self.run_all_blocks(definition, self.context, full_file_path, root_folder, report,
  File "/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py", line 304, in run_all_blocks
    self.run_block(definition[block_type], definitions_context,
  File "/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py", line 376, in run_block
    results = registry.scan(scanned_file, entity, skipped_checks, runner_filter)
  File "/usr/local/lib/python3.8/site-packages/checkov/common/checks/base_check_registry.py", line 1[27](https://github.com/amun/infra-terrafrom/actions/runs/3437973445/jobs/5733435968#step:5:28), in scan
    result = self.run_check(check, entity_configuration, entity_name, entity_type, scanned_file, skip_info)
  File "/usr/local/lib/python3.8/site-packages/checkov/common/checks/base_check_registry.py", line 141, in run_check
    result = check.run(
  File "/usr/local/lib/python3.8/site-packages/checkov/common/checks/base_check.py", line 70, in run
    check_result["result"] = self.scan_entity_conf(entity_configuration, entity_type)
  File "/usr/local/lib/python3.8/site-packages/checkov/terraform/checks/resource/base_resource_check.py", line 43, in scan_entity_conf
    return self.scan_resource_conf(conf)
  File "/usr/local/lib/python3.8/site-packages/checkov/terraform/checks/resource/aws/IMDSv1Disabled.py", line [33](https://github.com/amun/infra-terrafrom/actions/runs/3437973445/jobs/5733435968#step:5:34), in scan_resource_conf
    if 'metadata_options' not in conf.keys() or not isinstance(conf['metadata_options'][0], dict):
KeyError: 0
```

Yaml file:
```
name: Checkov
on:
  pull_request:
    branches:
      - staging
      - production
      - global

jobs:
  checkov-job:
    strategy:
      matrix:
        module: [aws-config, iam, ecs, eks, flux, lambdas, step-functions, vpc, vpc-peering, ecr]

    runs-on: 'self-hosted'
    name: checkov
    steps:
      - 
        name: Checkout repo
        uses: actions/checkout@master

      - 
        name: Run Checkov
        id: checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: modules/${{ matrix.module }}
          quiet: true # optional: display only failed checks
          soft_fail: true # optional: do not return an error code if there are failed checks
          framework: terraform # optional: run only on a specific infrastructure {cloudformation,terraform,kubernetes,all}
          output_format: sarif # optional: the output format, one of: cli, json, junitxml, github_failed_only, or sarif. Default: sarif
          download_external_modules: true # optional: download external terraform modules from public git repositories and terraform registry
          log_level: WARNING # optional: set log level. Default WARNING

```

**Functions in checkov source code that might be responsible for the error?:**

from `IMDSv1Disabled.py`:
```
def scan_resource_conf(self, conf):
        """
        Looks for if the metadata service is disabled or requires session tokens:
            https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#metadata-options
            or
            https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template#metadata-options

        :param conf: dict of supported resource configuration
        :return: <CheckResult>
        """
        if 'metadata_options' not in conf.keys() or not isinstance(conf['metadata_options'][0], dict):
            return CheckResult.FAILED
        metadata_options = conf['metadata_options'][0]
        if ('http_tokens' in metadata_options and metadata_options["http_tokens"] == ["required"]) or \
                ('http_endpoint' in metadata_options and metadata_options["http_endpoint"] == ["disabled"]):
            return CheckResult.PASSED
        return CheckResult.FAILED
```
from `EKSNodeGroupRemoteAccess.py`:
```
def scan_resource_conf(self, conf):
        remote_access = conf.get("remote_access")
        if remote_access and remote_access[0] and "ec2_ssh_key" in remote_access[0].keys() \
                and "source_security_group_ids" not in remote_access[0].keys():
            return CheckResult.FAILED
        return CheckResult.PASSED
```

Appreciate your help and let me know if you need me to provide more details.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `checkov/terraform/parser_functions.py`
Content:
```
1 import json
2 import logging
3 from typing import Dict, List, Union, Any
4 
5 from checkov.common.util.type_forcers import convert_str_to_bool
6 from checkov.common.util.parser_utils import eval_string, split_merge_args, string_to_native, to_string
7 
8 #
9 # Functions defined in this file implement terraform functions.
10 #
11 # Inputs:
12 #   - First arg (unnamed) - the value string provided to the function
13 #   - "var_resolver" - function pointer to resolve variable/local references and such
14 #   - "function_name" - name of the function being called (mainly useful for error reporting when a
15 #                       function isn't defined)
16 # These may be expanded over time, so accepting kwargs (via `**`) is recommended.
17 #
18 # If the value cannot be processed, `FUNCTION_FAILED` should be returned.
19 #
20 
21 FUNCTION_FAILED = "____FUNCTION_FAILED____"
22 
23 
24 def merge(original, var_resolver, **_):
25     # https://www.terraform.io/docs/language/functions/merge.html
26     args = split_merge_args(original)
27     if args is None:
28         return FUNCTION_FAILED
29     merged_map = {}
30     for arg in args:
31         if arg.startswith("{"):
32             arg_value = string_to_native(arg)
33             if arg_value is None:
34                 return FUNCTION_FAILED
35         else:
36             arg_value = var_resolver(arg)
37         if isinstance(arg_value, dict):
38             merged_map.update(arg_value)
39         else:
40             return FUNCTION_FAILED  # don't know what this is, blow out
41     return merged_map
42 
43 
44 def concat(original, var_resolver, **_):
45     # https://www.terraform.io/docs/language/functions/concat.html
46     args = split_merge_args(original)
47     if args is None:
48         return FUNCTION_FAILED
49     merged_list = []
50     for arg in args:
51         if arg.startswith("["):
52             value = eval_string(arg)
53             if value is None:
54                 logging.debug("Unable to convert to list: %s", arg)
55                 return FUNCTION_FAILED
56         else:
57             value = var_resolver(arg)
58         if isinstance(value, list):
59             merged_list.extend(value)
60         else:
61             return FUNCTION_FAILED  # don't know what this is, blow out
62     return merged_list
63 
64 
65 def tobool(original: Union[bool, str], **_: Any) -> Union[bool, str]:
66     # https://www.terraform.io/docs/configuration/functions/tobool.html
67     bool_value = convert_str_to_bool(original)
68     return bool_value if isinstance(bool_value, bool) else FUNCTION_FAILED
69 
70 
71 def tonumber(original, **_):
72     # https://www.terraform.io/docs/configuration/functions/tonumber.html
73     if original.startswith('"') and original.endswith('"'):
74         original = original[1:-1]
75     try:
76         if "." in original:
77             return float(original)
78         else:
79             return int(original)
80     except ValueError:
81         return FUNCTION_FAILED
82 
83     
84 def tostring(original, **_):
85     # Indicates a safe string, all good
86     if original.startswith('"') and original.endswith('"'):
87         return original[1:-1]
88     # Otherwise, need to check for valid types (number or bool)
89     bool_value = convert_str_to_bool(original)
90     if isinstance(bool_value, bool):
91         return bool_value
92     else:
93         try:
94             if "." in original:
95                 return str(float(original))
96             else:
97                 return str(int(original))
98         except ValueError:
99             return FUNCTION_FAILED  # no change
100 
101 
102 def tolist(original, **_):
103     # https://www.terraform.io/docs/configuration/functions/tolist.html
104     altered_value = eval_string(original)
105     if altered_value is None:
106         return FUNCTION_FAILED
107     return altered_value if isinstance(altered_value, list) else list(altered_value)
108 
109 
110 def toset(original, **_):
111     # https://www.terraform.io/docs/configuration/functions/toset.html
112     altered_value = eval_string(original)
113     if altered_value is None:
114         return FUNCTION_FAILED
115     return altered_value if isinstance(altered_value, set) else set(altered_value)
116 
117 
118 def tomap(original, **_):
119     # https://www.terraform.io/docs/language/functions/tomap.html
120     original = original.replace(":", "=")     # converted to colons by parser #shrug
121 
122     altered_value = eval_string(original)
123     if altered_value is None or not isinstance(altered_value, dict):
124         return FUNCTION_FAILED
125     return _check_map_type_consistency(altered_value)
126 
127 
128 def map(original, **_):
129     # https://www.terraform.io/docs/language/functions/map.html
130 
131     # NOTE: Splitting by commas is annoying due to possible commas in strings. To avoid
132     #       the issue, act like it's a list (to allow comma separation) and let the HCL
133     #       parser deal with it. Then iterating the list is easy.
134     converted_to_list = eval_string(f"[{original}]")
135     if converted_to_list is None or len(converted_to_list) & 1:       # none or odd number of args
136         return FUNCTION_FAILED
137 
138     return create_map(converted_to_list)
139 
140 
141 def create_map(lst: List):
142     new_map = {}
143     for i in range(0, len(lst), 2):
144         new_map[lst[i]] = lst[i + 1]
145     return _check_map_type_consistency(new_map)
146 
147 
148 def _check_map_type_consistency(value: Dict) -> Dict:
149     # If there is a string and anything else, convert to string
150     had_string = False
151     had_something_else = False
152     for k, v in value.items():
153         if v == "${True}":
154             value[k] = True
155             v = True
156         elif v == "${False}":
157             value[k] = False
158             v = False
159 
160         if isinstance(v, str):
161             had_string = True
162             if had_something_else:
163                 break
164         else:
165             had_something_else = True
166             if had_string:
167                 break
168     if had_string and had_something_else:
169         value = {k: to_string(v) for k, v in value.items()}
170     return value
171 
172 
173 def handle_dynamic_values(conf: Dict[str, List[Any]], has_dynamic_block: bool = False) -> bool:
174     # recursively search for blocks that are dynamic
175     for block_name in conf.keys():
176         conf_block = conf[block_name]
177         if isinstance(conf_block, dict):
178             has_dynamic_block = handle_dynamic_values(conf_block, has_dynamic_block)
179 
180         # if the configuration is a block element, search down again.
181         if conf_block and isinstance(conf_block, list) and isinstance(conf_block[0], dict):
182             has_dynamic_block = handle_dynamic_values(conf_block[0], has_dynamic_block)
183 
184     # if a dynamic block exists somewhere in the resource it will return True
185     return process_dynamic_values(conf) or has_dynamic_block
186 
187 
188 def process_dynamic_values(conf: Dict[str, List[Any]]) -> bool:
189     has_dynamic_block = False
190     for dynamic_element in conf.get("dynamic", {}):
191         if isinstance(dynamic_element, str):
192             try:
193                 dynamic_element = json.loads(dynamic_element)
194             except Exception:
195                 dynamic_element = {}
196 
197         for element_name, element_value in dynamic_element.items():
198             if "content" in element_value:
199                 conf[element_name] = element_value["content"]
200             else:
201                 # this should be the result of a successful dynamic block rendering
202                 conf[element_name] = element_value
203 
204         has_dynamic_block = True
205 
206     return has_dynamic_block
207
```
--- 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()

```

diff --git a/checkov/terraform/parser_functions.py b/checkov/terraform/parser_functions.py
--- a/checkov/terraform/parser_functions.py
+++ b/checkov/terraform/parser_functions.py
@@ -199,7 +199,8 @@
                 conf[element_name] = element_value["content"]
             else:
                 # this should be the result of a successful dynamic block rendering
-                conf[element_name] = element_value
+                # in some cases a whole dict is added, which doesn't have a list around it
+                conf[element_name] = element_value if isinstance(element_value, list) else [element_value]
 
         has_dynamic_block = True

{"golden_diff": "diff --git a/checkov/terraform/parser_functions.py b/checkov/terraform/parser_functions.py\n--- a/checkov/terraform/parser_functions.py\n+++ b/checkov/terraform/parser_functions.py\n@@ -199,7 +199,8 @@\n                 conf[element_name] = element_value[\"content\"]\n             else:\n                 # this should be the result of a successful dynamic block rendering\n-                conf[element_name] = element_value\n+                # in some cases a whole dict is added, which doesn't have a list around it\n+                conf[element_name] = element_value if isinstance(element_value, list) else [element_value]\n \n         has_dynamic_block = True\n", "issue": "Checkov action fails for certain modules due to what seems like datatype issue\nFacing an issue when checkov runs this module `\"terraform-aws-modules/eks/aws\"`. Specifically for `metadata_options` and `remote_access`. Seems like the issue is in the code below where checkov is expecting a list but gets a dictionary so it throws an error, if I remove the indices then it runs ok. Also, it seems that it's linked to the external modules, cause if I run checkov with `--download-external-modules false`, the error is not there.\r\n\r\nThe ERROR:\r\n```\r\nTraceback (most recent call last):\r\n  File \"/usr/local/bin/checkov\", line 9, in <module>\r\n    sys.exit(run())\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/main.py\", line 330, in run\r\n    scan_reports = runner_registry.run(root_folder=root_folder, external_checks_dir=external_checks_dir,\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/common/runners/runner_registry.py\", line 79, in run\r\n    self.runners[0].run(root_folder, external_checks_dir=external_checks_dir, files=files,\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py\", line 147, in run\r\n    self.check_tf_definition(report, root_folder, runner_filter, collect_skip_comments)\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py\", line 292, in check_tf_definition\r\n    self.run_all_blocks(definition, self.context, full_file_path, root_folder, report,\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py\", line 304, in run_all_blocks\r\n    self.run_block(definition[block_type], definitions_context,\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/terraform/runner.py\", line 376, in run_block\r\n    results = registry.scan(scanned_file, entity, skipped_checks, runner_filter)\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/common/checks/base_check_registry.py\", line 1[27](https://github.com/amun/infra-terrafrom/actions/runs/3437973445/jobs/5733435968#step:5:28), in scan\r\n    result = self.run_check(check, entity_configuration, entity_name, entity_type, scanned_file, skip_info)\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/common/checks/base_check_registry.py\", line 141, in run_check\r\n    result = check.run(\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/common/checks/base_check.py\", line 70, in run\r\n    check_result[\"result\"] = self.scan_entity_conf(entity_configuration, entity_type)\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/terraform/checks/resource/base_resource_check.py\", line 43, in scan_entity_conf\r\n    return self.scan_resource_conf(conf)\r\n  File \"/usr/local/lib/python3.8/site-packages/checkov/terraform/checks/resource/aws/IMDSv1Disabled.py\", line [33](https://github.com/amun/infra-terrafrom/actions/runs/3437973445/jobs/5733435968#step:5:34), in scan_resource_conf\r\n    if 'metadata_options' not in conf.keys() or not isinstance(conf['metadata_options'][0], dict):\r\nKeyError: 0\r\n```\r\n\r\nYaml file:\r\n```\r\nname: Checkov\r\non:\r\n  pull_request:\r\n    branches:\r\n      - staging\r\n      - production\r\n      - global\r\n\r\njobs:\r\n  checkov-job:\r\n    strategy:\r\n      matrix:\r\n        module: [aws-config, iam, ecs, eks, flux, lambdas, step-functions, vpc, vpc-peering, ecr]\r\n\r\n    runs-on: 'self-hosted'\r\n    name: checkov\r\n    steps:\r\n      - \r\n        name: Checkout repo\r\n        uses: actions/checkout@master\r\n\r\n      - \r\n        name: Run Checkov\r\n        id: checkov\r\n        uses: bridgecrewio/checkov-action@master\r\n        with:\r\n          directory: modules/${{ matrix.module }}\r\n          quiet: true # optional: display only failed checks\r\n          soft_fail: true # optional: do not return an error code if there are failed checks\r\n          framework: terraform # optional: run only on a specific infrastructure {cloudformation,terraform,kubernetes,all}\r\n          output_format: sarif # optional: the output format, one of: cli, json, junitxml, github_failed_only, or sarif. Default: sarif\r\n          download_external_modules: true # optional: download external terraform modules from public git repositories and terraform registry\r\n          log_level: WARNING # optional: set log level. Default WARNING\r\n\r\n```\r\n\r\n**Functions in checkov source code that might be responsible for the error?:**\r\n\r\nfrom `IMDSv1Disabled.py`:\r\n```\r\ndef scan_resource_conf(self, conf):\r\n        \"\"\"\r\n        Looks for if the metadata service is disabled or requires session tokens:\r\n            https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance#metadata-options\r\n            or\r\n            https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template#metadata-options\r\n\r\n        :param conf: dict of supported resource configuration\r\n        :return: <CheckResult>\r\n        \"\"\"\r\n        if 'metadata_options' not in conf.keys() or not isinstance(conf['metadata_options'][0], dict):\r\n            return CheckResult.FAILED\r\n        metadata_options = conf['metadata_options'][0]\r\n        if ('http_tokens' in metadata_options and metadata_options[\"http_tokens\"] == [\"required\"]) or \\\r\n                ('http_endpoint' in metadata_options and metadata_options[\"http_endpoint\"] == [\"disabled\"]):\r\n            return CheckResult.PASSED\r\n        return CheckResult.FAILED\r\n```\r\nfrom `EKSNodeGroupRemoteAccess.py`:\r\n```\r\ndef scan_resource_conf(self, conf):\r\n        remote_access = conf.get(\"remote_access\")\r\n        if remote_access and remote_access[0] and \"ec2_ssh_key\" in remote_access[0].keys() \\\r\n                and \"source_security_group_ids\" not in remote_access[0].keys():\r\n            return CheckResult.FAILED\r\n        return CheckResult.PASSED\r\n```\r\n\r\nAppreciate your help and let me know if you need me to provide more details.\n", "before_files": [{"content": "import json\nimport logging\nfrom typing import Dict, List, Union, Any\n\nfrom checkov.common.util.type_forcers import convert_str_to_bool\nfrom checkov.common.util.parser_utils import eval_string, split_merge_args, string_to_native, to_string\n\n#\n# Functions defined in this file implement terraform functions.\n#\n# Inputs:\n#   - First arg (unnamed) - the value string provided to the function\n#   - \"var_resolver\" - function pointer to resolve variable/local references and such\n#   - \"function_name\" - name of the function being called (mainly useful for error reporting when a\n#                       function isn't defined)\n# These may be expanded over time, so accepting kwargs (via `**`) is recommended.\n#\n# If the value cannot be processed, `FUNCTION_FAILED` should be returned.\n#\n\nFUNCTION_FAILED = \"____FUNCTION_FAILED____\"\n\n\ndef merge(original, var_resolver, **_):\n    # https://www.terraform.io/docs/language/functions/merge.html\n    args = split_merge_args(original)\n    if args is None:\n        return FUNCTION_FAILED\n    merged_map = {}\n    for arg in args:\n        if arg.startswith(\"{\"):\n            arg_value = string_to_native(arg)\n            if arg_value is None:\n                return FUNCTION_FAILED\n        else:\n            arg_value = var_resolver(arg)\n        if isinstance(arg_value, dict):\n            merged_map.update(arg_value)\n        else:\n            return FUNCTION_FAILED  # don't know what this is, blow out\n    return merged_map\n\n\ndef concat(original, var_resolver, **_):\n    # https://www.terraform.io/docs/language/functions/concat.html\n    args = split_merge_args(original)\n    if args is None:\n        return FUNCTION_FAILED\n    merged_list = []\n    for arg in args:\n        if arg.startswith(\"[\"):\n            value = eval_string(arg)\n            if value is None:\n                logging.debug(\"Unable to convert to list: %s\", arg)\n                return FUNCTION_FAILED\n        else:\n            value = var_resolver(arg)\n        if isinstance(value, list):\n            merged_list.extend(value)\n        else:\n            return FUNCTION_FAILED  # don't know what this is, blow out\n    return merged_list\n\n\ndef tobool(original: Union[bool, str], **_: Any) -> Union[bool, str]:\n    # https://www.terraform.io/docs/configuration/functions/tobool.html\n    bool_value = convert_str_to_bool(original)\n    return bool_value if isinstance(bool_value, bool) else FUNCTION_FAILED\n\n\ndef tonumber(original, **_):\n    # https://www.terraform.io/docs/configuration/functions/tonumber.html\n    if original.startswith('\"') and original.endswith('\"'):\n        original = original[1:-1]\n    try:\n        if \".\" in original:\n            return float(original)\n        else:\n            return int(original)\n    except ValueError:\n        return FUNCTION_FAILED\n\n    \ndef tostring(original, **_):\n    # Indicates a safe string, all good\n    if original.startswith('\"') and original.endswith('\"'):\n        return original[1:-1]\n    # Otherwise, need to check for valid types (number or bool)\n    bool_value = convert_str_to_bool(original)\n    if isinstance(bool_value, bool):\n        return bool_value\n    else:\n        try:\n            if \".\" in original:\n                return str(float(original))\n            else:\n                return str(int(original))\n        except ValueError:\n            return FUNCTION_FAILED  # no change\n\n\ndef tolist(original, **_):\n    # https://www.terraform.io/docs/configuration/functions/tolist.html\n    altered_value = eval_string(original)\n    if altered_value is None:\n        return FUNCTION_FAILED\n    return altered_value if isinstance(altered_value, list) else list(altered_value)\n\n\ndef toset(original, **_):\n    # https://www.terraform.io/docs/configuration/functions/toset.html\n    altered_value = eval_string(original)\n    if altered_value is None:\n        return FUNCTION_FAILED\n    return altered_value if isinstance(altered_value, set) else set(altered_value)\n\n\ndef tomap(original, **_):\n    # https://www.terraform.io/docs/language/functions/tomap.html\n    original = original.replace(\":\", \"=\")     # converted to colons by parser #shrug\n\n    altered_value = eval_string(original)\n    if altered_value is None or not isinstance(altered_value, dict):\n        return FUNCTION_FAILED\n    return _check_map_type_consistency(altered_value)\n\n\ndef map(original, **_):\n    # https://www.terraform.io/docs/language/functions/map.html\n\n    # NOTE: Splitting by commas is annoying due to possible commas in strings. To avoid\n    #       the issue, act like it's a list (to allow comma separation) and let the HCL\n    #       parser deal with it. Then iterating the list is easy.\n    converted_to_list = eval_string(f\"[{original}]\")\n    if converted_to_list is None or len(converted_to_list) & 1:       # none or odd number of args\n        return FUNCTION_FAILED\n\n    return create_map(converted_to_list)\n\n\ndef create_map(lst: List):\n    new_map = {}\n    for i in range(0, len(lst), 2):\n        new_map[lst[i]] = lst[i + 1]\n    return _check_map_type_consistency(new_map)\n\n\ndef _check_map_type_consistency(value: Dict) -> Dict:\n    # If there is a string and anything else, convert to string\n    had_string = False\n    had_something_else = False\n    for k, v in value.items():\n        if v == \"${True}\":\n            value[k] = True\n            v = True\n        elif v == \"${False}\":\n            value[k] = False\n            v = False\n\n        if isinstance(v, str):\n            had_string = True\n            if had_something_else:\n                break\n        else:\n            had_something_else = True\n            if had_string:\n                break\n    if had_string and had_something_else:\n        value = {k: to_string(v) for k, v in value.items()}\n    return value\n\n\ndef handle_dynamic_values(conf: Dict[str, List[Any]], has_dynamic_block: bool = False) -> bool:\n    # recursively search for blocks that are dynamic\n    for block_name in conf.keys():\n        conf_block = conf[block_name]\n        if isinstance(conf_block, dict):\n            has_dynamic_block = handle_dynamic_values(conf_block, has_dynamic_block)\n\n        # if the configuration is a block element, search down again.\n        if conf_block and isinstance(conf_block, list) and isinstance(conf_block[0], dict):\n            has_dynamic_block = handle_dynamic_values(conf_block[0], has_dynamic_block)\n\n    # if a dynamic block exists somewhere in the resource it will return True\n    return process_dynamic_values(conf) or has_dynamic_block\n\n\ndef process_dynamic_values(conf: Dict[str, List[Any]]) -> bool:\n    has_dynamic_block = False\n    for dynamic_element in conf.get(\"dynamic\", {}):\n        if isinstance(dynamic_element, str):\n            try:\n                dynamic_element = json.loads(dynamic_element)\n            except Exception:\n                dynamic_element = {}\n\n        for element_name, element_value in dynamic_element.items():\n            if \"content\" in element_value:\n                conf[element_name] = element_value[\"content\"]\n            else:\n                # this should be the result of a successful dynamic block rendering\n                conf[element_name] = element_value\n\n        has_dynamic_block = True\n\n    return has_dynamic_block\n", "path": "checkov/terraform/parser_functions.py"}], "after_files": [{"content": "import json\nimport logging\nfrom typing import Dict, List, Union, Any\n\nfrom checkov.common.util.type_forcers import convert_str_to_bool\nfrom checkov.common.util.parser_utils import eval_string, split_merge_args, string_to_native, to_string\n\n#\n# Functions defined in this file implement terraform functions.\n#\n# Inputs:\n#   - First arg (unnamed) - the value string provided to the function\n#   - \"var_resolver\" - function pointer to resolve variable/local references and such\n#   - \"function_name\" - name of the function being called (mainly useful for error reporting when a\n#                       function isn't defined)\n# These may be expanded over time, so accepting kwargs (via `**`) is recommended.\n#\n# If the value cannot be processed, `FUNCTION_FAILED` should be returned.\n#\n\nFUNCTION_FAILED = \"____FUNCTION_FAILED____\"\n\n\ndef merge(original, var_resolver, **_):\n    # https://www.terraform.io/docs/language/functions/merge.html\n    args = split_merge_args(original)\n    if args is None:\n        return FUNCTION_FAILED\n    merged_map = {}\n    for arg in args:\n        if arg.startswith(\"{\"):\n            arg_value = string_to_native(arg)\n            if arg_value is None:\n                return FUNCTION_FAILED\n        else:\n            arg_value = var_resolver(arg)\n        if isinstance(arg_value, dict):\n            merged_map.update(arg_value)\n        else:\n            return FUNCTION_FAILED  # don't know what this is, blow out\n    return merged_map\n\n\ndef concat(original, var_resolver, **_):\n    # https://www.terraform.io/docs/language/functions/concat.html\n    args = split_merge_args(original)\n    if args is None:\n        return FUNCTION_FAILED\n    merged_list = []\n    for arg in args:\n        if arg.startswith(\"[\"):\n            value = eval_string(arg)\n            if value is None:\n                logging.debug(\"Unable to convert to list: %s\", arg)\n                return FUNCTION_FAILED\n        else:\n            value = var_resolver(arg)\n        if isinstance(value, list):\n            merged_list.extend(value)\n        else:\n            return FUNCTION_FAILED  # don't know what this is, blow out\n    return merged_list\n\n\ndef tobool(original: Union[bool, str], **_: Any) -> Union[bool, str]:\n    # https://www.terraform.io/docs/configuration/functions/tobool.html\n    bool_value = convert_str_to_bool(original)\n    return bool_value if isinstance(bool_value, bool) else FUNCTION_FAILED\n\n\ndef tonumber(original, **_):\n    # https://www.terraform.io/docs/configuration/functions/tonumber.html\n    if original.startswith('\"') and original.endswith('\"'):\n        original = original[1:-1]\n    try:\n        if \".\" in original:\n            return float(original)\n        else:\n            return int(original)\n    except ValueError:\n        return FUNCTION_FAILED\n\n    \ndef tostring(original, **_):\n    # Indicates a safe string, all good\n    if original.startswith('\"') and original.endswith('\"'):\n        return original[1:-1]\n    # Otherwise, need to check for valid types (number or bool)\n    bool_value = convert_str_to_bool(original)\n    if isinstance(bool_value, bool):\n        return bool_value\n    else:\n        try:\n            if \".\" in original:\n                return str(float(original))\n            else:\n                return str(int(original))\n        except ValueError:\n            return FUNCTION_FAILED  # no change\n\n\ndef tolist(original, **_):\n    # https://www.terraform.io/docs/configuration/functions/tolist.html\n    altered_value = eval_string(original)\n    if altered_value is None:\n        return FUNCTION_FAILED\n    return altered_value if isinstance(altered_value, list) else list(altered_value)\n\n\ndef toset(original, **_):\n    # https://www.terraform.io/docs/configuration/functions/toset.html\n    altered_value = eval_string(original)\n    if altered_value is None:\n        return FUNCTION_FAILED\n    return altered_value if isinstance(altered_value, set) else set(altered_value)\n\n\ndef tomap(original, **_):\n    # https://www.terraform.io/docs/language/functions/tomap.html\n    original = original.replace(\":\", \"=\")     # converted to colons by parser #shrug\n\n    altered_value = eval_string(original)\n    if altered_value is None or not isinstance(altered_value, dict):\n        return FUNCTION_FAILED\n    return _check_map_type_consistency(altered_value)\n\n\ndef map(original, **_):\n    # https://www.terraform.io/docs/language/functions/map.html\n\n    # NOTE: Splitting by commas is annoying due to possible commas in strings. To avoid\n    #       the issue, act like it's a list (to allow comma separation) and let the HCL\n    #       parser deal with it. Then iterating the list is easy.\n    converted_to_list = eval_string(f\"[{original}]\")\n    if converted_to_list is None or len(converted_to_list) & 1:       # none or odd number of args\n        return FUNCTION_FAILED\n\n    return create_map(converted_to_list)\n\n\ndef create_map(lst: List):\n    new_map = {}\n    for i in range(0, len(lst), 2):\n        new_map[lst[i]] = lst[i + 1]\n    return _check_map_type_consistency(new_map)\n\n\ndef _check_map_type_consistency(value: Dict) -> Dict:\n    # If there is a string and anything else, convert to string\n    had_string = False\n    had_something_else = False\n    for k, v in value.items():\n        if v == \"${True}\":\n            value[k] = True\n            v = True\n        elif v == \"${False}\":\n            value[k] = False\n            v = False\n\n        if isinstance(v, str):\n            had_string = True\n            if had_something_else:\n                break\n        else:\n            had_something_else = True\n            if had_string:\n                break\n    if had_string and had_something_else:\n        value = {k: to_string(v) for k, v in value.items()}\n    return value\n\n\ndef handle_dynamic_values(conf: Dict[str, List[Any]], has_dynamic_block: bool = False) -> bool:\n    # recursively search for blocks that are dynamic\n    for block_name in conf.keys():\n        conf_block = conf[block_name]\n        if isinstance(conf_block, dict):\n            has_dynamic_block = handle_dynamic_values(conf_block, has_dynamic_block)\n\n        # if the configuration is a block element, search down again.\n        if conf_block and isinstance(conf_block, list) and isinstance(conf_block[0], dict):\n            has_dynamic_block = handle_dynamic_values(conf_block[0], has_dynamic_block)\n\n    # if a dynamic block exists somewhere in the resource it will return True\n    return process_dynamic_values(conf) or has_dynamic_block\n\n\ndef process_dynamic_values(conf: Dict[str, List[Any]]) -> bool:\n    has_dynamic_block = False\n    for dynamic_element in conf.get(\"dynamic\", {}):\n        if isinstance(dynamic_element, str):\n            try:\n                dynamic_element = json.loads(dynamic_element)\n            except Exception:\n                dynamic_element = {}\n\n        for element_name, element_value in dynamic_element.items():\n            if \"content\" in element_value:\n                conf[element_name] = element_value[\"content\"]\n            else:\n                # this should be the result of a successful dynamic block rendering\n                # in some cases a whole dict is added, which doesn't have a list around it\n                conf[element_name] = element_value if isinstance(element_value, list) else [element_value]\n\n        has_dynamic_block = True\n\n    return has_dynamic_block\n", "path": "checkov/terraform/parser_functions.py"}]}