azure-mi-role-assignment
match (record)
{
"condition": "principalType == \u0027ServicePrincipal\u0027 and principalId matches a MI principalId",
"field": "principalId",
"resource_type": "Microsoft.Authorization/roleAssignments"
}
emit
| source type | ManagedIdentity |
|---|---|
| target type | Role |
| source | <managed identity (resolved from principalId)> |
| target | <role definition at the assigned scope> |
Narrative
Managed identity {mi.name} holds {role.name} at scope {scope} - any bearer token for {mi.name} carries these ARM RBAC permissions.
Raw rule rules/explicit/azure-managedidentity.yaml
id: azure-mi-role-assignment
emits: HasRole
applies_to:
- azure
match_record:
resource_type: Microsoft.Authorization/roleAssignments
field: principalId
condition: principalType == 'ServicePrincipal' and principalId matches a MI principalId
emit:
source_type: ManagedIdentity
target_type: Role
source: <managed identity (resolved from principalId)>
target: <role definition at the assigned scope>
api_source: az role assignment list --assignee <mi-objectId> --all-namespaces
evidence_field: principalId, roleDefinitionId, scope
note: Resolve principalId -> MI node via the Entra SP object (principalType == 'ServicePrincipal', servicePrincipalType
== 'ManagedIdentity'). The scope on the role assignment determines whether this is same-subscription
or cross-subscription (cross-subscription feeds azure-mi-cross-subscription-rbac in managedidentity.yaml).
PIM-eligible assignments (not-active) should be emitted as CONDITIONAL(iam_permission).
narrative: "Managed identity {mi.name} holds {role.name} at scope {scope} \u2014 any bearer token for\
\ {mi.name} carries these ARM RBAC permissions."