The Problem
A department's files were migrated from a network share to SharePoint, but the team needed time to adjust to working inside SharePoint rather than a mapped drive. Forcing an immediate cutover risked resistance and mistakes, while allowing continued use of the share risked the real problem: files existing in two places, diverging into conflicting versions with no single source of truth. The migration needed to succeed without making adoption a hard deadline.
What I Built
A continuous one-way sync that watches the network share and uploads new and modified files to the SharePoint document library, preserving folder structure exactly. Staff could keep saving where they were comfortable, and anything they saved locally landed in SharePoint automatically. This turned a hard cutover into a gradual transition, with SharePoint always holding the current copy.
How It Works
- Runs as a scheduled task every five minutes, so changes propagate quickly enough that SharePoint is never meaningfully behind
- Certificate-based app-only authentication to both SharePoint (via PnP PowerShell) and Microsoft Graph, so it runs unattended with no stored secrets
- Signature-based change detection: each file is tracked by path, last-write time, and size. Files whose signature is unchanged since the last successful upload are skipped, making every run idempotent and safe to repeat.
- Persistent JSON state file carries upload history and failure counts across runs
- Locked-file handling: before uploading, the script attempts an exclusive read. If a user has the document open, the file is skipped and retried on the next run rather than failing the sync. On a share people work in all day, this matters.
- Temp file filtering excludes Office lock files and partial-write artifacts that should never reach SharePoint
- Folder structure is mirrored exactly, so the SharePoint library matches the source tree rather than flattening it
- Failure escalation with noise control: a file must fail three consecutive runs before an email alert is sent, and the counter resets on success. Transient failures resolve themselves silently; only genuinely stuck files generate a notification.
- Per-run logging with timestamps and severity levels
A Note on State Serialization
PowerShell's ConvertFrom-Json returns PSCustomObject rather than a hashtable, which does not support ContainsKey, Remove, or index assignment. Round-tripping sync state through JSON therefore breaks hashtable operations on load. The script normalizes deserialized state back into real hashtables, which keeps the state file both human-readable and safe to operate on.
One-Way by Design
The sync is deliberately one directional, from the file share to SharePoint. A reverse path was never needed, and omitting it removes an entire class of conflict-resolution problems.
Impact
- Completed the migration with no data loss and no version conflicts between the share and SharePoint
- Let the department adopt the new platform gradually instead of on a forced cutover date
- Still running today, though it does progressively less work as the team increasingly works directly in SharePoint