azure-msgraph-group-add-member

A service principal holding Group.ReadWrite.All, GroupMember.ReadWrite.All, or Directory.ReadWrite.All can add any principal (including itself) to any Entra group, inheriting that group's RBAC role assignments, Graph app role assignments, and any other entitlements the group carries.

derived azure emits CanAddMember

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'sp': None} HasRole {'approle': None} {'group': None} HasRole {'group_role': None}

where

node_type(?sp) in [MachineIdentity, ApplicationIdentity] ?approle is one of: Group.ReadWrite.All (id: 62a82d76-70ea-41e2-9197-370581804d09) OR GroupMember.ReadWrite.All (id: dbaae8cf-10b5-4b86-a4a1-f871c94c6695) OR Directory.ReadWrite.All (id: 19dbc75e-c2e2-444c-a770-ec69d8559fc7) on microsoft.graph SP node_type(?group) == Group ?group grants meaningful access (carries an RBAC role assignment at any scope, an appRoleAssignment, or is a role-assignable group associated with a directory role) — emit only for high-value groups to avoid excessive fan-out; analysts may enumerate all groups

emit

source typeIdentity
target typeGroup
source?sp
target?group
permissionsGroup.ReadWrite.All GroupMember.ReadWrite.All Directory.ReadWrite.All
conditionsiam_permission service_state
state logicACTIVE when the appRoleAssignment for Group.ReadWrite.All, GroupMember.ReadWrite.All, or Directory.ReadWrite.All is granted and the SP credential is valid. CONDITIONAL(service_state) if the SP credential is expired. BLOCKED for dynamic-membership groups (membershipType == Dynamic — members cannot be added manually; they are determined by the membershipRule). BLOCKED if the group is a mail-enabled security group that prohibits member modification via Graph in some configurations. CONDITIONAL(role_compatibility) for role-assignable groups (isAssignableToRole == true) in tenants with privileged group management (Entra ID Governance) — those groups may require PIM approval for membership changes (CONDITIONAL(pim_activation)). CONDITIONAL(role_compatibility) confidence: 0.85 when matched via Directory.ReadWrite.All (rather than the more specific Group.ReadWrite.All / GroupMember.ReadWrite.All), since Directory.ReadWrite.All's exact scope for per-group member operations should be validated against Microsoft docs.

Narrative

{sp.name} holds Group.ReadWrite.All (or Directory.ReadWrite.All) and can add any principal (including controlled SPs or users) to {group.name} via POST /groups/{id}/members/$ref, inheriting whatever RBAC role assignments, Graph app roles, or directory role associations that group carries.

Raw rule rules/derived/azure/msgraph.yaml

id: azure-msgraph-group-add-member
emits: CanAddMember
description: A service principal holding Group.ReadWrite.All, GroupMember.ReadWrite.All, or Directory.ReadWrite.All
  can add any principal (including itself) to any Entra group, inheriting that group's RBAC role assignments,
  Graph app role assignments, and any other entitlements the group carries.
match:
- - sp: null
  - HasRole
  - approle: null
- - group: null
  - HasRole
  - group_role: null
where:
- node_type(?sp) in [MachineIdentity, ApplicationIdentity]
- '?approle is one of: Group.ReadWrite.All (id: 62a82d76-70ea-41e2-9197-370581804d09) OR GroupMember.ReadWrite.All
  (id: dbaae8cf-10b5-4b86-a4a1-f871c94c6695) OR Directory.ReadWrite.All (id: 19dbc75e-c2e2-444c-a770-ec69d8559fc7)
  on microsoft.graph SP'
- node_type(?group) == Group
- "?group grants meaningful access (carries an RBAC role assignment at any scope, an appRoleAssignment,\
  \ or is a role-assignable group associated with a directory role) \u2014 emit only for high-value groups\
  \ to avoid excessive fan-out; analysts may enumerate all groups"
emit:
  source_type: Identity
  target_type: Group
  source: ?sp
  target: ?group
  permissions:
  - Group.ReadWrite.All
  - GroupMember.ReadWrite.All
  - Directory.ReadWrite.All
  conditions:
  - iam_permission
  - service_state
  state_logic: "ACTIVE when the appRoleAssignment for Group.ReadWrite.All, GroupMember.ReadWrite.All,\
    \ or Directory.ReadWrite.All is granted and the SP credential is valid. CONDITIONAL(service_state)\
    \ if the SP credential is expired. BLOCKED for dynamic-membership groups (membershipType == Dynamic\
    \ \u2014 members cannot be added manually; they are determined by the membershipRule). BLOCKED if\
    \ the group is a mail-enabled security group that prohibits member modification via Graph in some\
    \ configurations. CONDITIONAL(role_compatibility) for role-assignable groups (isAssignableToRole ==\
    \ true) in tenants with privileged group management (Entra ID Governance) \u2014 those groups may\
    \ require PIM approval for membership changes (CONDITIONAL(pim_activation)). CONDITIONAL(role_compatibility)\
    \ confidence: 0.85 when matched via Directory.ReadWrite.All (rather than the more specific Group.ReadWrite.All\
    \ / GroupMember.ReadWrite.All), since Directory.ReadWrite.All's exact scope for per-group member operations\
    \ should be validated against Microsoft docs."
  confidence: '0.88'
  derived_from:
  - <HasRole edge_id (appRoleAssignment Group.ReadWrite.All, GroupMember.ReadWrite.All, or Directory.ReadWrite.All
    on ?sp)>
  - "azure/identity-escalation.yaml#azure-group-owner-add-member (group-owner object path \u2014 parallel;\
    \ this covers the app-permission path)"
  false_positive_note: "Dynamic-membership groups (membershipType == Dynamic) cannot have members added\
    \ via POST /members/$ref \u2014 the Graph API returns an error. Always check the group's membershipRule\
    \ property; if non-null, this edge is BLOCKED for that group. Role-assignable groups (isAssignableToRole\
    \ == true) may be governed by PIM approval workflows in Entra ID P2 tenants \u2014 flag CONDITIONAL(pim_activation)\
    \ when PIM policies are detected on the group. Group.ReadWrite.All is a superset of GroupMember.ReadWrite.All;\
    \ both yield the member-add capability but Group.ReadWrite.All also permits group creation and deletion\
    \ (not additionally modeled here). Directory.ReadWrite.All covers /groups/{id}/members operations\
    \ per section 4 of msgraph.md (seed docs) but its exact scope is narrower than some other directory\
    \ operations \u2014 apply reduced confidence (0.85) and flag CONDITIONAL for Directory.ReadWrite.All\
    \ matches. Limit graph fan-out: emit per group only when that group can be shown to carry privilege\
    \ (RBAC role, app role, or role-assignable group status); open-ended emission against all groups inflates\
    \ the graph without attack-path value."
  narrative: '{sp.name} holds Group.ReadWrite.All (or Directory.ReadWrite.All) and can add any principal
    (including controlled SPs or users) to {group.name} via POST /groups/{id}/members/$ref, inheriting
    whatever RBAC role assignments, Graph app roles, or directory role associations that group carries.'
move · open · esc close