Skip to content

Commit 2568933

Browse files
fix(terraform_plan): Run provider checks against all providers in plan (#7061)
fix(terraform): plan scanning only runs provider checks against first provider Co-authored-by: Taylor <[email protected]>
1 parent 9309171 commit 2568933

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

checkov/terraform/plan_parser.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,36 @@ def _is_provider_key(key: str) -> bool:
333333
return (key.startswith('module.') or key.startswith('__') or key in {'start_line', 'end_line'})
334334

335335

336-
def _get_provider(template: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]:
337-
"""Returns the provider dict"""
338-
339-
provider_map: dict[str, dict[str, Any]] = {}
336+
def _get_providers(template: dict[str, dict[str, Any]]) -> list[dict[str, dict[str, Any]]]:
337+
"""Returns a list of provider dicts"""
338+
339+
# `providers` should be a list of dicts, one dict for each provider:
340+
# [
341+
# {
342+
# "aws": {
343+
# "region": ["us-east-1"],
344+
# . . .
345+
# }
346+
# },
347+
# {
348+
# "aws.west": {
349+
# "region": ["us-west-1"],
350+
# "alias": ["west"],
351+
# . . .
352+
# }
353+
# }
354+
# ]
355+
providers: list[dict[str, dict[str, Any]]] = []
340356
provider_config = template.get("configuration", {}).get("provider_config")
341357

342358
if provider_config and isinstance(provider_config, dict):
343359
for provider_key, provider_data in provider_config.items():
344360
if _is_provider_key(key=provider_key):
345361
# Not a provider, skip
346362
continue
347-
provider_map[provider_key] = {}
348363
provider_alias = provider_data.get("alias", "default")
364+
provider_map: dict[str, dict[str, Any]] = {}
365+
provider_map[provider_key] = {}
349366
provider_map_entry = provider_map[provider_key]
350367
for field, value in provider_data.get('expressions', {}).items():
351368
if field in LINE_FIELD_NAMES or not isinstance(value, dict):
@@ -360,9 +377,14 @@ def _get_provider(template: dict[str, dict[str, Any]]) -> dict[str, dict[str, An
360377
provider_map_entry[start_line] = [provider_data.get(START_LINE, 1) - 1]
361378
provider_map_entry[end_line] = [provider_data.get(END_LINE, 1)]
362379
provider_map_entry['alias'] = [provider_alias]
363-
provider_map_entry[TF_PLAN_RESOURCE_ADDRESS] = f"{provider_key}.{provider_alias}"
380+
# provider_key already contains the alias (ie "aws.east") for non-default providers
381+
if provider_alias == "default":
382+
provider_map_entry[TF_PLAN_RESOURCE_ADDRESS] = f"{provider_key}.{provider_alias}"
383+
else:
384+
provider_map_entry[TF_PLAN_RESOURCE_ADDRESS] = provider_key
385+
providers.append(provider_map)
364386

365-
return provider_map
387+
return providers
366388

367389

368390
def _get_resource_changes(template: dict[str, Any]) -> dict[str, dict[str, Any]]:
@@ -421,9 +443,7 @@ def parse_tf_plan(tf_plan_file: str, out_parsing_errors: Dict[str, str]) -> Tupl
421443
if not template:
422444
return None, None
423445

424-
provider = _get_provider(template=template)
425-
if bool(provider):
426-
tf_definition["provider"].append(provider)
446+
tf_definition["provider"] = _get_providers(template=template)
427447

428448
resource_changes = _get_resource_changes(template=template)
429449

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
provider "aws" {
2+
region = "us-east-1"
3+
}
4+
5+
provider "aws" {
6+
region = "us-east-2"
7+
alias = "ohio"
8+
}
9+
provider "aws" {
10+
region = "us-west-2"
11+
alias = "oregon"
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"format_version":"1.2","terraform_version":"1.10.5","planned_values":{"root_module":{}},"configuration":{"provider_config":{"aws":{"name":"aws","full_name":"registry.terraform.io/hashicorp/aws","expressions":{"region":{"constant_value":"us-east-1"}}},"aws.ohio":{"name":"aws","full_name":"registry.terraform.io/hashicorp/aws","alias":"ohio","expressions":{"region":{"constant_value":"us-east-2"}}},"aws.oregon":{"name":"aws","full_name":"registry.terraform.io/hashicorp/aws","alias":"oregon","expressions":{"region":{"constant_value":"us-west-2"}}}},"root_module":{}},"timestamp":"2025-03-19T18:18:14Z","applyable":false,"complete":true,"errored":false}

tests/terraform/parser/test_plan_parser.py

+19
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@ def test_provider_is_included(self):
3030
file_provider_definition = tf_definition['provider']
3131
self.assertTrue(file_provider_definition) # assert a provider exists
3232
assert file_provider_definition[0].get('aws', {}).get('region', None) == ['us-west-2']
33+
34+
def test_plan_multiple_providers(self):
35+
current_dir = os.path.dirname(os.path.realpath(__file__))
36+
valid_plan_path = current_dir + "/resources/plan_multiple_providers/tfplan.json"
37+
tf_definition, _ = parse_tf_plan(valid_plan_path, {})
38+
providers = tf_definition['provider']
39+
self.assertEqual( len(providers), 3)
40+
provider_keys = []
41+
provider_aliases = []
42+
provider_addresses = []
43+
for provider in providers:
44+
key = next(iter(provider))
45+
provider_keys.append(key)
46+
provider_aliases.append( provider[key]['alias'][0] )
47+
provider_addresses.append( provider[key]['__address__'] )
48+
49+
self.assertEqual(provider_keys, ["aws", "aws.ohio", "aws.oregon"])
50+
self.assertEqual(provider_aliases, ["default", "ohio", "oregon"])
51+
self.assertEqual(provider_addresses, ["aws.default", "aws.ohio", "aws.oregon"])
3352

3453
def test_more_tags_values_are_flattened(self):
3554
current_dir = os.path.dirname(os.path.realpath(__file__))

0 commit comments

Comments
 (0)