diff --git a/checkov/arm/checks/resource/VMStorageOsDisk.py b/checkov/arm/checks/resource/VMStorageOsDisk.py new file mode 100644 index 00000000000..50a4a1d5d84 --- /dev/null +++ b/checkov/arm/checks/resource/VMStorageOsDisk.py @@ -0,0 +1,36 @@ +from typing import Any, Dict + +from checkov.common.models.enums import CheckResult, CheckCategories +from checkov.arm.base_resource_value_check import BaseResourceCheck + + +class VMStorageOsDisk(BaseResourceCheck): + def __init__(self) -> None: + name = "Ensure that Virtual Machines use managed disks" + id = "CKV_AZURE_92" + supported_resources = ("Microsoft.Compute/virtualMachines",) + categories = (CheckCategories.GENERAL_SECURITY,) + super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources) + + def scan_resource_conf(self, conf: Dict[str, Any]) -> CheckResult: + properties = conf.get('properties') + if not properties or not isinstance(properties, dict): + return CheckResult.PASSED + storage_profile = properties.get('storageProfile') + if not storage_profile or not isinstance(storage_profile, dict): + return CheckResult.PASSED + os_disk = storage_profile.get('osDisk') + data_disks = list(storage_profile.get('dataDisks', [])) + if os_disk and isinstance(os_disk, dict) and "vhd" in os_disk: + self.evaluated_keys = ['os_disk'] + return CheckResult.FAILED + if data_disks and any(isinstance(data_disk, dict) and "vhd" in data_disk for data_disk in data_disks): + self.evaluated_keys = ['data_disks'] + return CheckResult.FAILED + self.evaluated_keys = ['os_disk'] if os_disk else [] + if data_disks: + self.evaluated_keys.append('data_disks') + return CheckResult.PASSED + + +check = VMStorageOsDisk() diff --git a/tests/arm/checks/resource/example_VMStorageOsDisk/fail.json b/tests/arm/checks/resource/example_VMStorageOsDisk/fail.json new file mode 100644 index 00000000000..dc778757dfa --- /dev/null +++ b/tests/arm/checks/resource/example_VMStorageOsDisk/fail.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-07-01", + "name": "fail-linux", + "location": "[resourceGroup().location]", + "properties": { + "hardwareProfile": { + "vmSize": "Standard_DS1_v2" + }, + "storageProfile": { + "imageReference": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "16.04-LTS", + "version": "latest" + }, + "osDisk": { + "name": "myosdisk3", + "caching": "ReadWrite", + "createOption": "FromImage", + "managedDisk": { + "storageAccountType": "Standard_LRS" + }, + "vhd": { + "uri": "someURI" + } + } + }, + "osProfile": { + "computerName": "hostname", + "adminUsername": "testadmin", + "adminPassword": "1111" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', 'main')]" + } + ] + } + }, + "tags": { + "environment": "staging" + } + } + ], + "parameters": { + "prefix": { + "type": "string", + "defaultValue": "myprefix" + } + } +} diff --git a/tests/arm/checks/resource/example_VMStorageOsDisk/fail2.json b/tests/arm/checks/resource/example_VMStorageOsDisk/fail2.json new file mode 100644 index 00000000000..bce2a3dd8e7 --- /dev/null +++ b/tests/arm/checks/resource/example_VMStorageOsDisk/fail2.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-07-01", + "name": "fail-windows", + "location": "[resourceGroup().location]", + "properties": { + "hardwareProfile": { + "vmSize": "Standard_DS1_v2" + }, + "storageProfile": { + "imageReference": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + }, + "dataDisks": [ + { + "name": "mydatadisk1", + "caching": "ReadWrite", + "createOption": "Empty", + "managedDisk": { + "storageAccountType": "Standard_LRS" + }, + "vhd": { + "uri": "someURI" + } + } + ] + }, + "osProfile": { + "computerName": "hostname", + "adminUsername": "testadmin", + "adminPassword": "1111" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', 'main')]" + } + ] + } + }, + "tags": { + "environment": "staging" + } + } + ], + "parameters": { + "prefix": { + "type": "string", + "defaultValue": "myprefix" + } + } +} diff --git a/tests/arm/checks/resource/example_VMStorageOsDisk/pass.json b/tests/arm/checks/resource/example_VMStorageOsDisk/pass.json new file mode 100644 index 00000000000..5f2003adf39 --- /dev/null +++ b/tests/arm/checks/resource/example_VMStorageOsDisk/pass.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-07-01", + "name": "pass-linux", + "location": "[resourceGroup().location]", + "properties": { + "hardwareProfile": { + "vmSize": "Standard_DS1_v2" + }, + "storageProfile": { + "imageReference": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "16.04-LTS", + "version": "latest" + }, + "osDisk": { + "name": "myosdisk1", + "caching": "ReadWrite", + "createOption": "FromImage", + "managedDisk": { + "storageAccountType": "Standard_LRS" + } + } + }, + "osProfile": { + "computerName": "hostname", + "adminUsername": "testadmin", + "adminPassword": "1111" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', 'main')]" + } + ] + } + }, + "tags": { + "environment": "staging" + } + } + ], + "parameters": { + "prefix": { + "type": "string", + "defaultValue": "myprefix" + } + } +} diff --git a/tests/arm/checks/resource/example_VMStorageOsDisk/pass2.json b/tests/arm/checks/resource/example_VMStorageOsDisk/pass2.json new file mode 100644 index 00000000000..230334fb43f --- /dev/null +++ b/tests/arm/checks/resource/example_VMStorageOsDisk/pass2.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-07-01", + "name": "pass-windows", + "location": "[resourceGroup().location]", + "properties": { + "hardwareProfile": { + "vmSize": "Standard_DS1_v2" + }, + "storageProfile": { + "imageReference": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + }, + "osDisk": { + "name": "myosdisk2", + "caching": "ReadWrite", + "createOption": "FromImage", + "managedDisk": { + "storageAccountType": "Standard_LRS" + } + } + }, + "osProfile": { + "computerName": "hostname", + "adminUsername": "testadmin", + "adminPassword": "1111" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', 'main')]" + } + ] + } + }, + "tags": { + "environment": "staging" + } + } + ], + "parameters": { + "prefix": { + "type": "string", + "defaultValue": "myprefix" + } + } +} diff --git a/tests/arm/checks/resource/test_VMStorageOsDisk.py b/tests/arm/checks/resource/test_VMStorageOsDisk.py new file mode 100644 index 00000000000..ed5a6736371 --- /dev/null +++ b/tests/arm/checks/resource/test_VMStorageOsDisk.py @@ -0,0 +1,44 @@ +import unittest +from pathlib import Path + +from checkov.runner_filter import RunnerFilter +from checkov.arm.checks.resource.VMStorageOsDisk import check +from checkov.arm.runner import Runner + + +class TestVMStorageOsDisk(unittest.TestCase): + + def test(self): + # given + test_files_dir = Path(__file__).parent / "example_VMStorageOsDisk" + + # when + report = Runner().run(root_folder=str(test_files_dir), runner_filter=RunnerFilter(checks=[check.id])) + + # then + summary = report.get_summary() + + passing_resources = { + "Microsoft.Compute/virtualMachines.pass-linux", + "Microsoft.Compute/virtualMachines.pass-windows", + } + failing_resources = { + "Microsoft.Compute/virtualMachines.fail-linux", + "Microsoft.Compute/virtualMachines.fail-windows", + } + + passed_check_resources = {c.resource for c in report.passed_checks} + failed_check_resources = {c.resource for c in report.failed_checks} + + self.assertEqual(summary["passed"], 2) + self.assertEqual(summary["failed"], 2) + self.assertEqual(summary["skipped"], 0) + self.assertEqual(summary["parsing_errors"], 0) + self.assertEqual(summary["resource_count"], 4) # 3 unknown + + self.assertEqual(passing_resources, passed_check_resources) + self.assertEqual(failing_resources, failed_check_resources) + + +if __name__ == '__main__': + unittest.main()