prowler-cloud/prowler

[New Check]: Conditional Access groups must be protected by RMAU or role-assignable groups

Open

#11060 opened on May 6, 2026

View on GitHub
 (2 comments) (0 reactions) (0 assignees)Python (8,957 stars) (1,322 forks)batch import
feature-requestgood first issuenew-checkprovider/m365

Description

Existing check search

  • I have searched existing issues, Prowler Hub, and the public roadmap, and this check does not already exist.

Provider

Microsoft 365

New provider name

No response

Service or product area

entra

Suggested check name

entra_conditional_access_policy_groups_management_restricted

Context and goal

  • Security condition to validate: Every security group referenced (in includeGroups or excludeGroups) by an enabled or report-only Conditional Access policy must be protected by at least one of these mechanisms:
    • Restricted Management Administrative Unit membership (isManagementRestricted = true), or
    • Role-assignable group (isAssignableToRole = true).
  • Why it matters: Groups that drive Conditional Access decisions are a privileged surface. If they are not management-restricted nor role-assignable, any user with Group.ReadWrite.All (Groups Administrator, User Administrator, app owners, etc.) can change membership and silently bypass or weaken Conditional Access — adding themselves to an exclusion group or removing themselves from an inclusion group. RMAU and role-assignable groups limit who can modify membership to privileged role holders only.
  • Resource involved: Microsoft Entra Conditional Access policies and the security groups they reference.

Expected behavior

  • Resource or scope to evaluate: All Conditional Access policies whose state is enabled or enabledForReportingButNotEnforced. For each, collect every group ID listed under conditions.users.includeGroups and conditions.users.excludeGroups, deduplicate, and resolve each group via Microsoft Graph.
  • PASS when: every resolved group satisfies isManagementRestricted = true OR isAssignableToRole = true. Also PASS when no enabled/report-only policy references any group.
  • FAIL when: at least one group used by an enabled/report-only Conditional Access policy has both isManagementRestricted = false and isAssignableToRole = false. The finding should list the unprotected group(s) and the policies that reference them, distinguishing inclusion vs exclusion usage.
  • MANUAL when: not applicable.
  • Exclusions / edge cases:
    • Skip the check when the tenant has no Microsoft Entra ID P1/P2 license (Conditional Access requires it).
    • Group IDs that no longer resolve in Graph (deleted groups) should be reported separately as a configuration anomaly, but should not silently pass.

References

Suggested severity

High

Additional implementation notes

  • Existing patterns to follow: Prowler already iterates Conditional Access policies in checks like entra_conditional_access_policy_mfa_enforced_for_guest_users and entra_legacy_authentication_blocked (see prowler/providers/m365/services/entra/). Reuse that pattern.
  • Service change (entra_service.py): the existing ConditionalAccessPolicy model exposes includeGroups / excludeGroups, but the Group enrichment with isManagementRestricted and isAssignableToRole does not exist yet. Extend the service to fetch these properties via Graph v1.0 (GET /groups/{id}?$select=id,displayName,isAssignableToRole,isManagementRestricted). Note that isManagementRestricted requires $select to be returned.
  • Permissions / scopes: No additional permissions beyond Prowler's M365 baseline (Directory.Read.All, Policy.Read.All). The Microsoft Graph endpoints used by this check (Conditional Access policies, group reads, administrative-unit membership) are all accepted under those baseline scopes.
  • PowerShell is NOT needed; the check uses the Microsoft Graph API consistently with all other CA checks.
  • Metadata: follow the M365 metadata schema used by sibling checks under entra/.

Contributor guide