azure-sub-move-out-of-guardrail

A principal who can move a resource/RG/subscription out of a guardrail's scope escapes the deny assignment without editing it.

derived azure emits CanModifyConfiguration

match

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

{'principal': None} HasPermission {'scope_binding': None}

where

?principal holds ONE OF: Microsoft.Resources/subscriptions/resourceGroups/moveResources/action (move a resource to an RG the guardrail does not cover); Microsoft.Resources/subscriptions/resourceGroups/write (create/repoint an RG outside scope); Microsoft.Management/managementGroups/subscriptions/write (move the subscription to an MG without the deny assignment) the target guardrail's scope does NOT cover the destination (else the move does not escape it)

emit

source typeIdentity
target type*
source?principal
target?scope_binding
permissionsMicrosoft.Resources/subscriptions/resourceGroups/moveResources/action Microsoft.Management/managementGroups/subscriptions/write
conditionsdestination_outside_guardrail
state logicCONDITIONAL(destination_outside_guardrail): only escapes the guardrail if a destination scope exists that the deny assignment/policy does not also cover (a tenant-root deny assignment cannot be escaped by moving within the tenant). ACTIVE only when such a destination is confirmed. BLOCKED if the move action itself is denied.

Narrative

{principal.name} can move the affected resource/subscription out of the guardrail {scope_binding.name}'s scope, escaping the deny assignment without editing it.

Raw rule rules/derived/azure/subscriptions.yaml

id: azure-sub-move-out-of-guardrail
emits: CanModifyConfiguration
description: A principal who can move a resource/RG/subscription out of a guardrail's scope escapes the
  deny assignment without editing it.
match:
- - principal: null
  - HasPermission
  - scope_binding: null
where:
- '?principal holds ONE OF: Microsoft.Resources/subscriptions/resourceGroups/moveResources/action (move
  a resource to an RG the guardrail does not cover); Microsoft.Resources/subscriptions/resourceGroups/write
  (create/repoint an RG outside scope); Microsoft.Management/managementGroups/subscriptions/write (move
  the subscription to an MG without the deny assignment)'
- the target guardrail's scope does NOT cover the destination (else the move does not escape it)
emit:
  source_type: Identity
  target_type: '*'
  source: ?principal
  target: ?scope_binding
  permissions:
  - Microsoft.Resources/subscriptions/resourceGroups/moveResources/action
  - Microsoft.Management/managementGroups/subscriptions/write
  conditions:
  - destination_outside_guardrail
  state_logic: 'CONDITIONAL(destination_outside_guardrail): only escapes the guardrail if a destination
    scope exists that the deny assignment/policy does not also cover (a tenant-root deny assignment cannot
    be escaped by moving within the tenant). ACTIVE only when such a destination is confirmed. BLOCKED
    if the move action itself is denied.'
  confidence: '0.7'
  derived_from:
  - <HasPermission edge_id (moveResources/action OR managementGroups/subscriptions/write @scope)>
  note: Provides the OPTIONAL move-out branch of hierarchy-chains guardrail-removal-upgrades-blocked (its
    optional CanModifyConfiguration clause). Either this or azure-sub-modify-deny-assignment suffices
    to trigger the BLOCKED->ACTIVE upgrade there.
  false_positive_note: "Moving a resource does not escape a guardrail whose scope also covers the destination\
    \ \u2014 a subscription-wide or root-MG deny assignment is not escaped by an in-subscription move.\
    \ Verify the destination is genuinely outside scope; otherwise do not treat as a removal path. moveResources\
    \ also requires write on both source and destination and may be blocked by resource-type move constraints."
  narrative: '{principal.name} can move the affected resource/subscription out of the guardrail {scope_binding.name}''s
    scope, escaping the deny assignment without editing it.'
move · open · esc close