Back to Checks
m365

User is MFA capable

entra_users_mfa_capable
Service: entraSeverity: high
OfficialBuilt-inVerified
Categories:identity-accesse3
Resource Type:NotDefined

Microsoft Entra users have a registered and enabled multifactor authentication method (MFA capable). The evaluation targets enabled accounts and identifies those lacking any usable second factor.

Risk

Without MFA, accounts are vulnerable to phishing, password spraying, and credential reuse, enabling takeover. Attackers can access mail and files, change settings, and move laterally, harming confidentiality, integrity, and availability of M365 resources.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler m365 --checks entra_users_mfa_capable

Recommendation

Enforce MFA for all enabled users, prioritizing phishing-resistant methods (FIDO2/passkeys/CBA) and limiting SMS/voice. Apply least privilege and require MFA for privileged roles. Require registration during onboarding and routinely review coverage to sustain defense-in-depth.

Remediation

CLI

New-MgUserAuthenticationPhoneMethod -UserId <USER_UPN> -PhoneType mobile -PhoneNumber "+15555550100"

Other

1In the Microsoft Entra admin center, go to Entra ID > Users
2Select the user marked as not MFA capable
3Select Authentication methods > + Add authentication method
4Choose Phone number, enter the number in E.164 format (e.g., +15555550100), and select Add
5Repeat for each failing user

Check Code

from datetime import datetime, timezone
from typing import List

from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.entra.entra_client import entra_client


class entra_users_mfa_capable(Check):
    """
    Ensure all member users are MFA capable.

    This check verifies if member users are MFA capable, aligning with CIS
    Microsoft 365 Foundations Benchmark recommendation 5.2.3.4
    ("Ensure all member users are 'MFA capable'").

    Guest users, disabled accounts, and future hires are excluded from the
    evaluation.

    - PASS: The member user is MFA capable.
    - FAIL: The member user is not MFA capable.
    - MANUAL: Users or their registration details could not be read
      (insufficient permissions), so MFA capability cannot be verified.
    """

    def execute(self) -> List[CheckReportM365]:
        """
        Execute the MFA capable check for all enabled member users.

        Iterates over the users retrieved from the Entra client and generates a report
        indicating if member users are MFA capable. Users explicitly typed as ``Guest``
        and disabled accounts are skipped, in line with the CIS recommendation that
        scopes the control to member users only. Users whose ``user_type`` could not
        be determined are still evaluated to avoid silently dropping accounts when
        Microsoft Graph does not return the property.

        Returns:
            List[CheckReportM365]: A list with one report per evaluated user.
        """
        findings = []

        data_error = (
            entra_client.users_error or entra_client.user_registration_details_error
        )
        if data_error:
            report = CheckReportM365(
                metadata=self.metadata(),
                resource={},
                resource_name="Entra Users",
                resource_id="users",
            )
            report.status = "MANUAL"
            report.status_extended = (
                f"Cannot verify MFA capability for member users: {data_error}."
            )
            findings.append(report)
            return findings

        for user in entra_client.users.values():
            if user.user_type == "Guest" or not user.account_enabled:
                continue
            if user.employee_hire_date:
                employee_hire_date = user.employee_hire_date
                if (
                    employee_hire_date.tzinfo is None
                    or employee_hire_date.utcoffset() is None
                ):
                    employee_hire_date = employee_hire_date.replace(tzinfo=timezone.utc)
                if employee_hire_date > datetime.now(timezone.utc):
                    continue

            report = CheckReportM365(
                metadata=self.metadata(),
                resource=user,
                resource_name=user.name,
                resource_id=user.id,
            )

            if not user.is_mfa_capable:
                report.status = "FAIL"
                report.status_extended = f"User {user.name} is not MFA capable."
            else:
                report.status = "PASS"
                report.status_extended = f"User {user.name} is MFA capable."

            findings.append(report)

        return findings