# Data Model — Intune RBAC Inventory & Backup v1 (127)

## Entities

### Foundation Type Metadata
Config-defined support metadata for RBAC foundations.

- Source: `config/tenantpilot.php`
- New keys:
  - `intuneRoleDefinition`
  - `intuneRoleAssignment`
- Required attributes:
  - `type`
  - `label`
  - `category = RBAC`
  - `platform = all`
  - `endpoint`
  - `backup`
  - `restore = preview-only`
  - `risk`

### Graph Contract Definition
Config-defined Graph fetch contract for inventory-grade RBAC capture.

- Source: `config/graph_contracts.php`
- New contract keys:
  - `intuneRoleDefinition`
  - `intuneRoleAssignment`
- Required fields:
  - `resource`
  - `allowed_select`
  - `allowed_expand`
  - `type_family` when available
  - optional `hydration` or follow-up fetch hints if full assignment fidelity requires them
- Compatibility rule:
  - Existing `directoryRoleDefinitions` and `rbacRoleAssignment` keys remain unchanged for current health/onboarding flows.

### InventoryItem (tenant-owned observed state)
Represents the latest observed RBAC inventory row per tenant and external object.

- Existing model/table: `InventoryItem`
- Ownership:
  - `workspace_id` NOT NULL
  - `tenant_id` NOT NULL
- Identity:
  - unique by `tenant_id + policy_type + external_id`
- Relevant fields for RBAC:
  - `policy_type` = `intuneRoleDefinition` or `intuneRoleAssignment`
  - `external_id`
  - `display_name`
  - `category = RBAC`
  - `platform = all`
  - `meta_jsonb` sanitized, metadata-only
  - `last_seen_at`
  - `last_seen_operation_run_id`
- `meta_jsonb` for Role Definitions should include only safe observed metadata such as:
  - OData type
  - built-in versus custom state
  - summary counts or identifiers needed for inventory surfaces
- `meta_jsonb` for Role Assignments should include only safe observed metadata such as:
  - linked role definition identifier or display name if available
  - assignment target counts or scope counts
  - warnings and sanitized support-state metadata

### Inventory Coverage Payload
Canonical coverage status written into `OperationRun.context.inventory.coverage.foundation_types`.

- Existing source: `App\Support\Inventory\InventoryCoverage`
- New keys:
  - `foundation_types.intuneRoleDefinition`
  - `foundation_types.intuneRoleAssignment`
- Per-type fields:
  - `status` in `succeeded | failed | skipped`
  - optional `item_count`
  - optional `error_code`

### BackupItem (tenant-owned immutable RBAC payload snapshot)
Represents a captured RBAC foundation snapshot inside a backup set.

- Existing model/table: `BackupItem`
- Ownership:
  - `tenant_id` NOT NULL
  - workspace derived via tenant relationship
- RBAC foundation shape:
  - `policy_id` = synthetic tenant-scoped policy anchor for `intuneRoleDefinition` and `intuneRoleAssignment`
  - `policy_version_id` = immutable snapshot row created or reused for the captured RBAC payload
  - `policy_type` = `intuneRoleDefinition` or `intuneRoleAssignment`
  - `policy_identifier` = source Graph object id
  - `payload` = full immutable RBAC payload
  - `metadata.displayName`
  - `metadata.kind`
  - `metadata.graph.resource`
  - additional metadata warnings or capture details

### Policy / PolicyVersion linkage for RBAC foundations
RBAC foundations reuse the existing policy-version review surfaces by creating synthetic policy anchors.

- Existing models/tables: `Policy`, `PolicyVersion`
- RBAC anchor shape:
  - `Policy.external_id` = RBAC source Graph object id
  - `Policy.policy_type` = `intuneRoleDefinition` or `intuneRoleAssignment`
  - `Policy.metadata.foundation_anchor = true`
  - `Policy.metadata.capture_mode = immutable_backup`
- RBAC version shape:
  - `PolicyVersion.snapshot` = immutable RBAC payload
  - `PolicyVersion.capture_purpose = backup`
  - identical protected snapshots are reused for the same synthetic policy instead of creating duplicate versions

### Normalized RBAC Snapshot View Model
Human-readable representation produced for backup/version display and future diff-safe reuse.

#### Role Definition normalized fields
- `name`
- `description`
- `is_built_in`
- `role_permissions`
  - normalized permission blocks
  - ordered for diff stability
- optional warnings when payload is incomplete or partially expanded

#### Role Assignment normalized fields
- `assignment_name`
- `role_definition`
  - preferred: readable name + stable id
- `members`
  - readable names when known, ids as fallback
- `scope_members`
  - readable names when known, ids as fallback
- `resource_scopes`
  - stable ordered values
- optional warnings when referenced objects are unresolved

### Verification Check Row
Provider or tenant verification result describing RBAC permission readiness.

- Existing source pattern: `TenantPermissionCheckClusters`
- Relevant fields:
  - `key`
  - `title`
  - `status = pass | warn | fail`
  - `severity`
  - `blocking`
  - `reason_code`
  - `message`
  - `evidence[]`
  - `next_steps[]`
- RBAC v1 target behavior:
  - Missing `DeviceManagementRBAC.Read.All` produces a clear warning or failure reason with actionable next steps.
  - The rest of the inventory pipeline remains able to complete partially.

## Relationships

- A `Tenant` has many `InventoryItem` rows, including RBAC foundation rows.
- A `Tenant` has many `BackupSet` rows.
- A `BackupSet` has many `BackupItem` rows, including RBAC foundation payload snapshots.
- A synthetic RBAC `Policy` has many immutable `PolicyVersion` rows and can be referenced by RBAC `BackupItem` rows.
- An `OperationRun` for inventory sync carries coverage status for RBAC foundation types in `context.inventory.coverage.foundation_types`.
- Verification rows and provider reason codes describe permission posture for the same tenant and workspace scope.

## Invariants

- `intuneRoleDefinition` and `intuneRoleAssignment` are separate foundation types everywhere: config, contracts, inventory, backup, coverage, and normalization.
- Inventory rows remain metadata-only and tenant-scoped.
- Full RBAC payloads are stored only in immutable backup/version artifacts.
- Restore mode for both RBAC foundation types is always `preview-only` in v1.
- Non-members must not be able to infer RBAC object existence across workspace or tenant boundaries.
- Missing `DeviceManagementRBAC.Read.All` must produce a stable reason path rather than an opaque exception.
- Role Definitions and Role Assignments must normalize in a stable, ordered, diff-safe manner.

## State Transitions

### Inventory coverage state
- `skipped`
  - when foundations are excluded from a run or a type is not attempted
- `failed`
  - when the RBAC fetch for that type fails or returns a permission/problem reason
- `succeeded`
  - when the RBAC type is fetched and inventory rows are updated successfully

### Backup capture state for RBAC foundations
- not present in backup set
- created as immutable `BackupItem` plus synthetic `Policy` / `PolicyVersion` linkage for version-detail review
- restored from soft-delete if already present in the same backup set and previously archived

### Verification state for RBAC permission readiness
- `pass`
  - all required RBAC read permissions are present
- `warn`
  - delegated refresh or adjacent non-blocking permission posture issue exists
- `fail`
  - required application permission is missing or permission fetch errored in a blocking way

## Validation Rules

- Foundation type config rows must include deterministic `restore` and `risk` metadata.
- RBAC Graph contracts must only request fields allowed by the contract registry.
- Inventory updates require non-empty Graph `id` values.
- Backup foundation snapshots require non-empty `source_id` values.
- Normalizers must preserve identifiers when readable expansions are absent.
