# Research: SoT Foundations & Assignments (006) This document resolves planning unknowns and records decisions for implementing foundations-first backup/restore and assignment-aware restore. ## Decision: Foundation object endpoints and permissions - **Decision**: Implement “foundation” backup/restore for: - Assignment Filters via `deviceManagement/assignmentFilters` (permission: `DeviceManagementConfiguration.ReadWrite.All`). - Scope Tags via `deviceManagement/roleScopeTags` (permission: `DeviceManagementRBAC.ReadWrite.All`). - Notification Message Templates via `deviceManagement/notificationMessageTemplates` (permission: `DeviceManagementServiceConfig.ReadWrite.All`, with `localizedNotificationMessages` treated as a future enhancement). - **Rationale**: These are explicitly called out as SoT foundations and appear as dependencies in the IntuneManagement reference implementation. - **Alternatives considered**: - Treat foundations as “manual prerequisites” only (no backup/restore) → rejected because it blocks safe assignment restore. - Store only names (no full payload) → rejected because restore needs full object definitions. ## Decision: Assignment apply mechanism (Graph) - **Decision**: Apply assignments using a per-resource `.../{id}/assign` Graph action (default), with request body shape: ```json { "assignments": [ { "target": { "@odata.type": "...", "groupId": "...", "deviceAndAppManagementAssignmentFilterId": "...", "deviceAndAppManagementAssignmentFilterType": "Include|Exclude" } } ] } ``` and support type-specific overrides if needed. - **Rationale**: Matches the IntuneManagement import approach and aligns with SoT “apply assignments after foundations exist”. - **Alternatives considered**: - PATCH the resource with an `assignments` property → rejected because many Intune resources do not support assignment updates via PATCH. - Only restore object payloads, never assignments → rejected (SoT requires assignment-aware restore). ## Decision: Mapping strategy (deterministic, safe) - **Decision**: Produce and persist an “old → new” mapping for foundation objects by matching primarily on `displayName` (or name-equivalent), with collision handling: - If a unique match exists in the target tenant by name: reuse (map old → existing). - If no match exists: create (map old → created). - If multiple matches exist: create a copy with a predictable suffix and record “created_copy” in the report. - **Rationale**: SoT requires determinism and auditability; mapping by opaque IDs is impossible across tenants. - **Alternatives considered**: - Always create new objects regardless of matches → rejected due to duplication and name collision risk. - Hash-based matching (normalize and compare multiple fields) → deferred; start with name-based plus explicit collision handling. ## Decision: Where to store mappings and restore decision report - **Decision**: Store mapping + decisions in `restore_runs.preview` (dry-run) and `restore_runs.results` (execute), optionally mirrored into `restore_runs.metadata` for fast access. - **Rationale**: The schema already supports JSON `preview`/`results`; this keeps the first iteration simple and audit-friendly. - **Alternatives considered**: - Dedicated `restore_mappings` table → deferred until querying/reporting requirements demand it. ## Decision: How to represent foundation snapshots in storage - **Decision**: Store foundation snapshots as `backup_items` rows with: - `policy_id = null` - `policy_type` set to a dedicated type key (e.g. `assignmentFilter`, `roleScopeTag`, `notificationMessageTemplate`) - `policy_identifier` set to the Graph object `id` - `payload` containing the raw Graph resource - `metadata` containing normalized identifiers used for matching (e.g. `displayName`). - **Rationale**: `backup_items.policy_id` is nullable; reusing the same snapshot container avoids schema churn. - **Alternatives considered**: - New “foundation_snapshots” table → rejected for MVP due to extra migrations and duplication. ## Decision: Conditional Access restore behavior (safety) - **Decision**: Keep Conditional Access restore as **preview-only**, even in execute mode. - **Rationale**: CA depends on identity objects (e.g., named locations) and is security-critical; SoT explicitly allows preview-first for risky items. - **Alternatives considered**: - Allow CA restore with best-effort group/user mapping → rejected as too risky without complete dependency mapping. ## Decision: Scope for assignment-aware restore (initial) - **Decision**: Apply assignment mapping for existing supported configuration objects (policy types already in `config/tenantpilot.php`), focusing first on targets that include: - group targeting (`groupId`) - assignment filters (`deviceAndAppManagementAssignmentFilterId`/Type) - role scope tags (`roleScopeTagIds`) where applicable. - **Rationale**: Incrementally delivers value without requiring support for all object classes in SoT. - **Alternatives considered**: - Expand to named locations / terms of use / authentication strengths immediately → deferred (separate dependency set).