From 361f8548bfb45f564c0a36fb6fbe1257d99af354 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Mon, 16 Mar 2026 12:14:58 +0100 Subject: [PATCH] feat(azure): add 'entra_conditional_access_policy_require_mfa_for_admin_portals' check and update compliance (#10330) --- prowler/CHANGELOG.md | 1 + prowler/compliance/azure/cis_3.0_azure.json | 2 +- prowler/compliance/azure/cis_4.0_azure.json | 2 +- prowler/compliance/azure/cis_5.0_azure.json | 2 +- .../azure/prowler_threatscore_azure.json | 2 +- prowler/providers/azure/config.py | 5 +- .../__init__.py | 0 ...equire_mfa_for_admin_portals.metadata.json | 38 +++ ...ss_policy_require_mfa_for_admin_portals.py | 44 +++ ...quire_mfa_for_management_api.metadata.json | 12 +- ...licy_require_mfa_for_admin_portals_test.py | 280 ++++++++++++++++++ 11 files changed, 379 insertions(+), 9 deletions(-) create mode 100644 prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/__init__.py create mode 100644 prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.metadata.json create mode 100644 prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.py create mode 100644 tests/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals_test.py diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index aa1b7b67d4..df49d1fa25 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - `entra_conditional_access_policy_device_code_flow_blocked` check for M365 provider [(#10218)](https://github.com/prowler-cloud/prowler/pull/10218) - CheckMetadata Pydantic validators [(#8584)](https://github.com/prowler-cloud/prowler/pull/8583) +- `entra_conditional_access_policy_require_mfa_for_admin_portals` check for Azure provider and update CIS compliance [(#10330)](https://github.com/prowler-cloud/prowler/pull/10330) ### 🔄 Changed diff --git a/prowler/compliance/azure/cis_3.0_azure.json b/prowler/compliance/azure/cis_3.0_azure.json index 3e70009d72..90fa5bfea0 100644 --- a/prowler/compliance/azure/cis_3.0_azure.json +++ b/prowler/compliance/azure/cis_3.0_azure.json @@ -752,7 +752,7 @@ "Id": "2.2.8", "Description": "Ensure Multi-factor Authentication is Required to access Microsoft Admin Portals", "Checks": [ - "defender_ensure_defender_for_server_is_on" + "entra_conditional_access_policy_require_mfa_for_admin_portals" ], "Attributes": [ { diff --git a/prowler/compliance/azure/cis_4.0_azure.json b/prowler/compliance/azure/cis_4.0_azure.json index 0df135a546..3777d24663 100644 --- a/prowler/compliance/azure/cis_4.0_azure.json +++ b/prowler/compliance/azure/cis_4.0_azure.json @@ -1036,7 +1036,7 @@ "Id": "6.2.7", "Description": "Ensure that multifactor authentication is required to access Microsoft Admin Portals", "Checks": [ - "defender_ensure_defender_for_server_is_on" + "entra_conditional_access_policy_require_mfa_for_admin_portals" ], "Attributes": [ { diff --git a/prowler/compliance/azure/cis_5.0_azure.json b/prowler/compliance/azure/cis_5.0_azure.json index 4a7172de77..f786ebd3e2 100644 --- a/prowler/compliance/azure/cis_5.0_azure.json +++ b/prowler/compliance/azure/cis_5.0_azure.json @@ -472,7 +472,7 @@ "Id": "5.2.7", "Description": "Ensure that multifactor authentication is required to access Microsoft Admin Portals", "Checks": [ - "defender_ensure_defender_for_server_is_on" + "entra_conditional_access_policy_require_mfa_for_admin_portals" ], "Attributes": [ { diff --git a/prowler/compliance/azure/prowler_threatscore_azure.json b/prowler/compliance/azure/prowler_threatscore_azure.json index 1eabf7d582..aa801887b2 100644 --- a/prowler/compliance/azure/prowler_threatscore_azure.json +++ b/prowler/compliance/azure/prowler_threatscore_azure.json @@ -63,7 +63,7 @@ "Id": "1.1.4", "Description": "Ensure Multi-factor Authentication is Required to access Microsoft Admin Portals", "Checks": [ - "defender_ensure_defender_for_server_is_on" + "entra_conditional_access_policy_require_mfa_for_admin_portals" ], "Attributes": [ { diff --git a/prowler/providers/azure/config.py b/prowler/providers/azure/config.py index c9c9f4823a..e1aa257a97 100644 --- a/prowler/providers/azure/config.py +++ b/prowler/providers/azure/config.py @@ -1,7 +1,10 @@ from uuid import UUID -# Service management API +# Conditional Access target resource identifiers +# (Graph API includeApplications values) WINDOWS_AZURE_SERVICE_MANAGEMENT_API = "797f4846-ba00-4fd7-ba43-dac1f8f63013" +# Named app group — Graph API returns this literal string, not a GUID +MICROSOFT_ADMIN_PORTALS = "MicrosoftAdminPortals" # Authorization policy roles GUEST_USER_ACCESS_NO_RESTRICTICTED = UUID("a0b1b346-4d3e-4e8b-98f8-753987be4970") diff --git a/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/__init__.py b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.metadata.json b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.metadata.json new file mode 100644 index 0000000000..d12df2e5e6 --- /dev/null +++ b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.metadata.json @@ -0,0 +1,38 @@ +{ + "Provider": "azure", + "CheckID": "entra_conditional_access_policy_require_mfa_for_admin_portals", + "CheckTitle": "Conditional Access policy requires MFA for Microsoft Admin Portals", + "CheckType": [], + "ServiceName": "entra", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "NotDefined", + "ResourceGroup": "IAM", + "Description": "**Microsoft Entra Conditional Access** is evaluated for a policy that requires **multifactor authentication** when accessing **Microsoft Admin Portals** (Microsoft 365 Admin Center, Microsoft Entra Admin Center, Microsoft Exchange Admin Center, etc.). The check confirms an enabled policy targets **All users**, includes the Microsoft Admin Portals app, and enforces an **MFA** grant control.", + "Risk": "Without **MFA** on admin portals, attackers with stolen credentials can access **Microsoft 365 Admin Center**, **Entra Admin Center**, or **Exchange Admin Center** to modify tenant settings, escalate privileges, and exfiltrate data. This directly impacts **confidentiality**, **integrity**, and **availability** of all services managed through those portals.", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://learn.microsoft.com/en-us/entra/identity/conditional-access/how-to-policy-mfa-admin-portals", + "https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-cloud-apps" + ], + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "1. Sign in to the Microsoft Entra admin center\n2. Go to Protection > Conditional Access > Policies\n3. Click + New policy and enter a name\n4. Under Users > Include, select All users\n5. Under Exclude, check Users and groups and select break-glass / non-interactive service accounts\n6. Under Target resources > Include, click Select apps, then select Microsoft Admin Portals\n7. Under Grant, select Grant access and check Require multifactor authentication\n8. Set Enable policy to Report-only, click Create\n9. After testing, change Enable policy from Report-only to On", + "Terraform": "```hcl\nresource \"azuread_conditional_access_policy\" \"\" {\n display_name = \"\"\n state = \"enabled\"\n\n conditions {\n users {\n included_users = [\"All\"]\n excluded_users = [\"\"]\n }\n applications {\n included_applications = [\"MicrosoftAdminPortals\"] # Critical: Microsoft Admin Portals\n }\n }\n\n grant_controls {\n operator = \"OR\"\n built_in_controls = [\"mfa\"] # Critical: requires MFA\n }\n}\n```" + }, + "Recommendation": { + "Text": "Create a **Conditional Access policy** that requires **MFA** for the **Microsoft Admin Portals** app targeting **All users**. Exclude only **break-glass** emergency accounts and non-interactive service principals. Test in **Report-only** mode before enforcing. Prefer **phishing-resistant** MFA methods (FIDO2, passkeys) and apply **least privilege** principles.", + "Url": "https://hub.prowler.com/check/entra_conditional_access_policy_require_mfa_for_admin_portals" + } + }, + "Categories": [ + "identity-access", + "e3" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Conditional Access policies require Microsoft Entra ID P1 or P2 licenses. Similarly, they may require additional overhead to maintain if users lose access to their MFA. Any users or groups which are granted an exception to this policy should be carefully tracked, be granted only minimal necessary privileges, and conditional access exceptions should be regularly reviewed or investigated." +} diff --git a/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.py b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.py new file mode 100644 index 0000000000..30ba990ec6 --- /dev/null +++ b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals.py @@ -0,0 +1,44 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.config import MICROSOFT_ADMIN_PORTALS +from prowler.providers.azure.services.entra.entra_client import entra_client + + +class entra_conditional_access_policy_require_mfa_for_admin_portals(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for ( + tenant_name, + conditional_access_policies, + ) in entra_client.conditional_access_policy.items(): + for policy in conditional_access_policies.values(): + if ( + policy.state == "enabled" + and "All" in policy.users["include"] + and MICROSOFT_ADMIN_PORTALS in policy.target_resources["include"] + and any( + "mfa" in access_control.lower() + for access_control in policy.access_controls["grant"] + ) + ): + report = Check_Report_Azure( + metadata=self.metadata(), resource=policy + ) + report.subscription = f"Tenant: {tenant_name}" + report.status = "PASS" + report.status_extended = "Conditional Access Policy requires MFA for Microsoft Admin Portals." + break + else: + report = Check_Report_Azure( + metadata=self.metadata(), + resource=conditional_access_policies, + ) + report.subscription = f"Tenant: {tenant_name}" + report.resource_name = "Conditional Access Policy" + report.resource_id = "Conditional Access Policy" + report.status = "FAIL" + report.status_extended = "Conditional Access Policy does not require MFA for Microsoft Admin Portals." + + findings.append(report) + + return findings diff --git a/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_management_api/entra_conditional_access_policy_require_mfa_for_management_api.metadata.json b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_management_api/entra_conditional_access_policy_require_mfa_for_management_api.metadata.json index 5d0d2cacb7..6304f8676b 100644 --- a/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_management_api/entra_conditional_access_policy_require_mfa_for_management_api.metadata.json +++ b/prowler/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_management_api/entra_conditional_access_policy_require_mfa_for_management_api.metadata.json @@ -9,15 +9,19 @@ "Severity": "medium", "ResourceType": "NotDefined", "ResourceGroup": "IAM", - "Description": "This recommendation ensures that users accessing the Windows Azure Service Management API (i.e. Azure Powershell, Azure CLI, Azure Resource Manager API, etc.) are required to use multifactor authentication (MFA) credentials when accessing resources through the Windows Azure Service Management API.", - "Risk": "Administrative access to the Windows Azure Service Management API should be secured with a higher level of scrutiny to authenticating mechanisms. Enabling multifactor authentication is recommended to reduce the potential for abuse of Administrative actions, and to prevent intruders or compromised admin credentials from changing administrative settings.", + "Description": "**Microsoft Entra Conditional Access** is evaluated for a policy that requires **multifactor authentication** when accessing the **Windows Azure Service Management API** (Azure PowerShell, Azure CLI, Azure Resource Manager API, etc.). The check confirms an enabled policy targets **All users**, includes the Service Management API app, and enforces an **MFA** grant control.", + "Risk": "Without **MFA** on the Service Management API, attackers with stolen credentials can use **Azure CLI**, **PowerShell**, or the **Resource Manager API** to modify infrastructure, escalate privileges, and exfiltrate data. This directly impacts **confidentiality**, **integrity**, and **availability** of all Azure resources managed through the API.", "RelatedUrl": "", + "AdditionalURLs": [ + "https://learn.microsoft.com/en-us/entra/identity/conditional-access/howto-conditional-access-policy-azure-management", + "https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-cloud-apps" + ], "Remediation": { "Code": { "CLI": "", "NativeIaC": "", - "Other": "", - "Terraform": "" + "Other": "1. Sign in to the Microsoft Entra admin center\n2. Go to Protection > Conditional Access > Policies\n3. Click + New policy and enter a name\n4. Under Users > Include, select All users\n5. Under Exclude, check Users and groups and select break-glass / non-interactive service accounts\n6. Under Target resources > Include, click Select apps, then select Windows Azure Service Management API\n7. Under Grant, select Grant access and check Require multifactor authentication\n8. Set Enable policy to Report-only, click Create\n9. After testing, change Enable policy from Report-only to On", + "Terraform": "```hcl\nresource \"azuread_conditional_access_policy\" \"\" {\n display_name = \"\"\n state = \"enabled\"\n\n conditions {\n users {\n included_users = [\"All\"]\n excluded_users = [\"\"]\n }\n applications {\n included_applications = [\"797f4846-ba00-4fd7-ba43-dac1f8f63013\"] # Critical: Windows Azure Service Management API\n }\n }\n\n grant_controls {\n operator = \"OR\"\n built_in_controls = [\"mfa\"] # Critical: requires MFA\n }\n}\n```" }, "Recommendation": { "Text": "1. From the Azure Admin Portal dashboard, open Microsoft Entra ID. 2. Click Security in the Entra ID blade. 3. Click Conditional Access in the Security blade. 4. Click Policies in the Conditional Access blade. 5. Click + New policy. 6. Enter a name for the policy. 7. Click the blue text under Users. 8. Under Include, select All users. 9. Under Exclude, check Users and groups. 10. Select users or groups to be exempted from this policy (e.g. break-glass emergency accounts, and non-interactive service accounts) then click the Select button. 11. Click the blue text under Target Resources. 12. Under Include, click the Select apps radio button. 13. Click the blue text under Select. 14. Check the box next to Windows Azure Service Management APIs then click the Select button. 15. Click the blue text under Grant. 16. Under Grant access check the box for Require multifactor authentication then click the Select button. 17. Before creating, set Enable policy to Report-only. 18. Click Create. After testing the policy in report-only mode, update the Enable policy setting from Report-only to On.", diff --git a/tests/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals_test.py b/tests/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals_test.py new file mode 100644 index 0000000000..3909b80568 --- /dev/null +++ b/tests/providers/azure/services/entra/entra_conditional_access_policy_require_mfa_for_admin_portals/entra_conditional_access_policy_require_mfa_for_admin_portals_test.py @@ -0,0 +1,280 @@ +from unittest import mock +from uuid import uuid4 + +from tests.providers.azure.azure_fixtures import DOMAIN, set_mocked_azure_provider + + +class Test_entra_conditional_access_policy_require_mfa_for_admin_portals: + def test_entra_no_subscriptions(self): + entra_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.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + + entra_client.conditional_access_policy = {} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 0 + + def test_entra_tenant_no_policies(self): + entra_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.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + + entra_client.conditional_access_policy = {DOMAIN: {}} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].subscription == f"Tenant: {DOMAIN}" + assert result[0].resource_name == "Conditional Access Policy" + assert result[0].resource_id == "Conditional Access Policy" + assert ( + result[0].status_extended + == "Conditional Access Policy does not require MFA for Microsoft Admin Portals." + ) + + def test_entra_tenant_policy_no_mfa(self): + entra_client = mock.MagicMock + policy_id = str(uuid4()) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + from prowler.providers.azure.services.entra.entra_service import ( + ConditionalAccessPolicy, + ) + + policy = ConditionalAccessPolicy( + id=policy_id, + name="Test Policy", + state="enabled", + users={"include": ["All"]}, + target_resources={"include": ["MicrosoftAdminPortals"]}, + access_controls={"grant": ["grant"]}, + ) + + entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].subscription == f"Tenant: {DOMAIN}" + assert result[0].resource_name == "Conditional Access Policy" + assert result[0].resource_id == "Conditional Access Policy" + assert ( + result[0].status_extended + == "Conditional Access Policy does not require MFA for Microsoft Admin Portals." + ) + + def test_entra_tenant_policy_mfa(self): + entra_client = mock.MagicMock + policy_id = str(uuid4()) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + from prowler.providers.azure.services.entra.entra_service import ( + ConditionalAccessPolicy, + ) + + policy = ConditionalAccessPolicy( + id=policy_id, + name="Test Policy", + state="enabled", + users={"include": ["All"]}, + target_resources={"include": ["MicrosoftAdminPortals"]}, + access_controls={"grant": ["grant", "MFA"]}, + ) + + entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert result[0].subscription == f"Tenant: {DOMAIN}" + assert result[0].resource_name == "Test Policy" + assert result[0].resource_id == policy_id + assert ( + result[0].status_extended + == "Conditional Access Policy requires MFA for Microsoft Admin Portals." + ) + + def test_entra_tenant_policy_mfa_disabled(self): + entra_client = mock.MagicMock + policy_id = str(uuid4()) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + from prowler.providers.azure.services.entra.entra_service import ( + ConditionalAccessPolicy, + ) + + policy = ConditionalAccessPolicy( + id=policy_id, + name="Test Policy", + state="disabled", + users={"include": ["All"]}, + target_resources={"include": ["MicrosoftAdminPortals"]}, + access_controls={"grant": ["grant", "MFA"]}, + ) + + entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].subscription == f"Tenant: {DOMAIN}" + assert result[0].resource_name == "Conditional Access Policy" + assert result[0].resource_id == "Conditional Access Policy" + assert ( + result[0].status_extended + == "Conditional Access Policy does not require MFA for Microsoft Admin Portals." + ) + + def test_entra_tenant_policy_mfa_no_target(self): + entra_client = mock.MagicMock + policy_id = str(uuid4()) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + from prowler.providers.azure.services.entra.entra_service import ( + ConditionalAccessPolicy, + ) + + policy = ConditionalAccessPolicy( + id=policy_id, + name="Test Policy", + state="enabled", + users={"include": ["All"]}, + target_resources={"include": []}, + access_controls={"grant": ["grant", "MFA"]}, + ) + + entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].subscription == f"Tenant: {DOMAIN}" + assert result[0].resource_name == "Conditional Access Policy" + assert result[0].resource_id == "Conditional Access Policy" + assert ( + result[0].status_extended + == "Conditional Access Policy does not require MFA for Microsoft Admin Portals." + ) + + def test_entra_tenant_policy_mfa_no_users(self): + entra_client = mock.MagicMock + policy_id = str(uuid4()) + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_client", + new=entra_client, + ), + ): + from prowler.providers.azure.services.entra.entra_conditional_access_policy_require_mfa_for_admin_portals.entra_conditional_access_policy_require_mfa_for_admin_portals import ( + entra_conditional_access_policy_require_mfa_for_admin_portals, + ) + from prowler.providers.azure.services.entra.entra_service import ( + ConditionalAccessPolicy, + ) + + policy = ConditionalAccessPolicy( + id=policy_id, + name="Test Policy", + state="enabled", + users={"include": []}, + target_resources={"include": ["MicrosoftAdminPortals"]}, + access_controls={"grant": ["grant", "MFA"]}, + ) + + entra_client.conditional_access_policy = {DOMAIN: {policy_id: policy}} + + check = entra_conditional_access_policy_require_mfa_for_admin_portals() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].subscription == f"Tenant: {DOMAIN}" + assert result[0].resource_name == "Conditional Access Policy" + assert result[0].resource_id == "Conditional Access Policy" + assert ( + result[0].status_extended + == "Conditional Access Policy does not require MFA for Microsoft Admin Portals." + )