azure-sub-contributor-administers
Contributor at a subscription/RG can create, modify, and delete resources under it, but cannot assign RBAC.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_class(?scope) == AdministrativeBoundary and node_type(?scope) in [
Subscription, ResourceGroup]
?principal has EFFECTIVE actions '*' at ?scope MINUS Microsoft.Authorization/*/write and .../elevateAccess/action (Contributor b24988ac-6180-42a0-ab88-20f7382dd24c)
?principal does NOT hold Microsoft.Authorization/roleAssignments/write at ?scope
emit
| source type | Identity |
|---|---|
| target type | * |
| source | ?principal |
| target | ?scope |
| permissions | * |
| conditions | deny_assignment condition_expression |
| state logic | ACTIVE when the Contributor assignment is unconditional and active; CONDITIONAL(condition_expression) with an ABAC condition or CONDITIONAL(pim_activation) for an eligible PIM assignment; BLOCKED where a deny assignment / deny Azure Policy covers the write actions at/above ?scope (honoring excludePrincipals / doNotApplyToChildScopes). Upgrade of a BLOCKED edge is deferred to hierarchy-chains guardrail-removal-upgrades-blocked. |
Narrative
{principal.name} holds Contributor at {scope.name} and can create, modify, and delete resources under it (but cannot assign RBAC, so cannot self-grant Owner).
Raw rule rules/derived/azure/subscriptions.yaml
id: azure-sub-contributor-administers
emits: CanAdminister
description: Contributor at a subscription/RG can create, modify, and delete resources under it, but cannot
assign RBAC.
match:
- - principal: null
- HasPermission
- scope: null
where:
- node_class(?scope) == AdministrativeBoundary and node_type(?scope) in [Subscription, ResourceGroup]
- ?principal has EFFECTIVE actions '*' at ?scope MINUS Microsoft.Authorization/*/write and .../elevateAccess/action
(Contributor b24988ac-6180-42a0-ab88-20f7382dd24c)
- ?principal does NOT hold Microsoft.Authorization/roleAssignments/write at ?scope
emit:
source_type: Identity
target_type: '*'
source: ?principal
target: ?scope
permissions:
- '*'
conditions:
- deny_assignment
- condition_expression
state_logic: ACTIVE when the Contributor assignment is unconditional and active; CONDITIONAL(condition_expression)
with an ABAC condition or CONDITIONAL(pim_activation) for an eligible PIM assignment; BLOCKED where
a deny assignment / deny Azure Policy covers the write actions at/above ?scope (honoring excludePrincipals
/ doNotApplyToChildScopes). Upgrade of a BLOCKED edge is deferred to hierarchy-chains guardrail-removal-upgrades-blocked.
confidence: '0.9'
derived_from:
- <HasPermission edge_id (Contributor / '*' minus Authorization/*/write @scope)>
false_positive_note: "CRITICAL: Contributor CANNOT assign RBAC \u2014 never emit CanGrantPermission\
\ or Controls for it. It yields management-plane control over resources (create/ modify/delete/deploy)\
\ that inherits down the tree, but it cannot self-escalate to Owner via role assignment. Its only\
\ escalation is Phase-4 deploy-with-MI (see vm.yaml/functions.yaml), NOT modelled here. Do not fire\
\ if the principal also holds roleAssignments/write (that is Owner/UAA -> the grant rules)."
narrative: '{principal.name} holds Contributor at {scope.name} and can create, modify, and delete resources
under it (but cannot assign RBAC, so cannot self-grant Owner).'