From 6491bce5a697f77f32f91d4c291db5cc84cc0c75 Mon Sep 17 00:00:00 2001 From: Prowler Bot Date: Wed, 26 Feb 2025 10:49:33 +0100 Subject: [PATCH] fix(azure): migrate resource models to avoid using SDK defaults (#7043) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rubén De la Torre Vico --- .../azure/services/aks/aks_service.py | 23 ++- .../azure/services/app/app_service.py | 75 ++++++++- .../containerregistry_service.py | 29 +++- .../services/cosmosdb/cosmosdb_service.py | 24 ++- .../azure/services/entra/entra_service.py | 8 +- .../azure/services/iam/iam_service.py | 24 ++- ...keyvault_key_expiration_set_in_non_rbac.py | 6 +- ...keyvault_non_rbac_secret_expiration_set.py | 6 +- .../keyvault_rbac_key_expiration_set.py | 6 +- .../keyvault_rbac_secret_expiration_set.py | 6 +- .../services/keyvault/keyvault_service.py | 107 ++++++++++-- .../azure/services/monitor/monitor_service.py | 43 ++++- .../azure/services/network/network_service.py | 53 +++++- .../services/sqlserver/sqlserver_service.py | 159 +++++++++++++++--- .../azure/services/storage/storage_service.py | 65 +++++-- .../providers/azure/services/vm/vm_service.py | 54 +++++- ...tainerregistry_admin_user_disabled_test.py | 2 - ...erregistry_not_publicly_accessible_test.py | 4 - .../containerregistry_service_test.py | 1 - ...ontainerregistry_uses_private_link_test.py | 2 - ...unt_firewall_use_selected_networks_test.py | 2 + .../cosmosdb/cosmosdb_service_test.py | 1 + .../vm_ensure_using_managed_disks_test.py | 3 + .../azure/services/vm/vm_service_test.py | 1 + .../vm_trusted_launch_enabled_test.py | 62 ++++--- 25 files changed, 624 insertions(+), 142 deletions(-) diff --git a/prowler/providers/azure/services/aks/aks_service.py b/prowler/providers/azure/services/aks/aks_service.py index beb35c3842..4c269fbf28 100644 --- a/prowler/providers/azure/services/aks/aks_service.py +++ b/prowler/providers/azure/services/aks/aks_service.py @@ -2,7 +2,6 @@ from typing import List from azure.mgmt.containerservice import ContainerServiceClient -from azure.mgmt.containerservice.models import ManagedClusterAgentPoolProfile from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -42,9 +41,19 @@ def _get_clusters(self): if getattr(cluster, "network_profile", None) else None ), - agent_pool_profiles=getattr( - cluster, "agent_pool_profiles", [] - ), + agent_pool_profiles=[ + ManagedClusterAgentPoolProfile( + name=agent_pool_profile.name, + enable_node_public_ip=getattr( + agent_pool_profile, + "enable_node_public_ip", + False, + ), + ) + for agent_pool_profile in getattr( + cluster, "agent_pool_profiles", [] + ) + ], rbac_enabled=getattr(cluster, "enable_rbac", False), ) } @@ -57,6 +66,12 @@ def _get_clusters(self): return clusters +@dataclass +class ManagedClusterAgentPoolProfile: + name: str + enable_node_public_ip: bool + + @dataclass class Cluster: id: str diff --git a/prowler/providers/azure/services/app/app_service.py b/prowler/providers/azure/services/app/app_service.py index 68cb927d0d..fc2ee5cf13 100644 --- a/prowler/providers/azure/services/app/app_service.py +++ b/prowler/providers/azure/services/app/app_service.py @@ -1,8 +1,7 @@ -from dataclasses import dataclass -from typing import Dict +from dataclasses import dataclass, field +from typing import Dict, List from azure.mgmt.web import WebSiteManagementClient -from azure.mgmt.web.models import ManagedServiceIdentity, SiteConfigResource from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -37,6 +36,11 @@ def _get_apps(self): None, ) + # Get app configurations + app_configurations = client.web_apps.get_configuration( + resource_group_name=app.resource_group, name=app.name + ) + apps[subscription_name].update( { app.id: WebApp( @@ -47,9 +51,30 @@ def _get_apps(self): if platform_auth else False ), - configurations=client.web_apps.get_configuration( - resource_group_name=app.resource_group, - name=app.name, + configurations=SiteConfigResource( + id=app_configurations.id, + name=app_configurations.name, + linux_fx_version=getattr( + app_configurations, "linux_fx_version", "" + ), + java_version=getattr( + app_configurations, "java_version", "" + ), + php_version=getattr( + app_configurations, "php_version", "" + ), + python_version=getattr( + app_configurations, "python_version", "" + ), + http20_enabled=getattr( + app_configurations, "http20_enabled", False + ), + ftps_state=getattr( + app_configurations, "ftps_state", "" + ), + min_tls_version=getattr( + app_configurations, "min_tls_version", "" + ), ), client_cert_mode=self._get_client_cert_mode( getattr(app, "client_cert_enabled", False), @@ -59,7 +84,21 @@ def _get_apps(self): app.name, app.resource_group, subscription_name ), https_only=getattr(app, "https_only", False), - identity=getattr(app, "identity", None), + identity=ManagedServiceIdentity( + principal_id=getattr( + getattr(app, "identity", {}), + "principal_id", + "", + ), + tenant_id=getattr( + getattr(app, "identity", {}), + "tenant_id", + "", + ), + type=getattr( + getattr(app, "identity", {}), "type", "" + ), + ), location=app.location, kind=app.kind, ) @@ -171,6 +210,26 @@ def _get_app_monitor_settings(self, app_name, resource_group, subscription): return monitor_diagnostics_settings +@dataclass +class ManagedServiceIdentity: + principal_id: str + tenant_id: str + type: str + + +@dataclass +class SiteConfigResource: + id: str + name: str + linux_fx_version: str + java_version: str + php_version: str + python_version: str + http20_enabled: bool + ftps_state: str + min_tls_version: str + + @dataclass class WebApp: resource_id: str @@ -181,7 +240,7 @@ class WebApp: client_cert_mode: str = "Ignore" auth_enabled: bool = False https_only: bool = False - monitor_diagnostic_settings: list[DiagnosticSetting] = None + monitor_diagnostic_settings: List[DiagnosticSetting] = field(default_factory=list) kind: str = "app" diff --git a/prowler/providers/azure/services/containerregistry/containerregistry_service.py b/prowler/providers/azure/services/containerregistry/containerregistry_service.py index 189c89c642..287d18203f 100644 --- a/prowler/providers/azure/services/containerregistry/containerregistry_service.py +++ b/prowler/providers/azure/services/containerregistry/containerregistry_service.py @@ -1,10 +1,6 @@ from dataclasses import dataclass from azure.mgmt.containerregistry import ContainerRegistryManagementClient -from azure.mgmt.containerregistry.models import ( - NetworkRuleSet, - PrivateEndpointConnection, -) from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -40,7 +36,9 @@ def _get_container_registries(self): public_network_access=( False if getattr( - registry, "public_network_access" "Enabled" + registry, + "public_network_access_enabled", + "Enabled", ) == "Disabled" else True @@ -54,9 +52,16 @@ def _get_container_registries(self): monitor_diagnostic_settings=self._get_registry_monitor_settings( registry.name, resource_group, subscription ), - private_endpoint_connections=getattr( - registry, "private_endpoint_connections", [] - ), + private_endpoint_connections=[ + PrivateEndpointConnection( + id=pec.id, + name=pec.name, + type=pec.type, + ) + for pec in getattr( + registry, "private_endpoint_connections", [] + ) + ], ) }, ) @@ -90,6 +95,13 @@ def _get_registry_monitor_settings( return monitor_diagnostics_settings +@dataclass +class PrivateEndpointConnection: + id: str + name: str + type: str + + @dataclass class ContainerRegistryInfo: id: str @@ -100,6 +112,5 @@ class ContainerRegistryInfo: login_server: str public_network_access: bool admin_user_enabled: bool - network_rule_set: NetworkRuleSet monitor_diagnostic_settings: list[DiagnosticSetting] private_endpoint_connections: list[PrivateEndpointConnection] diff --git a/prowler/providers/azure/services/cosmosdb/cosmosdb_service.py b/prowler/providers/azure/services/cosmosdb/cosmosdb_service.py index bd5eb5a81b..8d21fad43a 100644 --- a/prowler/providers/azure/services/cosmosdb/cosmosdb_service.py +++ b/prowler/providers/azure/services/cosmosdb/cosmosdb_service.py @@ -1,7 +1,7 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass +from typing import List from azure.mgmt.cosmosdb import CosmosDBManagementClient -from azure.mgmt.cosmosdb.models import PrivateEndpointConnection from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -30,7 +30,14 @@ def _get_accounts(self): type=account.type, tags=account.tags, is_virtual_network_filter_enabled=account.is_virtual_network_filter_enabled, - private_endpoint_connections=account.private_endpoint_connections, + private_endpoint_connections=[ + PrivateEndpointConnection( + id=private_endpoint_connection.id, + name=private_endpoint_connection.name, + type=private_endpoint_connection.type, + ) + for private_endpoint_connection in account.private_endpoint_connections + ], disable_local_auth=account.disable_local_auth, ) ) @@ -41,6 +48,13 @@ def _get_accounts(self): return accounts +@dataclass +class PrivateEndpointConnection: + id: str + name: str + type: str + + @dataclass class Account: id: str @@ -50,7 +64,5 @@ class Account: tags: dict is_virtual_network_filter_enabled: bool location: str - private_endpoint_connections: list[PrivateEndpointConnection] = field( - default_factory=list - ) + private_endpoint_connections: List[PrivateEndpointConnection] disable_local_auth: bool = False diff --git a/prowler/providers/azure/services/entra/entra_service.py b/prowler/providers/azure/services/entra/entra_service.py index f2b02dcc5d..45f0c0128c 100644 --- a/prowler/providers/azure/services/entra/entra_service.py +++ b/prowler/providers/azure/services/entra/entra_service.py @@ -48,7 +48,7 @@ async def _get_users(self): for user in users_list.value: users[tenant].update( { - user.user_principal_name: User( + user.id: User( id=user.id, name=user.display_name, authentication_methods=[ @@ -261,11 +261,9 @@ async def _get_directory_roles(self): directory_role.display_name: DirectoryRole( id=directory_role.id, members=[ - self.users[tenant][member.user_principal_name] + self.users[tenant][member.id] for member in directory_role_members.value - if self.users[tenant].get( - member.user_principal_name, None - ) + if self.users[tenant].get(member.id, None) ], ) } diff --git a/prowler/providers/azure/services/iam/iam_service.py b/prowler/providers/azure/services/iam/iam_service.py index 30662295db..657921eb4f 100644 --- a/prowler/providers/azure/services/iam/iam_service.py +++ b/prowler/providers/azure/services/iam/iam_service.py @@ -1,7 +1,7 @@ from dataclasses import dataclass +from typing import List from azure.mgmt.authorization import AuthorizationManagementClient -from azure.mgmt.authorization.v2022_04_01.models import Permission from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -33,7 +33,16 @@ def _get_roles(self): name=role.role_name, type=role.role_type, assignable_scopes=role.assignable_scopes, - permissions=role.permissions, + permissions=[ + Permission( + condition=getattr(permission, "condition", ""), + condition_version=getattr( + permission, "condition_version", "" + ), + actions=getattr(permission, "actions", []), + ) + for permission in getattr(role, "permissions", []) + ], ) ) else: @@ -82,13 +91,20 @@ def _get_role_assignments(self): return role_assignments +@dataclass +class Permission: + actions: List[str] + condition: str + condition_version: str + + @dataclass class Role: id: str name: str type: str - assignable_scopes: list[str] - permissions: list[Permission] + assignable_scopes: List[str] + permissions: List[Permission] @dataclass diff --git a/prowler/providers/azure/services/keyvault/keyvault_key_expiration_set_in_non_rbac/keyvault_key_expiration_set_in_non_rbac.py b/prowler/providers/azure/services/keyvault/keyvault_key_expiration_set_in_non_rbac/keyvault_key_expiration_set_in_non_rbac.py index 783f4e28ef..debca1ac34 100644 --- a/prowler/providers/azure/services/keyvault/keyvault_key_expiration_set_in_non_rbac/keyvault_key_expiration_set_in_non_rbac.py +++ b/prowler/providers/azure/services/keyvault/keyvault_key_expiration_set_in_non_rbac/keyvault_key_expiration_set_in_non_rbac.py @@ -16,11 +16,7 @@ def execute(self) -> Check_Report_Azure: report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has all the keys with expiration date set." has_key_without_expiration = False for key in keyvault.keys: - if ( - key.attributes - and not key.attributes.expires - and key.enabled - ): + if not key.attributes.expires and key.enabled: report.status = "FAIL" report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has the key {key.name} without expiration date set." has_key_without_expiration = True diff --git a/prowler/providers/azure/services/keyvault/keyvault_non_rbac_secret_expiration_set/keyvault_non_rbac_secret_expiration_set.py b/prowler/providers/azure/services/keyvault/keyvault_non_rbac_secret_expiration_set/keyvault_non_rbac_secret_expiration_set.py index 766a797732..9afe72fb21 100644 --- a/prowler/providers/azure/services/keyvault/keyvault_non_rbac_secret_expiration_set/keyvault_non_rbac_secret_expiration_set.py +++ b/prowler/providers/azure/services/keyvault/keyvault_non_rbac_secret_expiration_set/keyvault_non_rbac_secret_expiration_set.py @@ -19,11 +19,7 @@ def execute(self) -> Check_Report_Azure: report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has all the secrets with expiration date set." has_secret_without_expiration = False for secret in keyvault.secrets: - if ( - secret.attributes - and not secret.attributes.expires - and secret.enabled - ): + if not secret.attributes.expires and secret.enabled: report.status = "FAIL" report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has the secret {secret.name} without expiration date set." has_secret_without_expiration = True diff --git a/prowler/providers/azure/services/keyvault/keyvault_rbac_key_expiration_set/keyvault_rbac_key_expiration_set.py b/prowler/providers/azure/services/keyvault/keyvault_rbac_key_expiration_set/keyvault_rbac_key_expiration_set.py index f706676498..1ca90c445b 100644 --- a/prowler/providers/azure/services/keyvault/keyvault_rbac_key_expiration_set/keyvault_rbac_key_expiration_set.py +++ b/prowler/providers/azure/services/keyvault/keyvault_rbac_key_expiration_set/keyvault_rbac_key_expiration_set.py @@ -16,11 +16,7 @@ def execute(self) -> Check_Report_Azure: report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has all the keys with expiration date set." has_key_without_expiration = False for key in keyvault.keys: - if ( - key.attributes - and not key.attributes.expires - and key.enabled - ): + if not key.attributes.expires and key.enabled: report.status = "FAIL" report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has the key {key.name} without expiration date set." has_key_without_expiration = True diff --git a/prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py b/prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py index f2cc00bd47..0a3648b907 100644 --- a/prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py +++ b/prowler/providers/azure/services/keyvault/keyvault_rbac_secret_expiration_set/keyvault_rbac_secret_expiration_set.py @@ -16,11 +16,7 @@ def execute(self) -> Check_Report_Azure: report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has all the secrets with expiration date set." has_secret_without_expiration = False for secret in keyvault.secrets: - if ( - secret.attributes - and not secret.attributes.expires - and secret.enabled - ): + if not secret.attributes.expires and secret.enabled: report.status = "FAIL" report.status_extended = f"Keyvault {keyvault.name} from subscription {subscription} has the secret {secret.name} without expiration date set." has_secret_without_expiration = True diff --git a/prowler/providers/azure/services/keyvault/keyvault_service.py b/prowler/providers/azure/services/keyvault/keyvault_service.py index d030d655a8..3a2cc1bf69 100644 --- a/prowler/providers/azure/services/keyvault/keyvault_service.py +++ b/prowler/providers/azure/services/keyvault/keyvault_service.py @@ -1,13 +1,10 @@ from dataclasses import dataclass +from datetime import datetime +from typing import List, Optional, Union from azure.core.exceptions import HttpResponseError from azure.keyvault.keys import KeyClient from azure.mgmt.keyvault import KeyVaultManagementClient -from azure.mgmt.keyvault.v2023_07_01.models import ( - KeyAttributes, - SecretAttributes, - VaultProperties, -) from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -47,7 +44,30 @@ def _get_key_vaults(self, provider): name=getattr(keyvault, "name", ""), location=getattr(keyvault, "location", ""), resource_group=resource_group, - properties=keyvault_properties, + properties=VaultProperties( + tenant_id=getattr(keyvault_properties, "tenant_id", ""), + enable_rbac_authorization=getattr( + keyvault_properties, + "enable_rbac_authorization", + False, + ), + private_endpoint_connections=[ + PrivateEndpointConnection(id=conn.id) + for conn in getattr( + keyvault_properties, + "private_endpoint_connections", + [], + ) + ], + enable_soft_delete=getattr( + keyvault_properties, "enable_soft_delete", False + ), + enable_purge_protection=getattr( + keyvault_properties, + "enable_purge_protection", + False, + ), + ), keys=keys, secrets=secrets, monitor_diagnostic_settings=self._get_vault_monitor_settings( @@ -74,7 +94,12 @@ def _get_keys(self, subscription, resource_group, keyvault_name, provider): name=getattr(key, "name", ""), enabled=getattr(key.attributes, "enabled", False), location=getattr(key, "location", ""), - attributes=getattr(key, "attributes", None), + attributes=KeyAttributes( + enabled=getattr(key.attributes, "enabled", False), + created=getattr(key.attributes, "created", 0), + updated=getattr(key.attributes, "updated", 0), + expires=getattr(key.attributes, "expires", 0), + ), ) ) except Exception as error: @@ -93,11 +118,17 @@ def _get_keys(self, subscription, resource_group, keyvault_name, provider): policy = key_client.get_key_rotation_policy(prop.name) for key in keys: if key.name == prop.name: - key.rotation_policy = policy + key.rotation_policy = KeyRotationPolicy( + id=getattr(policy, "id", ""), + lifetime_actions=[ + KeyRotationLifetimeAction(action=action.action) + for action in getattr(policy, "lifetime_actions", []) + ], + ) # TODO: handle different errors here since we are catching all HTTP Errors here except HttpResponseError: - logger.error( + logger.warning( f"Subscription name: {subscription} -- has no access policy configured for keyvault {keyvault_name}" ) return keys @@ -115,7 +146,20 @@ def _get_secrets(self, subscription, resource_group, keyvault_name): name=getattr(secret, "name", ""), enabled=getattr(secret.properties.attributes, "enabled", False), location=getattr(secret, "location", ""), - attributes=getattr(secret.properties, "attributes", None), + attributes=SecretAttributes( + enabled=getattr( + secret.properties.attributes, "enabled", False + ), + created=getattr( + secret.properties.attributes, "created", None + ), + updated=getattr( + secret.properties.attributes, "updated", None + ), + expires=getattr( + secret.properties.attributes, "expires", None + ), + ), ) ) except Exception as error: @@ -142,6 +186,25 @@ def _get_vault_monitor_settings(self, keyvault_name, resource_group, subscriptio return monitor_diagnostics_settings +@dataclass +class KeyAttributes: + enabled: bool + created: int + updated: int + expires: int + + +@dataclass +class KeyRotationLifetimeAction: + action: str + + +@dataclass +class KeyRotationPolicy: + id: str + lifetime_actions: list[KeyRotationLifetimeAction] + + @dataclass class Key: id: str @@ -149,7 +212,15 @@ class Key: enabled: bool location: str attributes: KeyAttributes - rotation_policy: str = None + rotation_policy: Optional[KeyRotationPolicy] = None + + +@dataclass +class SecretAttributes: + enabled: bool + created: Union[datetime, None] + updated: Union[datetime, None] + expires: Union[datetime, None] @dataclass @@ -161,6 +232,20 @@ class Secret: attributes: SecretAttributes +@dataclass +class PrivateEndpointConnection: + id: str + + +@dataclass +class VaultProperties: + tenant_id: str + enable_rbac_authorization: bool + private_endpoint_connections: List[PrivateEndpointConnection] + enable_soft_delete: bool + enable_purge_protection: bool + + @dataclass class KeyVaultInfo: id: str diff --git a/prowler/providers/azure/services/monitor/monitor_service.py b/prowler/providers/azure/services/monitor/monitor_service.py index abaca305f0..c0239e467b 100644 --- a/prowler/providers/azure/services/monitor/monitor_service.py +++ b/prowler/providers/azure/services/monitor/monitor_service.py @@ -1,7 +1,7 @@ from dataclasses import dataclass +from typing import List from azure.mgmt.monitor import MonitorManagementClient -from azure.mgmt.monitor.models import AlertRuleAllOfCondition, LogSettings from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -47,7 +47,14 @@ def diagnostic_settings_with_uri(self, subscription, uri, client): if getattr(setting, "storage_account_id", None) else None ), - logs=setting.logs, + logs=[ + LogSettings( + category=log_settings.category, + category_group=log_settings.category_group, + enabled=log_settings.enabled, + ) + for log_settings in getattr(setting, "logs", []) + ], storage_account_id=setting.storage_account_id, ) ) @@ -69,7 +76,17 @@ def get_alert_rules(self): AlertRule( id=rule.id, name=rule.name, - condition=rule.condition, + condition=AlertRuleAllOfCondition( + all_of=[ + AlertRuleAnyOfOrLeafCondition( + field=condition.field, + equals=condition.equals, + ) + for condition in getattr( + getattr(rule, "condition", None), "all_of", [] + ) + ] + ), enabled=rule.enabled, description=rule.description, ) @@ -81,15 +98,33 @@ def get_alert_rules(self): return alert_rules +@dataclass +class LogSettings: + category: str + category_group: str + enabled: bool + + @dataclass class DiagnosticSetting: id: str storage_account_id: str storage_account_name: str - logs: LogSettings + logs: List[LogSettings] name: str +@dataclass +class AlertRuleAnyOfOrLeafCondition: + field: str + equals: str + + +@dataclass +class AlertRuleAllOfCondition: + all_of: List[AlertRuleAnyOfOrLeafCondition] + + @dataclass class AlertRule: id: str diff --git a/prowler/providers/azure/services/network/network_service.py b/prowler/providers/azure/services/network/network_service.py index 3f24e3ee7c..6842926014 100644 --- a/prowler/providers/azure/services/network/network_service.py +++ b/prowler/providers/azure/services/network/network_service.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import List from azure.mgmt.network import NetworkManagementClient @@ -28,7 +29,24 @@ def _get_security_groups(self): id=security_group.id, name=security_group.name, location=security_group.location, - security_rules=security_group.security_rules, + security_rules=[ + SecurityRule( + id=rule.id, + name=rule.name, + destination_port_range=getattr( + rule, "destination_port_range", "" + ), + protocol=getattr(rule, "protocol", ""), + source_address_prefix=getattr( + rule, "source_address_prefix", "" + ), + access=getattr(rule, "access", "Allow"), + direction=getattr(rule, "direction", "Inbound"), + ) + for rule in getattr( + security_group, "security_rules", [] + ) + ], ) ) @@ -52,7 +70,9 @@ def _get_network_watchers(self): id=network_watcher.id, name=network_watcher.name, location=network_watcher.location, - flow_logs=flow_logs, + flow_logs=[ + FlowLog(id=flow_log.id) for flow_log in flow_logs + ], ) ) @@ -122,12 +142,37 @@ class BastionHost: location: str +@dataclass +class RetentionPolicy: + enabled: bool = False + days: int = 0 + + +@dataclass +class FlowLog: + id: str + name: str + enabled: bool + retention_policy: RetentionPolicy + + @dataclass class NetworkWatcher: id: str name: str location: str - flow_logs: list + flow_logs: List[FlowLog] + + +@dataclass +class SecurityRule: + id: str + name: str + destination_port_range: str + protocol: str + source_address_prefix: str + access: str + direction: str @dataclass @@ -135,7 +180,7 @@ class SecurityGroup: id: str name: str location: str - security_rules: list + security_rules: List[SecurityRule] @dataclass diff --git a/prowler/providers/azure/services/sqlserver/sqlserver_service.py b/prowler/providers/azure/services/sqlserver/sqlserver_service.py index f05ca34500..4d4ca00ebc 100644 --- a/prowler/providers/azure/services/sqlserver/sqlserver_service.py +++ b/prowler/providers/azure/services/sqlserver/sqlserver_service.py @@ -1,15 +1,7 @@ from dataclasses import dataclass +from typing import List, Optional from azure.mgmt.sql import SqlManagementClient -from azure.mgmt.sql.models import ( - EncryptionProtector, - FirewallRule, - ServerBlobAuditingPolicy, - ServerExternalAdministrator, - ServerSecurityAlertPolicy, - ServerVulnerabilityAssessment, - TransparentDataEncryption, -) from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -55,7 +47,18 @@ def _get_sql_servers(self): name=sql_server.name, public_network_access=sql_server.public_network_access, minimal_tls_version=sql_server.minimal_tls_version, - administrators=sql_server.administrators, + administrators=ServerExternalAdministrator( + sid=getattr( + getattr(sql_server, "administrators", None), + "sid", + "", + ), + administrator_type=getattr( + getattr(sql_server, "administrators", None), + "administrator_type", + "", + ), + ), auditing_policies=auditing_policies, firewall_rules=firewall_rules, encryption_protector=encryption_protector, @@ -96,7 +99,16 @@ def _get_enctyption_protectors(self, subscription, resource_group, server_name): server_name=server_name, encryption_protector_name="current", ) - return encryption_protectors + + current_encryption_protector = EncryptionProtector( + id=encryption_protectors.id, + name=encryption_protectors.name, + type=encryption_protectors.type, + server_key_name=encryption_protectors.server_key_name, + server_key_type=encryption_protectors.server_key_type, + ) + + return current_encryption_protector def _get_databases(self, subscription, resource_group, server_name): logger.info("SQL Server - Getting server databases...") @@ -118,7 +130,13 @@ def _get_databases(self, subscription, resource_group, server_name): type=database.type, location=database.location, managed_by=database.managed_by, - tde_encryption=tde_encrypted, + tde_encryption=TransparentDataEncryption( + id=tde_encrypted.id, + name=tde_encrypted.name, + type=tde_encrypted.type, + location=tde_encrypted.location, + status=tde_encrypted.status, + ), ) ) except Exception as error: @@ -134,7 +152,17 @@ def _get_vulnerability_assesments(self, subscription, resource_group, server_nam server_name=server_name, vulnerability_assessment_name="default", ) - return vulnerability_assessment + return ServerVulnerabilityAssessment( + id=vulnerability_assessment.id, + name=vulnerability_assessment.name, + type=vulnerability_assessment.type, + storage_container_path=vulnerability_assessment.storage_container_path, + recurring_scans=VulnerabilityAssessmentRecurringScans( + is_enabled=vulnerability_assessment.recurring_scans.is_enabled, + emails=vulnerability_assessment.recurring_scans.emails, + email_subscription_admins=vulnerability_assessment.recurring_scans.email_subscription_admins, + ), + ) def _get_server_blob_auditing_policies( self, subscription, resource_group, server_name @@ -144,14 +172,34 @@ def _get_server_blob_auditing_policies( resource_group_name=resource_group, server_name=server_name, ) - return auditing_policies + auditing_policies_objects = [] + for policy in auditing_policies: + auditing_policies_objects.append( + ServerBlobAuditingPolicy( + id=policy.id, + name=policy.name, + type=policy.type, + state=policy.state, + retention_days=policy.retention_days, + ) + ) + return auditing_policies_objects def _get_firewall_rules(self, subscription, resource_group, server_name): client = self.clients[subscription] firewall_rules = client.firewall_rules.list_by_server( resource_group_name=resource_group, server_name=server_name ) - return firewall_rules + firewall_rules_objects = [] + for rule in firewall_rules: + firewall_rules_objects.append( + FirewallRule( + name=rule.name, + start_ip_address=rule.start_ip_address, + end_ip_address=rule.end_ip_address, + ) + ) + return firewall_rules_objects def _get_server_security_alert_policies( self, subscription, resource_group, server_name @@ -162,7 +210,12 @@ def _get_server_security_alert_policies( server_name=server_name, security_alert_policy_name="default", ) - return security_alert_policies + return ServerSecurityAlertPolicy( + id=security_alert_policies.id, + name=security_alert_policies.name, + type=security_alert_policies.type, + state=security_alert_policies.state, + ) def _get_location(self, subscription, resouce_group_name, server_name): client = self.clients[subscription] @@ -171,6 +224,15 @@ def _get_location(self, subscription, resouce_group_name, server_name): return location +@dataclass +class TransparentDataEncryption: + id: str + name: str + type: str + location: str + status: str + + @dataclass class Database: id: str @@ -181,6 +243,61 @@ class Database: tde_encryption: TransparentDataEncryption +@dataclass +class ServerExternalAdministrator: + sid: str + administrator_type: str + + +@dataclass +class ServerBlobAuditingPolicy: + id: str + name: str + type: str + state: str + retention_days: int + + +@dataclass +class FirewallRule: + name: str + start_ip_address: str + end_ip_address: str + + +@dataclass +class EncryptionProtector: + id: str + name: str + type: str + server_key_name: str + server_key_type: str + + +@dataclass +class VulnerabilityAssessmentRecurringScans: + is_enabled: bool + emails: List[str] + email_subscription_admins: bool + + +@dataclass +class ServerVulnerabilityAssessment: + id: str + name: str + type: str + storage_container_path: str + recurring_scans: VulnerabilityAssessmentRecurringScans + + +@dataclass +class ServerSecurityAlertPolicy: + id: str + name: str + type: str + state: str + + @dataclass class Server: id: str @@ -188,10 +305,10 @@ class Server: public_network_access: str minimal_tls_version: str administrators: ServerExternalAdministrator - auditing_policies: ServerBlobAuditingPolicy - firewall_rules: FirewallRule + auditing_policies: List[ServerBlobAuditingPolicy] + firewall_rules: List[FirewallRule] location: str - encryption_protector: EncryptionProtector = None + encryption_protector: Optional[EncryptionProtector] = None + vulnerability_assessment: Optional[ServerVulnerabilityAssessment] = None + security_alert_policies: Optional[ServerSecurityAlertPolicy] = None databases: list[Database] = None - vulnerability_assessment: ServerVulnerabilityAssessment = None - security_alert_policies: ServerSecurityAlertPolicy = None diff --git a/prowler/providers/azure/services/storage/storage_service.py b/prowler/providers/azure/services/storage/storage_service.py index 63b5fcf64c..cf30241c75 100644 --- a/prowler/providers/azure/services/storage/storage_service.py +++ b/prowler/providers/azure/services/storage/storage_service.py @@ -1,11 +1,7 @@ from dataclasses import dataclass +from typing import List, Optional from azure.mgmt.storage import StorageManagementClient -from azure.mgmt.storage.v2022_09_01.models import ( - DeleteRetentionPolicy, - NetworkRuleSet, - PrivateEndpointConnection, -) from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -45,10 +41,30 @@ def _get_storage_accounts(self): enable_https_traffic_only=storage_account.enable_https_traffic_only, infrastructure_encryption=storage_account.encryption.require_infrastructure_encryption, allow_blob_public_access=storage_account.allow_blob_public_access, - network_rule_set=storage_account.network_rule_set, + network_rule_set=NetworkRuleSet( + bypass=getattr( + storage_account.network_rule_set, + "bypass", + "AzureServices", + ), + default_action=getattr( + storage_account.network_rule_set, + "default_action", + "Allow", + ), + ), encryption_type=storage_account.encryption.key_source, minimum_tls_version=storage_account.minimum_tls_version, - private_endpoint_connections=storage_account.private_endpoint_connections, + private_endpoint_connections=[ + PrivateEndpointConnection( + id=pec.id, + name=pec.name, + type=pec.type, + ) + for pec in getattr( + storage_account, "private_endpoint_connections", [] + ) + ], key_expiration_period_in_days=key_expiration_period_in_days, location=storage_account.location, ) @@ -68,12 +84,22 @@ def _get_blob_properties(self): properties = client.blob_services.get_service_properties( account.resouce_group_name, account.name ) + container_delete_retention_policy = getattr( + properties, "container_delete_retention_policy", None + ) account.blob_properties = BlobProperties( id=properties.id, name=properties.name, type=properties.type, default_service_version=properties.default_service_version, - container_delete_retention_policy=properties.container_delete_retention_policy, + container_delete_retention_policy=DeleteRetentionPolicy( + enabled=getattr( + container_delete_retention_policy, + "enabled", + False, + ), + days=getattr(container_delete_retention_policy, "days", 0), + ), ) except Exception as error: logger.error( @@ -81,6 +107,12 @@ def _get_blob_properties(self): ) +@dataclass +class DeleteRetentionPolicy: + enabled: bool + days: int + + @dataclass class BlobProperties: id: str @@ -90,6 +122,19 @@ class BlobProperties: container_delete_retention_policy: DeleteRetentionPolicy +@dataclass +class NetworkRuleSet: + bypass: str + default_action: str + + +@dataclass +class PrivateEndpointConnection: + id: str + name: str + type: str + + @dataclass class Account: id: str @@ -101,7 +146,7 @@ class Account: network_rule_set: NetworkRuleSet encryption_type: str minimum_tls_version: str - private_endpoint_connections: PrivateEndpointConnection + private_endpoint_connections: List[PrivateEndpointConnection] key_expiration_period_in_days: str location: str - blob_properties: BlobProperties = None + blob_properties: Optional[BlobProperties] = None diff --git a/prowler/providers/azure/services/vm/vm_service.py b/prowler/providers/azure/services/vm/vm_service.py index b624011be3..4700f81eca 100644 --- a/prowler/providers/azure/services/vm/vm_service.py +++ b/prowler/providers/azure/services/vm/vm_service.py @@ -1,7 +1,7 @@ from dataclasses import dataclass +from typing import List, Optional from azure.mgmt.compute import ComputeManagementClient -from azure.mgmt.compute.models import StorageProfile from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider @@ -29,9 +29,32 @@ def _get_virtual_machines(self): vm.id: VirtualMachine( resource_id=vm.id, resource_name=vm.name, - storage_profile=getattr(vm, "storage_profile", None), + storage_profile=( + StorageProfile( + os_disk=OSDisk( + name=vm.storage_profile.os_disk.name, + managed_disk=vm.storage_profile.os_disk.managed_disk, + ), + data_disks=[ + DataDisk( + lun=data_disk.lun, + name=data_disk.name, + managed_disk=data_disk.managed_disk, + ) + for data_disk in getattr( + vm.storage_profile, "data_disks", [] + ) + ], + ) + if getattr(vm, "storage_profile", None) + else None + ), location=vm.location, security_profile=vm.security_profile, + extensions=[ + VirtualMachineExtension(id=extension.id) + for extension in getattr(vm, "resources", []) + ], ) } ) @@ -90,13 +113,38 @@ class SecurityProfile: uefi_settings: UefiSettings +@dataclass +class OSDisk: + name: str + managed_disk: bool + + +@dataclass +class DataDisk: + lun: int + name: str + managed_disk: bool + + +@dataclass +class StorageProfile: + os_disk: OSDisk + data_disks: List[DataDisk] + + +@dataclass +class VirtualMachineExtension: + id: str + + @dataclass class VirtualMachine: resource_id: str resource_name: str - storage_profile: StorageProfile location: str security_profile: SecurityProfile + extensions: list[VirtualMachineExtension] + storage_profile: Optional[StorageProfile] = None @dataclass diff --git a/tests/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled_test.py b/tests/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled_test.py index 82ef50f88e..6f4b3e6c5c 100644 --- a/tests/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled_test.py +++ b/tests/providers/azure/services/containerregistry/containerregistry_admin_user_disabled/containerregistry_admin_user_disabled_test.py @@ -57,7 +57,6 @@ def test_container_registry_admin_user_enabled(self): login_server="mock_login_server.azurecr.io", public_network_access="Enabled", admin_user_enabled=True, - network_rule_set=None, monitor_diagnostic_settings=[], private_endpoint_connections=[], ) @@ -114,7 +113,6 @@ def test_container_registry_admin_user_disabled(self): login_server="mock_login_server.azurecr.io", public_network_access="Enabled", admin_user_enabled=False, - network_rule_set=None, monitor_diagnostic_settings=[], private_endpoint_connections=[], ) diff --git a/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py b/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py index 8239aa5b70..a54e5b8554 100644 --- a/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py +++ b/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py @@ -2,8 +2,6 @@ from unittest.mock import MagicMock from uuid import uuid4 -from azure.mgmt.containerregistry.models import NetworkRuleSet - from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -59,7 +57,6 @@ def test_container_registry_network_access_unrestricted(self): login_server="mock_login_server.azurecr.io", public_network_access=True, admin_user_enabled=True, - network_rule_set=NetworkRuleSet(default_action="Allow"), private_endpoint_connections=[], monitor_diagnostic_settings=[ { @@ -133,7 +130,6 @@ def test_container_registry_network_access_restricted(self): login_server="mock_login_server.azurecr.io", public_network_access=False, admin_user_enabled=False, - network_rule_set=NetworkRuleSet(default_action="Deny"), private_endpoint_connections=[], monitor_diagnostic_settings=[ { diff --git a/tests/providers/azure/services/containerregistry/containerregistry_service_test.py b/tests/providers/azure/services/containerregistry/containerregistry_service_test.py index b6ff17fb16..744459a75f 100644 --- a/tests/providers/azure/services/containerregistry/containerregistry_service_test.py +++ b/tests/providers/azure/services/containerregistry/containerregistry_service_test.py @@ -34,7 +34,6 @@ def test_get_container_registry(self): login_server="mock_login_server.azurecr.io", public_network_access=False, admin_user_enabled=True, - network_rule_set=None, private_endpoint_connections=[], monitor_diagnostic_settings=[ { diff --git a/tests/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link_test.py b/tests/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link_test.py index 79508b229e..422aebdbe4 100644 --- a/tests/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link_test.py +++ b/tests/providers/azure/services/containerregistry/containerregistry_uses_private_link/containerregistry_uses_private_link_test.py @@ -64,7 +64,6 @@ def test_container_registry_not_uses_private_link(self): public_network_access="Enabled", admin_user_enabled=True, monitor_diagnostic_settings=[], - network_rule_set=[], private_endpoint_connections=[], ) } @@ -121,7 +120,6 @@ def test_container_registry_uses_private_link(self): public_network_access="Enabled", admin_user_enabled=False, monitor_diagnostic_settings=[], - network_rule_set=[], private_endpoint_connections=[ PrivateEndpointConnection( id="/subscriptions/AZURE_SUBSCRIPTION_ID/resourceGroups/mock_resource_group/providers/Microsoft.ContainerRegistry/registries/mock_registry/privateEndpointConnections/myConnection", diff --git a/tests/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks_test.py b/tests/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks_test.py index c86339e4ed..347f65e393 100644 --- a/tests/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks_test.py +++ b/tests/providers/azure/services/cosmosdb/cosmosdb_account_firewall_use_selected_networks/cosmosdb_account_firewall_use_selected_networks_test.py @@ -43,6 +43,7 @@ def test_accounts_no_virtual_network_filter_enabled(self): tags=None, disable_local_auth=None, is_virtual_network_filter_enabled=False, + private_endpoint_connections=[], ) ] } @@ -86,6 +87,7 @@ def test_accounts_virtual_network_filter_enabled(self): tags=None, disable_local_auth=None, is_virtual_network_filter_enabled=True, + private_endpoint_connections=[], ) ] } diff --git a/tests/providers/azure/services/cosmosdb/cosmosdb_service_test.py b/tests/providers/azure/services/cosmosdb/cosmosdb_service_test.py index 643ff98a25..209dc69156 100644 --- a/tests/providers/azure/services/cosmosdb/cosmosdb_service_test.py +++ b/tests/providers/azure/services/cosmosdb/cosmosdb_service_test.py @@ -19,6 +19,7 @@ def mock_cosmosdb_get_accounts(_): tags=None, is_virtual_network_filter_enabled=None, disable_local_auth=None, + private_endpoint_connections=[], ) ] } diff --git a/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py b/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py index 226cd2aa9a..de9b2ddd54 100644 --- a/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py +++ b/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py @@ -63,6 +63,7 @@ def test_vm_ensure_using_managed_disks(self): v_tpm_enabled=True, ), ), + extensions=[], storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", @@ -114,6 +115,7 @@ def test_vm_using_not_managed_os_disk(self): v_tpm_enabled=True, ), ), + extensions=[], storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", @@ -165,6 +167,7 @@ def test_vm_using_not_managed_data_disks(self): v_tpm_enabled=True, ), ), + extensions=[], storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", diff --git a/tests/providers/azure/services/vm/vm_service_test.py b/tests/providers/azure/services/vm/vm_service_test.py index 2d876f7308..5bd225e1bf 100644 --- a/tests/providers/azure/services/vm/vm_service_test.py +++ b/tests/providers/azure/services/vm/vm_service_test.py @@ -28,6 +28,7 @@ def mock_vm_get_virtual_machines(_): v_tpm_enabled=True, ), ), + extensions=[], storage_profile=StorageProfile( os_disk=OSDisk( create_option="FromImage", diff --git a/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py b/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py index 1bc20cb770..8f81756561 100644 --- a/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py +++ b/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py @@ -11,12 +11,15 @@ class Test_vm_trusted_launch_enabled: def test_vm_no_subscriptions(self): vm_client = mock.MagicMock vm_client.virtual_machines = {} - 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", - new=vm_client, + 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ), ): from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( vm_trusted_launch_enabled, @@ -29,12 +32,15 @@ def test_vm_no_subscriptions(self): def test_vm_no_vm(self): vm_client = mock.MagicMock vm_client.virtual_machines = {AZURE_SUBSCRIPTION_ID: {}} - 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", - new=vm_client, + 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ), ): from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( vm_trusted_launch_enabled, @@ -47,12 +53,15 @@ def test_vm_no_vm(self): def test_vm_trusted_launch_enabled(self): vm_id = str(uuid4()) vm_client = mock.MagicMock - 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", - new=vm_client, + 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ), ): from prowler.providers.azure.services.vm.vm_service import VirtualMachine from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( @@ -72,6 +81,7 @@ def test_vm_trusted_launch_enabled(self): v_tpm_enabled=True, ), ), + extensions=[], storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", @@ -97,12 +107,15 @@ def test_vm_trusted_launch_enabled(self): def test_vm_trusted_launch_disabled(self): vm_id = str(uuid4()) vm_client = mock.MagicMock - 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", - new=vm_client, + 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_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ), ): from prowler.providers.azure.services.vm.vm_service import VirtualMachine from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( @@ -122,6 +135,7 @@ def test_vm_trusted_launch_disabled(self): v_tpm_enabled=False, ), ), + extensions=[], storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage",