Repository: bridgecrewio/checkov

Issue:
False positive for CKV_AZURE_43: check storage account name
I'm building my Storage Account names like this
```
name                = "${local.saname_prefix}diagnostics${module.tf-var-project.random_id}
```

With https://github.com/bridgecrewio/checkov/pull/429 merged I now get a Check failure on the SA name:

```
Check: CKV_AZURE_43: "Ensure the Storage Account naming rules"
	FAILED for resource: azurerm_storage_account.diagnostics
	File: /az_diag_sa.tf:8-22

		8  | resource "azurerm_storage_account" "diagnostics" {
		9  |   #checkov:skip=CKV_AZURE_35:Public access is allowed
		10 |   name                = "${local.saname_prefix}diagnostics${module.tf-var-project.random_id}"

````


Discussion:
I am also now getting what I believe are similar false positives for CKV_AZURE_43.

```
Check: CKV_AZURE_43: "Ensure the Storage Account naming rules"
	FAILED for resource: azurerm_storage_account.state
	File: /main.tf:27-68

		27 | resource "azurerm_storage_account" "state" {
		28 |   name                      = local.storage_acct
```

The local is defined with
```
   clean_app           = "${substr(replace(lower("${var.app}"), "/[^\\w]/", ""), 0, 16)}${random_string.suffix.result}"
   storage_acct        = local.clean_app
```

Another is:
```
Check: CKV_AZURE_43: "Ensure the Storage Account naming rules"
	FAILED for resource: azurerm_storage_account.bootdiag
	File: /bootdiag.tf:10-46

		10 | resource "azurerm_storage_account" "bootdiag" {
		11 |   name                      = "diag${random_id.randomId.hex}"
```
I have the same issue that the storage account name is correct with the rule but it still shows the error that it's failed. 
However, when I run the step in CI pipeline, it showed a failed error but the step is still passed like the picture given bellow. Is this a bug or not, can you confirm this? 
![image](https://user-images.githubusercontent.com/36813718/104590270-c322d600-5673-11eb-8824-a3fb14767856.png)
Hi @JoonatanNguyen Are you using the github action for your CI? Can you re-test the "pass vs fail by default" behaviour?

i can replicate this one @metahertz ill show u test code tomox

@JoonatanNguyen  the rule triggers id your name is short than 3 or longer than 20? is your storage account name too long?
> @JoonatanNguyen the rule triggers id your name is short than 3 or longer than 20? is your storage account name too long?

I checked and counted manually quite many times and it in the range of 3 - 20 characters for the storage account name 🤔
yeah im getting an issue, but i thought id at least ask the obvious. i've made those mistakes 👀
Hi, 
I'm also having this issue, my storageaccount is between 3 and 24 characters and terraforms creates it just fine.
However, checkov keeps raising CKV_AZURE_43
hi @fculpo a related PR was merged: https://github.com/bridgecrewio/checkov/pull/1018/files
Can you try with checkov latest version?
Problem still exists with version 1.0.862.
Can confirm the same behavior with the false positive. I have the following variable for name which ensures the same regex validation

```
variable "name" {
  description = "Storage account name"
  type = string
  validation {
    condition     = can(regex("^[a-z0-9]{3,24}$", var.name))
    error_message = "Storage account names must be between 3 and 24 characters in length and may contain numbers and lowercase letters only and must be globally unique."
  }
}
```

however when passing it in as follows, CKV_AZURE_43 is raised
```
resource "azurerm_storage_account" "stg" {
  name                      = var.name
...
}
```

though hardcoding it to something that passes the regex satisfies the condition.
```
resource "azurerm_storage_account" "stg" {
  name                      = "foo"
 ...
 }
 ```

It is likely validating the literal `var.name` instead of a potential variable and as such because `var.name` the literal has a `.` it will fail to satisfy the regex and raise the error. Ideally the check would have to evaluate variables (and determine what do if no default exists) and see if they satisfy the constraint, however that may be a bit much to ask. 
As with @daviddob, I too am experiencing the same issue having used variables to construct the storage account name (23 chars long).  

The accounts are created fine, but the rule check fails.
@schosterbarak Do you have any idea when this one will likely to be fixed? Currently we're raising an exemption for checkov to pass

Resolving patch:
diff --git a/checkov/terraform/checks/resource/azure/StorageAccountName.py b/checkov/terraform/checks/resource/azure/StorageAccountName.py
--- a/checkov/terraform/checks/resource/azure/StorageAccountName.py
+++ b/checkov/terraform/checks/resource/azure/StorageAccountName.py
@@ -1,31 +1,41 @@
+import re
+from typing import List, Dict, Any
+
 from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
 from checkov.common.models.enums import CheckResult, CheckCategories
-import re
-from typing import List
 
-STO_NAME_REGEX = re.compile('^[a-z0-9]{3,24}$')
+STO_NAME_REGEX = re.compile(r"^[a-z0-9]{3,24}$")
+VARIABLE_REFS = ("local.", "module.", "var.")
 
 
 class StorageAccountName(BaseResourceCheck):
-    def __init__(self):
+    def __init__(self) -> None:
         name = "Ensure Storage Accounts adhere to the naming rules"
         id = "CKV_AZURE_43"
-        supported_resources = ['azurerm_storage_account']
+        supported_resources = ["azurerm_storage_account"]
         categories = [CheckCategories.CONVENTION]
-        super().__init__(name=name, id=id, categories=categories,
-                         supported_resources=supported_resources)
+        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
 
-    def scan_resource_conf(self, conf):
+    def scan_resource_conf(self, conf: Dict[str, Any]) -> CheckResult:
         """
             The Storage Account naming reference:
             https://docs.microsoft.com/en-us/azure/storage/common/storage-account-overview#naming-storage-accounts
         :param conf: azurerm_storage_account configuration
         :return: <CheckResult>
         """
-        return CheckResult.PASSED if conf.get('name') and re.findall(STO_NAME_REGEX, str(conf['name'][0])) else CheckResult.FAILED
+        name = conf.get("name")
+        if name:
+            name = name[0]
+            if any(x in name for x in VARIABLE_REFS):
+                # in the case we couldn't evaluate the name, just ignore
+                return CheckResult.UNKNOWN
+            if re.findall(STO_NAME_REGEX, str(conf["name"][0])):
+                return CheckResult.PASSED
+
+        return CheckResult.FAILED
 
     def get_evaluated_keys(self) -> List[str]:
-        return ['name']
+        return ["name"]
 
 
 check = StorageAccountName()


Test patch:
diff --git a/tests/terraform/checks/resource/azure/example_StorageAccountName/main.tf b/tests/terraform/checks/resource/azure/example_StorageAccountName/main.tf
new file mode 100644
--- /dev/null
+++ b/tests/terraform/checks/resource/azure/example_StorageAccountName/main.tf
@@ -0,0 +1,61 @@
+# pass
+
+resource "azurerm_storage_account" "pass" {
+  name                     = "storageaccountname"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
+
+# fail
+
+resource "azurerm_storage_account" "camel_case" {
+  name                     = "thisIsWrong"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
+
+resource "azurerm_storage_account" "kebab_case" {
+  name                     = "this-is-wrong"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
+
+resource "azurerm_storage_account" "too_long" {
+  name                     = "thisiswayyyyyytoooloooong"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
+
+# unknown
+
+resource "azurerm_storage_account" "local" {
+  name                     = "${local.prefix}example"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
+
+resource "azurerm_storage_account" "module" {
+  name                     = "${module.something.prefix}example"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
+
+resource "azurerm_storage_account" "var" {
+  name                     = "${var.prefix}example"
+  resource_group_name      = "azurerm_resource_group.example.name"
+  location                 = "azurerm_resource_group.example.location"
+  account_tier             = "Standard"
+  account_replication_type = "GRS"
+}
diff --git a/tests/terraform/checks/resource/azure/test_StorageAccountName.py b/tests/terraform/checks/resource/azure/test_StorageAccountName.py
--- a/tests/terraform/checks/resource/azure/test_StorageAccountName.py
+++ b/tests/terraform/checks/resource/azure/test_StorageAccountName.py
@@ -1,78 +1,43 @@
 import unittest
+from pathlib import Path
 
-import hcl2
-
+from checkov.runner_filter import RunnerFilter
 from checkov.terraform.checks.resource.azure.StorageAccountName import check
-from checkov.common.models.enums import CheckResult
+from checkov.terraform.runner import Runner
+
 
+class TestStorageAccountName(unittest.TestCase):
+    def test(self):
+        # given
+        test_files_dir = Path(__file__).parent / "example_StorageAccountName"
 
-class TestAzureStorageAccountNamingRule(unittest.TestCase):
+        # when
+        report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id]))
 
-    def test_failure_dash(self):
-        hcl_res = hcl2.loads("""
-            resource "azurerm_storage_account" "example" {
-              name                     = "this-is-wrong"
-              resource_group_name      = data.azurerm_resource_group.example.name
-              location                 = data.azurerm_resource_group.example.location
-              account_tier             = "Standard"
-              account_replication_type = "GRS"
-            }
-                """)
-        resource_conf = hcl_res['resource'][0]['azurerm_storage_account']['example']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.FAILED, scan_result)
+        # then
+        summary = report.get_summary()
 
-    def test_failure_length(self):
-        hcl_res = hcl2.loads("""
-            resource "azurerm_storage_account" "example" {
-              name                     = "thisiswayyyyyytoooloooong"
-              resource_group_name      = data.azurerm_resource_group.example.name
-              location                 = data.azurerm_resource_group.example.location
-              account_tier             = "Standard"
-              account_replication_type = "GRS"
-            }
-                """)
-        resource_conf = hcl_res['resource'][0]['azurerm_storage_account']['example']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.FAILED, scan_result)
+        passing_resources = {
+            "azurerm_storage_account.pass",
+        }
+        failing_resources = {
+            "azurerm_storage_account.camel_case",
+            "azurerm_storage_account.kebab_case",
+            "azurerm_storage_account.too_long",
+        }
 
-    def test_failure_case(self):
-        hcl_res = hcl2.loads("""
-            resource "azurerm_storage_account" "example" {
-              name                     = "thisIsWrong"
-              resource_group_name      = data.azurerm_resource_group.example.name
-              location                 = data.azurerm_resource_group.example.location
-              account_tier             = "Standard"
-              account_replication_type = "GRS"
-            }
-                """)
-        resource_conf = hcl_res['resource'][0]['azurerm_storage_account']['example']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.FAILED, scan_result)
+        passed_check_resources = {c.resource for c in report.passed_checks}
+        failed_check_resources = {c.resource for c in report.failed_checks}
 
-    def test_success(self):
-        hcl_res = hcl2.loads("""
-            resource "azurerm_storage_account" "example" {
-              name                     = "stomyexample"
-              resource_group_name      = data.azurerm_resource_group.example.name
-              location                 = data.azurerm_resource_group.example.location
-              account_tier             = "Standard"
-              account_replication_type = "GRS"
-            }
-                """)
-        resource_conf = hcl_res['resource'][0]['azurerm_storage_account']['example']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.PASSED, scan_result)
+        self.assertEqual(summary["passed"], 1)
+        self.assertEqual(summary["failed"], 3)
+        self.assertEqual(summary["skipped"], 0)
+        self.assertEqual(summary["parsing_errors"], 0)
+        self.assertEqual(summary["resource_count"], 7)  # 3 unknown
 
-    def test_failure_empty_configuration(self):
-        hcl_res = hcl2.loads("""
-            resource "azurerm_storage_account" "example" {
-            }
-                """)
-        resource_conf = hcl_res['resource'][0]['azurerm_storage_account']['example']
-        scan_result = check.scan_resource_conf(conf=resource_conf)
-        self.assertEqual(CheckResult.FAILED, scan_result)
+        self.assertEqual(passing_resources, passed_check_resources)
+        self.assertEqual(failing_resources, failed_check_resources)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()