From f8e713a5447c7b9284b2d67ac237bc9050cab21f Mon Sep 17 00:00:00 2001 From: Nacho Rivera Date: Tue, 14 Nov 2023 13:17:48 +0100 Subject: [PATCH] feat(azure regions): support non default azure region (#3013) Co-authored-by: Pepe Fagoaga --- docs/tutorials/azure/use-non-default-cloud.md | 16 ++++++ mkdocs.yml | 1 + poetry.lock | 35 ++++++++++++- prowler/providers/azure/azure_provider.py | 26 +++++++--- .../azure/lib/arguments/arguments.py | 27 ++++++++++ .../azure/lib/audit_info/audit_info.py | 2 + .../providers/azure/lib/audit_info/models.py | 17 ++++++- .../providers/azure/lib/regions/__init__.py | 0 .../providers/azure/lib/regions/regions.py | 38 ++++++++++++++ .../providers/azure/lib/service/service.py | 16 ++++-- prowler/providers/common/audit_info.py | 19 ++++++- pyproject.toml | 1 + tests/lib/cli/parser_test.py | 40 +++++++++++++++ tests/lib/outputs/slack_test.py | 2 + .../azure/lib/regions/regions_test.py | 50 +++++++++++++++++++ tests/providers/common/audit_info_test.py | 7 ++- tests/providers/common/common_outputs_test.py | 2 + 17 files changed, 284 insertions(+), 15 deletions(-) create mode 100644 docs/tutorials/azure/use-non-default-cloud.md create mode 100644 prowler/providers/azure/lib/regions/__init__.py create mode 100644 prowler/providers/azure/lib/regions/regions.py create mode 100644 tests/providers/azure/lib/regions/regions_test.py diff --git a/docs/tutorials/azure/use-non-default-cloud.md b/docs/tutorials/azure/use-non-default-cloud.md new file mode 100644 index 00000000..50e94224 --- /dev/null +++ b/docs/tutorials/azure/use-non-default-cloud.md @@ -0,0 +1,16 @@ +# Use non default Azure regions + +Microsoft provides clouds for compliance with regional laws, which are available for your use. +By default, Prowler uses `AzureCloud` cloud which is the comercial one. (you can list all the available with `az cloud list --output table`). + +At the time of writing this documentation the available Azure Clouds from different regions are the following: +- AzureCloud +- AzureChinaCloud +- AzureUSGovernment +- AzureGermanCloud + +If you want to change the default one you must include the flag `--azure-region`, i.e.: + +```console +prowler azure --az-cli-auth --azure-region AzureChinaCloud +``` diff --git a/mkdocs.yml b/mkdocs.yml index d06c747a..1078789b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,6 +56,7 @@ nav: - Boto3 Configuration: tutorials/aws/boto3-configuration.md - Azure: - Authentication: tutorials/azure/authentication.md + - Non default clouds: tutorials/azure/use-non-default-cloud.md - Subscriptions: tutorials/azure/subscriptions.md - Google Cloud: - Authentication: tutorials/gcp/authentication.md diff --git a/poetry.lock b/poetry.lock index 41eb60f4..d938a6cc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,6 +11,23 @@ files = [ {file = "about_time-4.2.1-py3-none-any.whl", hash = "sha256:8bbf4c75fe13cbd3d72f49a03b02c5c7dca32169b6d49117c257e7eb3eaee341"}, ] +[[package]] +name = "adal" +version = "1.2.7" +description = "Note: This library is already replaced by MSAL Python, available here: https://pypi.org/project/msal/ .ADAL Python remains available here as a legacy. The ADAL for Python library makes it easy for python application to authenticate to Azure Active Directory (AAD) in order to access AAD protected web resources." +optional = false +python-versions = "*" +files = [ + {file = "adal-1.2.7-py2.py3-none-any.whl", hash = "sha256:2a7451ed7441ddbc57703042204a3e30ef747478eea022c70f789fc7f084bc3d"}, + {file = "adal-1.2.7.tar.gz", hash = "sha256:d74f45b81317454d96e982fd1c50e6fb5c99ac2223728aea8764433a39f566f1"}, +] + +[package.dependencies] +cryptography = ">=1.1.0" +PyJWT = ">=1.0.0,<3" +python-dateutil = ">=2.1.0,<3" +requests = ">=2.0.0,<3" + [[package]] name = "alive-progress" version = "3.1.5" @@ -1537,6 +1554,22 @@ requests-oauthlib = ">=0.5.0" [package.extras] async = ["aiodns", "aiohttp (>=3.0)"] +[[package]] +name = "msrestazure" +version = "0.6.4" +description = "AutoRest swagger generator Python client runtime. Azure-specific module." +optional = false +python-versions = "*" +files = [ + {file = "msrestazure-0.6.4-py2.py3-none-any.whl", hash = "sha256:3de50f56147ef529b31e099a982496690468ecef33f0544cb0fa0cfe1e1de5b9"}, + {file = "msrestazure-0.6.4.tar.gz", hash = "sha256:a06f0dabc9a6f5efe3b6add4bd8fb623aeadacf816b7a35b0f89107e0544d189"}, +] + +[package.dependencies] +adal = ">=0.6.0,<2.0.0" +msrest = ">=0.6.0,<2.0.0" +six = "*" + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -2889,4 +2922,4 @@ docs = ["mkdocs", "mkdocs-material"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "18f9e44d555478c5d70cec7f39fc18263c54567a14ee0b100faf14f4d0cb8946" +content-hash = "594dc3dc4952b294042203c3338b6959fed04eb6eb181796a4ae8c27cde5bf32" diff --git a/prowler/providers/azure/azure_provider.py b/prowler/providers/azure/azure_provider.py index 2f054ec9..52f5459a 100644 --- a/prowler/providers/azure/azure_provider.py +++ b/prowler/providers/azure/azure_provider.py @@ -7,6 +7,7 @@ from msgraph.core import GraphClient from prowler.lib.logger import logger from prowler.providers.azure.lib.audit_info.models import Azure_Identity_Info +from prowler.providers.azure.lib.regions.regions import get_regions_config class Azure_Provider: @@ -18,12 +19,14 @@ class Azure_Provider: managed_entity_auth: bool, subscription_ids: list, tenant_id: str, + region: str, ): logger.info("Instantiating Azure Provider ...") - self.credentials = self.__set_credentials__( + self.region_config = self.__get_region_config__(region) + self.credentials = self.__get_credentials__( az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id ) - self.identity = self.__set_identity_info__( + self.identity = self.__get_identity_info__( self.credentials, az_cli_auth, sp_env_auth, @@ -32,7 +35,10 @@ class Azure_Provider: subscription_ids, ) - def __set_credentials__( + def __get_region_config__(self, region): + return get_regions_config(region) + + def __get_credentials__( self, az_cli_auth, sp_env_auth, browser_auth, managed_entity_auth, tenant_id ): # Browser auth creds cannot be set with DefaultAzureCredentials() @@ -52,6 +58,8 @@ class Azure_Provider: exclude_shared_token_cache_credential=True, # Azure Auth using PowerShell is not supported exclude_powershell_credential=True, + # set Authority of a Microsoft Entra endpoint + authority=self.region_config["authority"], ) except Exception as error: logger.critical("Failed to retrieve azure credentials") @@ -61,7 +69,6 @@ class Azure_Provider: sys.exit(1) else: try: - print(tenant_id) credentials = InteractiveBrowserCredential(tenant_id=tenant_id) except Exception as error: logger.critical("Failed to retrieve azure credentials") @@ -83,7 +90,7 @@ class Azure_Provider: ) sys.exit(1) - def __set_identity_info__( + def __get_identity_info__( self, credentials, az_cli_auth, @@ -153,7 +160,11 @@ class Azure_Provider: logger.info( "Trying to subscriptions and tenant ids to populate identity structure ..." ) - subscriptions_client = SubscriptionClient(credential=credentials) + subscriptions_client = SubscriptionClient( + credential=credentials, + base_url=self.region_config["base_url"], + credential_scopes=self.region_config["credential_scopes"], + ) if not subscription_ids: logger.info("Scanning all the Azure subscriptions...") for subscription in subscriptions_client.subscriptions.list(): @@ -195,3 +206,6 @@ class Azure_Provider: def get_identity(self): return self.identity + + def get_region_config(self): + return self.region_config diff --git a/prowler/providers/azure/lib/arguments/arguments.py b/prowler/providers/azure/lib/arguments/arguments.py index 5c1316e6..30cf7a33 100644 --- a/prowler/providers/azure/lib/arguments/arguments.py +++ b/prowler/providers/azure/lib/arguments/arguments.py @@ -1,3 +1,6 @@ +from argparse import ArgumentTypeError + + def init_parser(self): """Init the Azure Provider CLI parser""" azure_parser = self.subparsers.add_parser( @@ -40,3 +43,27 @@ def init_parser(self): default=None, help="Azure Tenant ID to be used with --browser-auth option", ) + # Regions + azure_regions_subparser = azure_parser.add_argument_group("Regions") + azure_regions_subparser.add_argument( + "--azure-region", + nargs="?", + default="AzureCloud", + type=validate_azure_region, + help="Azure region from `az cloud list --output table`, by default AzureCloud", + ) + + +def validate_azure_region(region): + """validate_azure_region validates if the region passed as argument is valid""" + regions_allowed = [ + "AzureChinaCloud", + "AzureUSGovernment", + "AzureGermanCloud", + "AzureCloud", + ] + if region not in regions_allowed: + raise ArgumentTypeError( + f"Region {region} not allowed, allowed regions are {' '.join(regions_allowed)}" + ) + return region diff --git a/prowler/providers/azure/lib/audit_info/audit_info.py b/prowler/providers/azure/lib/audit_info/audit_info.py index 62144444..098f63c7 100644 --- a/prowler/providers/azure/lib/audit_info/audit_info.py +++ b/prowler/providers/azure/lib/audit_info/audit_info.py @@ -1,6 +1,7 @@ from prowler.providers.azure.lib.audit_info.models import ( Azure_Audit_Info, Azure_Identity_Info, + Azure_Region_Config, ) azure_audit_info = Azure_Audit_Info( @@ -9,4 +10,5 @@ azure_audit_info = Azure_Audit_Info( audit_resources=None, audit_metadata=None, audit_config=None, + azure_region_config=Azure_Region_Config(), ) diff --git a/prowler/providers/azure/lib/audit_info/models.py b/prowler/providers/azure/lib/audit_info/models.py index dcbe1ade..978ec342 100644 --- a/prowler/providers/azure/lib/audit_info/models.py +++ b/prowler/providers/azure/lib/audit_info/models.py @@ -13,6 +13,13 @@ class Azure_Identity_Info(BaseModel): subscriptions: dict = {} +class Azure_Region_Config(BaseModel): + name: str = "" + authority: str = None + base_url: str = "" + credential_scopes: list = [] + + @dataclass class Azure_Audit_Info: credentials: DefaultAzureCredential @@ -20,12 +27,20 @@ class Azure_Audit_Info: audit_resources: Optional[Any] audit_metadata: Optional[Any] audit_config: dict + azure_region_config: Azure_Region_Config def __init__( - self, credentials, identity, audit_metadata, audit_resources, audit_config + self, + credentials, + identity, + audit_metadata, + audit_resources, + audit_config, + azure_region_config, ): self.credentials = credentials self.identity = identity self.audit_metadata = audit_metadata self.audit_resources = audit_resources self.audit_config = audit_config + self.azure_region_config = azure_region_config diff --git a/prowler/providers/azure/lib/regions/__init__.py b/prowler/providers/azure/lib/regions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/azure/lib/regions/regions.py b/prowler/providers/azure/lib/regions/regions.py new file mode 100644 index 00000000..a9d29d7e --- /dev/null +++ b/prowler/providers/azure/lib/regions/regions.py @@ -0,0 +1,38 @@ +from azure.identity import AzureAuthorityHosts +from msrestazure.azure_cloud import ( + AZURE_CHINA_CLOUD, + AZURE_GERMAN_CLOUD, + AZURE_US_GOV_CLOUD, +) + + +def get_regions_config(region): + allowed_regions = { + "AzureCloud": { + "authority": None, + "base_url": "https://management.azure.com", + "credential_scopes": ["https://management.azure.com/.default"], + }, + "AzureChinaCloud": { + "authority": AzureAuthorityHosts.AZURE_CHINA, + "base_url": AZURE_CHINA_CLOUD.endpoints.resource_manager, + "credential_scopes": [ + AZURE_CHINA_CLOUD.endpoints.resource_manager + "/.default" + ], + }, + "AzureUSGovernment": { + "authority": AzureAuthorityHosts.AZURE_GOVERNMENT, + "base_url": AZURE_US_GOV_CLOUD.endpoints.resource_manager, + "credential_scopes": [ + AZURE_US_GOV_CLOUD.endpoints.resource_manager + "/.default" + ], + }, + "AzureGermanCloud": { + "authority": AzureAuthorityHosts.AZURE_GERMANY, + "base_url": AZURE_GERMAN_CLOUD.endpoints.resource_manager, + "credential_scopes": [ + AZURE_GERMAN_CLOUD.endpoints.resource_manager + "/.default" + ], + }, + } + return allowed_regions[region] diff --git a/prowler/providers/azure/lib/service/service.py b/prowler/providers/azure/lib/service/service.py index f45747c5..305f2589 100644 --- a/prowler/providers/azure/lib/service/service.py +++ b/prowler/providers/azure/lib/service/service.py @@ -9,17 +9,27 @@ class AzureService: audit_info: Azure_Audit_Info, ): self.clients = self.__set_clients__( - audit_info.identity.subscriptions, audit_info.credentials, service + audit_info.identity.subscriptions, + audit_info.credentials, + service, + audit_info.azure_region_config, ) self.subscriptions = audit_info.identity.subscriptions - def __set_clients__(self, subscriptions, credentials, service): + def __set_clients__(self, subscriptions, credentials, service, region_config): clients = {} try: for display_name, id in subscriptions.items(): clients.update( - {display_name: service(credential=credentials, subscription_id=id)} + { + display_name: service( + credential=credentials, + subscription_id=id, + base_url=region_config.base_url, + credential_scopes=region_config.credential_scopes, + ) + } ) except Exception as error: logger.error( diff --git a/prowler/providers/common/audit_info.py b/prowler/providers/common/audit_info.py index d2ee1361..6ec6b429 100644 --- a/prowler/providers/common/audit_info.py +++ b/prowler/providers/common/audit_info.py @@ -26,7 +26,10 @@ from prowler.providers.aws.lib.resource_api_tagging.resource_api_tagging import ) from prowler.providers.azure.azure_provider import Azure_Provider from prowler.providers.azure.lib.audit_info.audit_info import azure_audit_info -from prowler.providers.azure.lib.audit_info.models import Azure_Audit_Info +from prowler.providers.azure.lib.audit_info.models import ( + Azure_Audit_Info, + Azure_Region_Config, +) from prowler.providers.gcp.gcp_provider import GCP_Provider from prowler.providers.gcp.lib.audit_info.audit_info import gcp_audit_info from prowler.providers.gcp.lib.audit_info.models import GCP_Audit_Info @@ -63,7 +66,7 @@ GCP Account: {Fore.YELLOW}[{profile}]{Style.RESET_ALL} GCP Project IDs: {Fore.Y report = f""" This report is being generated using the identity below: -Azure Tenant IDs: {Fore.YELLOW}[{" ".join(audit_info.identity.tenant_ids)}]{Style.RESET_ALL} Azure Tenant Domain: {Fore.YELLOW}[{audit_info.identity.domain}]{Style.RESET_ALL} +Azure Tenant IDs: {Fore.YELLOW}[{" ".join(audit_info.identity.tenant_ids)}]{Style.RESET_ALL} Azure Tenant Domain: {Fore.YELLOW}[{audit_info.identity.domain}]{Style.RESET_ALL} Azure Region: {Fore.YELLOW}[{audit_info.azure_region_config.name}]{Style.RESET_ALL} Azure Subscriptions: {Fore.YELLOW}{printed_subscriptions}{Style.RESET_ALL} Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RESET_ALL} Azure Identity ID: {Fore.YELLOW}[{audit_info.identity.identity_id}]{Style.RESET_ALL} """ @@ -282,6 +285,10 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE browser_auth = arguments.get("browser_auth") managed_entity_auth = arguments.get("managed_entity_auth") tenant_id = arguments.get("tenant_id") + + logger.info("Checking if region is different than default one") + region = arguments.get("azure_region") + if ( not az_cli_auth and not sp_env_auth @@ -303,9 +310,17 @@ Azure Identity Type: {Fore.YELLOW}[{audit_info.identity.identity_type}]{Style.RE managed_entity_auth, subscription_ids, tenant_id, + region, ) azure_audit_info.credentials = azure_provider.get_credentials() azure_audit_info.identity = azure_provider.get_identity() + region_config = azure_provider.get_region_config() + azure_audit_info.azure_region_config = Azure_Region_Config( + name=region, + authority=region_config["authority"], + base_url=region_config["base_url"], + credential_scopes=region_config["credential_scopes"], + ) if not arguments.get("only_logs"): self.print_azure_credentials(azure_audit_info) diff --git a/pyproject.toml b/pyproject.toml index 22735452..d5d6ec26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ google-auth-httplib2 = "^0.1.0" mkdocs = {version = "1.5.3", optional = true} mkdocs-material = {version = "9.4.8", optional = true} msgraph-core = "0.2.2" +msrestazure = "^0.6.4" pydantic = "1.10.13" python = "^3.9" schema = "0.7.5" diff --git a/tests/lib/cli/parser_test.py b/tests/lib/cli/parser_test.py index 2f911ef5..a307c541 100644 --- a/tests/lib/cli/parser_test.py +++ b/tests/lib/cli/parser_test.py @@ -1,9 +1,11 @@ import uuid +from argparse import ArgumentTypeError import pytest from mock import patch from prowler.lib.cli.parser import ProwlerArgumentParser +from prowler.providers.azure.lib.arguments.arguments import validate_azure_region prowler_command = "prowler" @@ -1050,6 +1052,14 @@ class Test_Parser: assert parsed.subscription_ids[0] == subscription_1 assert parsed.subscription_ids[1] == subscription_2 + def test_parser_azure_region(self): + argument = "--azure-region" + region = "AzureChinaCloud" + command = [prowler_command, "azure", argument, region] + parsed = self.parser.parse(command) + assert parsed.provider == "azure" + assert parsed.azure_region == region + # Test AWS flags with Azure provider def test_parser_azure_with_aws_flag(self, capsys): command = [prowler_command, "azure", "-p"] @@ -1092,3 +1102,33 @@ class Test_Parser: assert len(parsed.project_ids) == 2 assert parsed.project_ids[0] == project_1 assert parsed.project_ids[1] == project_2 + + def test_validate_azure_region_valid_regions(self): + expected_regions = [ + "AzureChinaCloud", + "AzureUSGovernment", + "AzureGermanCloud", + "AzureCloud", + ] + input_regions = [ + "AzureChinaCloud", + "AzureUSGovernment", + "AzureGermanCloud", + "AzureCloud", + ] + for region in input_regions: + assert validate_azure_region(region) in expected_regions + + def test_validate_azure_region_invalid_regions(self): + expected_regions = [ + "AzureChinaCloud", + "AzureUSGovernment", + "AzureGermanCloud", + "AzureCloud", + ] + invalid_region = "non-valid-region" + with pytest.raises( + ArgumentTypeError, + match=f"Region {invalid_region} not allowed, allowed regions are {' '.join(expected_regions)}", + ): + validate_azure_region(invalid_region) diff --git a/tests/lib/outputs/slack_test.py b/tests/lib/outputs/slack_test.py index b6609356..02e572ea 100644 --- a/tests/lib/outputs/slack_test.py +++ b/tests/lib/outputs/slack_test.py @@ -11,6 +11,7 @@ from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info from prowler.providers.azure.lib.audit_info.models import ( Azure_Audit_Info, Azure_Identity_Info, + Azure_Region_Config, ) from prowler.providers.common.models import Audit_Metadata from prowler.providers.gcp.lib.audit_info.models import GCP_Audit_Info @@ -76,6 +77,7 @@ class Test_Slack_Integration: audit_resources=None, audit_metadata=None, audit_config=None, + azure_region_config=Azure_Region_Config(), ) assert create_message_identity("aws", aws_audit_info) == ( f"AWS Account *{aws_audit_info.audited_account}*", diff --git a/tests/providers/azure/lib/regions/regions_test.py b/tests/providers/azure/lib/regions/regions_test.py new file mode 100644 index 00000000..acf9d2b1 --- /dev/null +++ b/tests/providers/azure/lib/regions/regions_test.py @@ -0,0 +1,50 @@ +from azure.identity import AzureAuthorityHosts +from msrestazure.azure_cloud import ( + AZURE_CHINA_CLOUD, + AZURE_GERMAN_CLOUD, + AZURE_US_GOV_CLOUD, +) + +from prowler.providers.azure.lib.regions.regions import get_regions_config + + +class Test_azure_regions: + def test_get_regions_config(self): + allowed_regions = [ + "AzureCloud", + "AzureChinaCloud", + "AzureUSGovernment", + "AzureGermanCloud", + ] + expected_output = { + "AzureCloud": { + "authority": None, + "base_url": "https://management.azure.com", + "credential_scopes": ["https://management.azure.com/.default"], + }, + "AzureChinaCloud": { + "authority": AzureAuthorityHosts.AZURE_CHINA, + "base_url": AZURE_CHINA_CLOUD.endpoints.resource_manager, + "credential_scopes": [ + AZURE_CHINA_CLOUD.endpoints.resource_manager + "/.default" + ], + }, + "AzureUSGovernment": { + "authority": AzureAuthorityHosts.AZURE_GOVERNMENT, + "base_url": AZURE_US_GOV_CLOUD.endpoints.resource_manager, + "credential_scopes": [ + AZURE_US_GOV_CLOUD.endpoints.resource_manager + "/.default" + ], + }, + "AzureGermanCloud": { + "authority": AzureAuthorityHosts.AZURE_GERMANY, + "base_url": AZURE_GERMAN_CLOUD.endpoints.resource_manager, + "credential_scopes": [ + AZURE_GERMAN_CLOUD.endpoints.resource_manager + "/.default" + ], + }, + } + + for region in allowed_regions: + region_config = get_regions_config(region) + assert region_config == expected_output[region] diff --git a/tests/providers/common/audit_info_test.py b/tests/providers/common/audit_info_test.py index d8c9b5d7..4ce4fb0c 100644 --- a/tests/providers/common/audit_info_test.py +++ b/tests/providers/common/audit_info_test.py @@ -11,6 +11,7 @@ from prowler.providers.azure.azure_provider import Azure_Provider from prowler.providers.azure.lib.audit_info.models import ( Azure_Audit_Info, Azure_Identity_Info, + Azure_Region_Config, ) from prowler.providers.common.audit_info import ( Audit_Info, @@ -31,6 +32,7 @@ mock_azure_audit_info = Azure_Audit_Info( audit_metadata=None, audit_resources=None, audit_config=None, + azure_region_config=Azure_Region_Config(), ) mock_set_audit_info = Audit_Info() @@ -132,8 +134,8 @@ class Test_Set_Audit_Info: "prowler.providers.common.audit_info.azure_audit_info", new=mock_azure_audit_info, ) - @patch.object(Azure_Provider, "__set_credentials__", new=mock_set_azure_credentials) - @patch.object(Azure_Provider, "__set_identity_info__", new=mock_set_identity_info) + @patch.object(Azure_Provider, "__get_credentials__", new=mock_set_azure_credentials) + @patch.object(Azure_Provider, "__get_identity_info__", new=mock_set_identity_info) def test_set_audit_info_azure(self): provider = "azure" arguments = { @@ -150,6 +152,7 @@ class Test_Set_Audit_Info: "browser_auth": None, "managed_entity_auth": None, "config_file": default_config_file_path, + "azure_region": "AzureCloud", } audit_info = set_provider_audit_info(provider, arguments) diff --git a/tests/providers/common/common_outputs_test.py b/tests/providers/common/common_outputs_test.py index a756b723..c3f1fb94 100644 --- a/tests/providers/common/common_outputs_test.py +++ b/tests/providers/common/common_outputs_test.py @@ -9,6 +9,7 @@ from prowler.providers.aws.lib.audit_info.audit_info import AWS_Audit_Info from prowler.providers.azure.lib.audit_info.audit_info import ( Azure_Audit_Info, Azure_Identity_Info, + Azure_Region_Config, ) from prowler.providers.common.models import Audit_Metadata from prowler.providers.common.outputs import ( @@ -33,6 +34,7 @@ class Test_Common_Output_Options: audit_metadata=None, audit_resources=None, audit_config=None, + azure_region_config=Azure_Region_Config(), ) return audit_info