# 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).
