feat(azure/vm): add new check vm_desired_sku_size (#8191)

Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
This commit is contained in:
Rubén De la Torre Vico
2025-07-25 11:51:01 +02:00
committed by GitHub
parent 285aea3458
commit d9a9236ab7
12 changed files with 820 additions and 2 deletions

View File

@@ -78,7 +78,8 @@ The following list includes all the Azure checks with configurable variables tha
| `app_ensure_python_version_is_latest` | `python_latest_version` | String |
| `app_ensure_java_version_is_latest` | `java_latest_version` | String |
| `sqlserver_recommended_minimal_tls_version` | `recommended_minimal_tls_versions` | List of Strings |
| `defender_attack_path_notifications_properly_configured` | `defender_attack_path_minimal_risk_level` | String |
| `vm_desired_sku_size` | `desired_vm_sku_sizes` | List of Strings |
| `defender_attack_path_notifications_properly_configured` | `defender_attack_path_minimal_risk_level` | String |
## GCP
@@ -481,6 +482,16 @@ azure:
"1.3"
]
# Azure Virtual Machines
# azure.vm_desired_sku_size
# List of desired VM SKU sizes that are allowed in the organization
desired_vm_sku_sizes:
[
"Standard_A8_v2",
"Standard_DS3_v2",
"Standard_D4s_v3",
]
# GCP Configuration
gcp:
# GCP Compute Configuration

View File

@@ -8,6 +8,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `bedrock_api_key_no_administrative_privileges` check for AWS provider [(#8321)](https://github.com/prowler-cloud/prowler/pull/8321)
- Support App Key Content in GitHub provider [(#8271)](https://github.com/prowler-cloud/prowler/pull/8271)
- CIS 4.0 for the Azure provider [(#7782)](https://github.com/prowler-cloud/prowler/pull/7782)
- `vm_desired_sku_size` check for Azure provider [(#8191)](https://github.com/prowler-cloud/prowler/pull/8191)
### Changed
- Handle some AWS errors as warnings instead of errors [(#8347)](https://github.com/prowler-cloud/prowler/pull/8347)

View File

@@ -450,6 +450,16 @@ azure:
"1.3",
]
# Azure Virtual Machines
# azure.vm_desired_sku_size
# List of desired VM SKU sizes that are allowed in the organization
desired_vm_sku_sizes:
[
"Standard_A8_v2",
"Standard_DS3_v2",
"Standard_D4s_v3",
]
# GCP Configuration
gcp:
# GCP Compute Configuration

View File

@@ -0,0 +1,30 @@
{
"Provider": "azure",
"CheckID": "vm_desired_sku_size",
"CheckTitle": "Ensure that your virtual machine instances are using SKU sizes that are approved by your organization",
"CheckType": [],
"ServiceName": "vm",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "Microsoft.Compute/virtualMachines",
"Description": "Ensure that your virtual machine instances are using SKU sizes that are approved by your organization. This check requires configuration of the desired VM SKU sizes in the Prowler configuration file.",
"Risk": "Setting limits for the SKU size(s) of the virtual machine instances provisioned in your Microsoft Azure account can help you to manage better your cloud compute power, address internal compliance requirements and prevent unexpected charges on your Azure monthly bill. Without proper SKU size controls, organizations may face cost overruns and compliance violations.",
"RelatedUrl": "https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/overview",
"Remediation": {
"Code": {
"CLI": "az policy assignment create --display-name 'Allowed VM SKU Sizes' --policy cccc23c7-8427-4f53-ad12-b6a63eb452b3 -p '{\"listOfAllowedSKUs\": {\"value\": [\"<desired-sku-1>\", \"<desired-sku-2>\"]}}' --scope /subscriptions/<subscription-id>",
"NativeIaC": "",
"Other": "",
"Terraform": ""
},
"Recommendation": {
"Text": "1. Define and document your organization's approved VM SKU sizes based on workload requirements, cost constraints, and compliance needs. 2. Implement Azure Policy to enforce VM size restrictions across your subscriptions. 3. Use the 'Allowed virtual machine size SKUs' built-in policy to restrict VM creation to approved sizes. 4. Regularly review and update your approved SKU list based on changing business requirements and cost optimization goals. 5. Monitor VM usage and costs to ensure compliance with your SKU size policies.",
"Url": "https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/resize-vm"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": "This check requires configuration of the desired VM SKU sizes in the Prowler configuration file. Configure the azure.desired_vm_sku_sizes list in your Prowler configuration file (see https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/configuration_file/) with the SKU sizes approved by your organization."
}

View File

@@ -0,0 +1,49 @@
from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.vm.vm_client import vm_client
class vm_desired_sku_size(Check):
"""
Ensure that Azure virtual machines are using SKU sizes that are approved by your organization.
This check evaluates whether each virtual machine's SKU size is included in the organization's approved list of VM sizes.
The approved SKU sizes are configured in the Prowler configuration file under azure.desired_vm_sku_sizes.
- PASS: The VM is using a SKU size that is approved by the organization.
- FAIL: The VM is using a SKU size that is not approved by the organization.
"""
def execute(self) -> list[Check_Report_Azure]:
"""
Execute the check to verify that virtual machines are using desired SKU sizes.
Returns:
A list of check reports for each virtual machine
"""
findings = []
# Get the desired SKU sizes from configuration
DESIRED_SKU_SIZES = vm_client.audit_config.get(
"desired_vm_sku_sizes",
[
"Standard_A8_v2",
"Standard_DS3_v2",
"Standard_D4s_v3",
],
)
for subscription_name, vms in vm_client.virtual_machines.items():
for vm in vms.values():
report = Check_Report_Azure(metadata=self.metadata(), resource=vm)
report.subscription = subscription_name
if vm.vm_size in DESIRED_SKU_SIZES:
report.status = "PASS"
report.status_extended = f"VM {vm.resource_name} is using desired SKU size {vm.vm_size} in subscription {subscription_name}."
else:
report.status = "FAIL"
report.status_extended = f"VM {vm.resource_name} is using {vm.vm_size} which is not a desired SKU size in subscription {subscription_name}."
findings.append(report)
return findings

View File

@@ -75,6 +75,30 @@ class VirtualMachines(AzureService):
)
)
# Convert Azure SDK SecurityProfile to custom SecurityProfile dataclass
azure_security_profile = getattr(vm, "security_profile", None)
security_profile = None
if azure_security_profile:
uefi_settings = None
azure_uefi_settings = getattr(
azure_security_profile, "uefi_settings", None
)
if azure_uefi_settings:
uefi_settings = UefiSettings(
secure_boot_enabled=getattr(
azure_uefi_settings, "secure_boot_enabled", False
),
v_tpm_enabled=getattr(
azure_uefi_settings, "v_tpm_enabled", False
),
)
security_profile = SecurityProfile(
security_type=getattr(
azure_security_profile, "security_type", None
),
uefi_settings=uefi_settings,
)
virtual_machines[subscription_name].update(
{
vm.id: VirtualMachine(
@@ -103,8 +127,13 @@ class VirtualMachines(AzureService):
else None
),
location=vm.location,
security_profile=getattr(vm, "security_profile", None),
security_profile=security_profile,
extensions=extensions,
vm_size=getattr(
getattr(vm, "hardware_profile", None),
"vm_size",
None,
),
image_reference=getattr(
getattr(storage_profile, "image_reference", None),
"id",
@@ -273,6 +302,7 @@ class VirtualMachine(BaseModel):
security_profile: Optional[SecurityProfile]
extensions: list[VirtualMachineExtension]
storage_profile: Optional[StorageProfile] = None
vm_size: Optional[str] = None
image_reference: Optional[str] = None
linux_configuration: Optional[LinuxConfiguration] = None

View File

@@ -321,6 +321,11 @@ config_azure = {
"python_latest_version": "3.12",
"java_latest_version": "17",
"recommended_minimal_tls_versions": ["1.2", "1.3"],
"desired_vm_sku_sizes": [
"Standard_A8_v2",
"Standard_DS3_v2",
"Standard_D4s_v3",
],
"defender_attack_path_minimal_risk_level": "High",
}

View File

@@ -395,6 +395,16 @@ azure:
"1.3"
]
# Azure Virtual Machines
# azure.vm_desired_sku_size
# List of desired VM SKU sizes that are allowed in the organization
desired_vm_sku_sizes:
[
"Standard_A8_v2",
"Standard_DS3_v2",
"Standard_D4s_v3",
]
# GCP Configuration
gcp:
# GCP Compute Configuration

View File

@@ -85,6 +85,11 @@ class TestAzureProvider:
"python_latest_version": "3.12",
"java_latest_version": "17",
"recommended_minimal_tls_versions": ["1.2", "1.3"],
"desired_vm_sku_sizes": [
"Standard_A8_v2",
"Standard_DS3_v2",
"Standard_D4s_v3",
],
"defender_attack_path_minimal_risk_level": "High",
}

View File

@@ -0,0 +1,660 @@
from unittest import mock
from uuid import uuid4
from prowler.providers.azure.services.vm.vm_service import (
SecurityProfile,
StorageProfile,
UefiSettings,
VirtualMachine,
)
from tests.providers.azure.azure_fixtures import (
AZURE_SUBSCRIPTION_ID,
set_mocked_azure_provider,
)
class Test_vm_desired_sku_size:
def test_vm_no_subscriptions(self):
"""Test when there are no subscriptions."""
vm_client = mock.MagicMock
vm_client.virtual_machines = {}
vm_client.audit_config = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 0
def test_vm_subscriptions_empty(self):
"""Test when subscriptions exist but have no VMs."""
vm_client = mock.MagicMock
vm_client.virtual_machines = {AZURE_SUBSCRIPTION_ID: {}}
vm_client.audit_config = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 0
def test_vm_using_desired_sku_size_default_config(self):
"""Test VM using a SKU size that is in the default configuration."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_A8_v2",
),
}
}
vm_client.audit_config = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using desired SKU size Standard_A8_v2 in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_vm_using_desired_sku_size_custom_config(self):
"""Test VM using a SKU size that is in the custom configuration."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_B1s",
),
}
}
vm_client.audit_config = {
"desired_vm_sku_sizes": ["Standard_B1s", "Standard_B2s"]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using desired SKU size Standard_B1s in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_vm_using_non_desired_sku_size_default_config(self):
"""Test VM using a SKU size that is not in the default configuration."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_B1s",
),
}
}
vm_client.audit_config = {}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using Standard_B1s which is not a desired SKU size in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_vm_using_non_desired_sku_size_custom_config(self):
"""Test VM using a SKU size that is not in the custom configuration."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_A8_v2",
),
}
}
vm_client.audit_config = {
"desired_vm_sku_sizes": ["Standard_B1s", "Standard_B2s"]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using Standard_A8_v2 which is not a desired SKU size in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_vm_with_none_vm_size(self):
"""Test VM with None vm_size."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size=None,
),
}
}
vm_client.audit_config = {"desired_vm_sku_sizes": ["Standard_A8_v2"]}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using None which is not a desired SKU size in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_multiple_vms_different_statuses(self):
"""Test multiple VMs with different statuses."""
vm_id_1 = str(uuid4())
vm_id_2 = str(uuid4())
vm_id_3 = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id_1: VirtualMachine(
resource_id=vm_id_1,
resource_name="VMApproved",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_A8_v2",
),
vm_id_2: VirtualMachine(
resource_id=vm_id_2,
resource_name="VMNotApproved",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_B1s",
),
vm_id_3: VirtualMachine(
resource_id=vm_id_3,
resource_name="VMAnotherApproved",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_DS3_v2",
),
}
}
vm_client.audit_config = {
"desired_vm_sku_sizes": ["Standard_A8_v2", "Standard_DS3_v2"]
}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 3
# Find the PASS result
pass_result = next(
r
for r in result
if r.status == "PASS" and r.resource_name == "VMApproved"
)
assert pass_result.subscription == AZURE_SUBSCRIPTION_ID
assert pass_result.resource_id == vm_id_1
assert (
pass_result.status_extended
== f"VM VMApproved is using desired SKU size Standard_A8_v2 in subscription {AZURE_SUBSCRIPTION_ID}."
)
# Find the FAIL result
fail_result = next(
r
for r in result
if r.status == "FAIL" and r.resource_name == "VMNotApproved"
)
assert fail_result.subscription == AZURE_SUBSCRIPTION_ID
assert fail_result.resource_id == vm_id_2
assert (
fail_result.status_extended
== f"VM VMNotApproved is using Standard_B1s which is not a desired SKU size in subscription {AZURE_SUBSCRIPTION_ID}."
)
# Find the second PASS result
pass_result_2 = next(
r
for r in result
if r.status == "PASS" and r.resource_name == "VMAnotherApproved"
)
assert pass_result_2.subscription == AZURE_SUBSCRIPTION_ID
assert pass_result_2.resource_id == vm_id_3
assert (
pass_result_2.status_extended
== f"VM VMAnotherApproved is using desired SKU size Standard_DS3_v2 in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_multiple_subscriptions(self):
"""Test multiple subscriptions with different VMs."""
vm_id_1 = str(uuid4())
vm_id_2 = str(uuid4())
subscription_2 = "subscription-2"
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id_1: VirtualMachine(
resource_id=vm_id_1,
resource_name="VMSub1",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_A8_v2",
),
},
subscription_2: {
vm_id_2: VirtualMachine(
resource_id=vm_id_2,
resource_name="VMSub2",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_B1s",
),
},
}
vm_client.audit_config = {"desired_vm_sku_sizes": ["Standard_A8_v2"]}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 2
# Find the PASS result from subscription 1
pass_result = next(
r
for r in result
if r.status == "PASS" and r.subscription == AZURE_SUBSCRIPTION_ID
)
assert pass_result.resource_name == "VMSub1"
assert pass_result.resource_id == vm_id_1
# Find the FAIL result from subscription 2
fail_result = next(
r
for r in result
if r.status == "FAIL" and r.subscription == subscription_2
)
assert fail_result.resource_name == "VMSub2"
assert fail_result.resource_id == vm_id_2
def test_empty_desired_sku_sizes_config(self):
"""Test when the desired SKU sizes configuration is empty."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="Standard_A8_v2",
),
}
}
vm_client.audit_config = {"desired_vm_sku_sizes": []}
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using Standard_A8_v2 which is not a desired SKU size in subscription {AZURE_SUBSCRIPTION_ID}."
)
def test_case_sensitive_sku_size_matching(self):
"""Test that SKU size matching is case sensitive."""
vm_id = str(uuid4())
vm_client = mock.MagicMock
vm_client.virtual_machines = {
AZURE_SUBSCRIPTION_ID: {
vm_id: VirtualMachine(
resource_id=vm_id,
resource_name="VMTest",
location="location",
security_profile=SecurityProfile(
security_type="TrustedLaunch",
uefi_settings=UefiSettings(
secure_boot_enabled=True,
v_tpm_enabled=True,
),
),
extensions=[],
storage_profile=StorageProfile(
os_disk=None,
data_disks=[],
),
vm_size="standard_a8_v2", # lowercase
),
}
}
vm_client.audit_config = {
"desired_vm_sku_sizes": ["Standard_A8_v2"]
} # proper case
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_azure_provider(),
),
mock.patch(
"prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size.vm_client",
new=vm_client,
),
):
from prowler.providers.azure.services.vm.vm_desired_sku_size.vm_desired_sku_size import (
vm_desired_sku_size,
)
check = vm_desired_sku_size()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL" # Should fail due to case mismatch
assert result[0].subscription == AZURE_SUBSCRIPTION_ID
assert result[0].resource_name == "VMTest"
assert result[0].resource_id == vm_id
assert (
result[0].status_extended
== f"VM VMTest is using standard_a8_v2 which is not a desired SKU size in subscription {AZURE_SUBSCRIPTION_ID}."
)

View File

@@ -41,6 +41,7 @@ def mock_vm_get_virtual_machines(_):
),
data_disks=[],
),
vm_size="Standard_A8_v2",
linux_configuration=None,
)
}
@@ -57,6 +58,7 @@ def mock_vm_get_virtual_machines_with_none(_):
security_profile=None,
extensions=[],
storage_profile=None,
vm_size=None,
linux_configuration=None,
),
"vm_id-2": VirtualMachine(
@@ -69,6 +71,7 @@ def mock_vm_get_virtual_machines_with_none(_):
os_disk=None,
data_disks=[],
),
vm_size="Standard_B1s",
linux_configuration=None,
),
}
@@ -177,6 +180,10 @@ class Test_VirtualMachines_Service:
)
== 0
)
assert (
virtual_machines.virtual_machines[AZURE_SUBSCRIPTION_ID]["vm_id-1"].vm_size
== "Standard_A8_v2"
)
def test__get_disks(self):
disks = VirtualMachines(set_mocked_azure_provider()).disks