Config Snapshots

Point-in-time backups of a tenant's configuration, section by section, diffable against each other and optionally mirrored into a real git repository. This is a visibility and drift-detection layer, deliberately separate from the Deploy wizard and known-fix Workflows that actually change a tenant -- see "What this deliberately doesn't do" below.

Config Snapshots screen showing run history with git commit shas and a diff between two runs with Modified, Added, and Removed entries.

Sections

A snapshot captures every registered section in one run. Today:

SectionCategoryGraph resource
Conditional Access PoliciesIdentity/identity/conditionalAccess/policies
Named LocationsIdentity/identity/conditionalAccess/namedLocations
Device Compliance PoliciesDevices/deviceManagement/deviceCompliancePolicies

Each captures the full object, minus createdDateTime/modifiedDateTime-style timestamps (they change on every edit regardless of whether anything meaningful did, which would otherwise show up as permanent noise in every diff). Adding a section is a new IConfigSection implementation registered through DI -- same pattern as adding a known-fix Workflow -- with no controller or diff-engine change needed.

Diffing

Any two snapshot runs can be diffed, either one section at a time or across the whole tenant. Each item is matched by its Graph id; the diff reports items added, removed, or modified (with the specific top-level fields that changed). A run's history is the backup: taking a new snapshot before a risky change gives you a known-good point to diff back against afterward.

Workbooks: export and import

A single run, or a diff between two runs, can be exported as a portable file -- a run exports as JSON, a diff exports as readable patch-style text (*.patch) suitable for pasting into a ticket. An exported run can be re-imported (on this instance or a different one) to create a new snapshot record from already-captured data, purely for comparison.

What this deliberately doesn't do: there is no "apply this diff/workbook to a tenant" endpoint, and import never issues a single Graph write. A generic "replay this JSON at a tenant" path would be an unreviewable, un-auditable way to mutate a customer's identity and device configuration. Making changes stays the job of the Deploy wizard and the known-fix Workflows, where every write is a single, named, reviewed action with its own diagnose/fix/verify loop. Config Snapshots is the read side; those are the write side. Kept separate on purpose.

Git sync (optional)

Set GitSync:RepoUrl and every successful capture also gets committed and pushed to a real git repository -- GitHub or any host that speaks standard HTTPS git. Each section becomes one file under tenants/{tenant}/{section}.json; a capture with no changes since the last one makes no commit. This turns tenant configuration into real version-controlled history: diffable and blameable with any git tool, browsable on GitHub, not just inside the app.

Implemented by shelling out to the git CLI (the same out-of-process pattern PwshRunner uses for Exchange Online), not a native git library -- one well-known tool already in the container image, not a new native-binary compatibility surface. The auth token is sent as a per-request HTTP header and never written to the working copy's git config or embedded in the remote URL, so it never touches disk.

KeyNotes
GitSync:RepoUrle.g. https://github.com/your-org/tenant-config-history.git. Unset = disabled (default).
GitSync:BranchDefault main.
GitSync:TokenSecret. A token with push access (a GitHub PAT with repo scope, or equivalent).
GitSync:CommitterName / CommitterEmailAttribution on the commits this app makes.
GitSync:LocalPathWorking copy on disk. Default /git-sync -- mount a persistent volume there in production, same as /keys and /packages.

Access control

Follows the same per-tenant roles as everything else (see Authentication): Viewer can list runs and view/export diffs; Operator can also take a new snapshot or import a workbook. There is no separate permission model for this feature.