Johan Sandoval 41ff9e2b49 Demo: migrations follow the schedule, worked by a simulated team
Each device now migrates on the day of its date tag instead of on a
looping 24-hour clock: earlier days are finished, today's devices go
through backup and restore during working hours, later days haven't
started. The few that don't finish have a cause the dashboard shows
(broken client, broken WMI, low disk, a USMT error, unreachable PC).

A simulated team keeps two weeks of working days tagged. During working
hours it adds devices, reschedules or drops one, tags stuck PCs with
their problem, then fixes them and moves them to a new day; each morning
finished days are closed out and a new day is scheduled. Actions print
in the worker terminal as [Team] lines. Settings live in Demo.Team.

The visitor sandbox now keeps per-tag edits laid over the live roster,
so the team's changes keep showing around a visitor's own edits.

Overview calendar: today is a filled badge on the date number, so it no
longer looks like the selected day (outline plus tint).
2026-10-07 20:41:07 +00:00
2026-10-07 17:07:51 +00:00

USMT Dash

A real-time migration dashboard for Windows device-refresh projects. It tracks every old-to-new PC migration driven by ConfigMgr (SCCM) and USMT: who's online, how much disk is free, where each backup and restore is, and which specialty apps still need to go onto the new machine.

It's written in PowerShell end to end: an HttpListener web server with a JSON API, a polling worker that queries SCCM's SQL database, Active Directory and the devices themselves, and a single-file HTML/JS front end. It needs no web framework, database server or runtime beyond PowerShell.

Live demo: the hosted demo runs the real server and worker in a container against a simulated 200-device fleet. Open the Terminals panel at the bottom of the page to watch them work.

Overview page

Why it exists

On a large hardware refresh, a technician checking one device in the ConfigMgr console (the association, whether the backup started, whether the box is even on the network) took about a minute. USMT Dash brings that down to under a second for every device at once, and gives a team a shared view of the whole refresh.

Built and run during a real enterprise Windows 11 refresh, where the team's daily build rate went from about 10 to 25 devices a day and the project closed out about 1,900 machines against a 1,300-device scope.

Features

  • Overview: the landing page, built to drive a refresh's open issues to zero. Counters for offline sources, not started, Bad Client and Broken WMI tags, devices missing from AD / SCCM / DNS, low disk, backup errors and undocumented apps; overall progress; devices per Backup Status; a building x floor map of what's left; and a calendar of the deployment-date tags showing how far each day's devices have got. Every number opens the devices behind it.
  • Tracked Devices: technicians register devices under tags (a name, a date batch, a department). The worker only sweeps tagged devices. Tags can be flagged Skip USMT for inventory-only tracking, and SOURCE, DEST lines create manual pairs for hardware exceptions.
  • USMT Status: every migration grouped by state (Not Started, Backup In Progress, Backup Completed, Restore In Progress, Completed, Not Associated), with ping, free space and the latest scanstate/loadstate log line. Each column can be sorted, filtered and copied.
  • Smart status engine: combines the ConfigMgr MigrationStatus with whether the USMT progress log in SMSTSLog is actively being written. Completed pairs are cached and skip network checks on later cycles. A reverted association resets the cache.
  • Network checks that tell the truth: resolves all A records, pings each one, and validates reverse DNS. This separates Online, Timed Out, Not Found and DNS Issue (a stale record pointing at a different machine).
  • Missing software: compares installed software on source and destination, filters out the baseline image using a pattern list, groups multi-part products into suites, and lists the specialty apps not yet on the new device.
  • Device details: AD OU and groups, hardware, disk, RAM, monitors, client health, location, department, users, for both sides of a migration. A blank says why: Not in AD, Not in SCCM, No DNS record, or Waiting for worker... if it hasn't been looked up yet. Open dialogs refresh in place as new data arrives.
  • Presence: every ping is recorded, so each device shows when it was last online and how many days it has been offline. Completed pairs and Skip USMT devices, which skip network checks in the sweep, are pinged hourly during business hours and at midnight to keep theirs current.
  • Snapshots: the last free space and scanstate / loadstate lines seen while a device was reachable are kept with their timestamps, and shown for completed migrations and offline devices instead of blanks.
  • Excel-style filters on every table: sort, search and tick values; list columns match any, only or exactly the ticked items. On software columns, Documented + Only ticked shows devices whose apps all have a documentation article, and Easiest first sorts by how many apps still lack one.
  • Phones and tablets: tables become cards with one Filter & sort button, and a floating Jump button finds a device by name (scrolls to its card without filtering) or jumps to any section, the top or the bottom of the page.
  • Custom View: build your own table from any field (including days offline, last online, found in AD/SCCM/DNS and monitors), with drag-to-reorder, resizable and Excel-style filterable columns. Saved per browser.
  • Workbench: personal "stacks" of devices for the cart in front of you.
  • Knowledge base: Markdown articles with an editor, image manager, badges, alerts and click-to-copy links. Each article has its own address (/docs/Adobe Acrobat Pro); the list is searchable, counted and split into Applications and Troubleshooting & Guides; PDF export and print. Renaming an image rewrites every article that links to it. Articles named after an app are linked from that app in the software view.
  • Live, on a schedule: the worker cycles every few minutes during business hours, sleeps overnight, and makes a midnight "pitstop" for the daily software refresh. While a cycle runs, the rows swept so far are published every few seconds, so the dashboard updates live; newly tracked devices are swept and published first. Force Update and Force Full Refresh skip the wait, and the sync dialog shows how long each kind of device (SCCM association, manual pair, completed snapshot...) took.
  • Every page has an address (/status, /devices, /docs/Adobe Acrobat Pro, ...), so links, bookmarks and the back button work. The logo goes home to the Overview.
USMT Status Terminals
Missing software Custom View
Device details Documentation

Architecture

Architecture

The server and worker never talk to each other directly. Data\ is the entire interface: the worker writes JSON state files, the server serves them, and the dashboard's "force refresh" buttons drop flag files that the worker watches for.

Component What it does
USMT-Server.ps1 System.Net.HttpListener server: static files, a JSON API (17 routes), IP allowlist, gzip
USMT-Worker.ps1 Polling loop: builds the working set from the tags, sweeps each device, works out its status, writes Data\*.json
Get-Software.ps1 Per-device installed-software lookup with a 24-hour cache (used by the worker and the API)
Providers\SCCM.ps1 Real data source: ConfigMgr module, site SQL database, ADSI, DNS/ICMP, admin shares
Providers\Demo.ps1 Simulated data source used by the demo (see below)
Config\usmt-dash.psd1 Every environment-specific setting
web\index.html The dashboard (HTML/JS, about 4,600 lines)
web\demo\demo.js Terminal panel, plus the demo-mode browser sandbox
web\overview.js Overview page (issue counters, progress, locations, deployment calendar)
web\device-info.js Shared device helpers: missing-data reasons, presence, snapshot markers
web\excel-filter.js Excel-style sort / filter menus on every table; phone Filter & sort
web\layout.js / layout.css Sticky headers, card view on phones and tablets, Custom View scrolling and column drag
web\docs.js Knowledge base: permalinks, search, categories, PDF / print
web\router.js Page addresses (/status, /docs/...) and back / forward
web\jump.js Phones and tablets: jump to a device, a section, the top, middle or bottom

API

Method & path Purpose
GET / and static files Dashboard, assets, Data\ state files, Documentation\
GET /tracked · /workbench · /status · /devices · /custom · /docs The dashboard, opened on that page (the logo and name go to /, the Overview)
GET /docs/<title> The dashboard, opened on that knowledge-base article
GET /get-config Mode flags for the front end
GET /get-logs?source=server|worker&after=<id> Tail of the service console output (terminal panel)
POST /force-update · POST /force-full Wake the worker now (full = also refresh software/inventory caches)
GET /get-blacklist Baseline-app pattern list
GET /get-all-tags · GET /get-tracked?tech= · GET /get-tag-meta?tech= Read tracking tags
POST /update-tracked Set a tag's devices and Skip USMT flag
GET /get-software?name= Installed software for one device
GET /get-docs-list · POST /save-doc · POST /delete-doc Knowledge base (delete archives to Documentation\Archive)
GET /list-images · POST /upload-image · POST /rename-image · POST /delete-image Knowledge-base images

Demo mode

The public demo has to show the whole dashboard working without real infrastructure, and without letting strangers change what other visitors see.

  • It's the real code. The container runs the unmodified USMT-Server.ps1 and USMT-Worker.ps1 under PowerShell 7 with -Demo. The worker loads Providers\Demo.ps1 instead of Providers\SCCM.ps1, and that's the only difference.
  • A believable fleet. The demo provider derives each device's hardware, users, location, department and software from a hash of its name. Each device migrates on the day of its date tag: earlier days are finished, today's devices move through backup and restore during working hours, and later days haven't started. The few that don't finish have a reason the dashboard shows: a broken ConfigMgr client, broken WMI, low disk space, a USMT error, or a PC nobody can reach.
  • A simulated team. The next two weeks of working days are tagged, like a team working a two-week schedule. During working hours someone adds devices, reschedules or drops one, tags a stuck PC with its problem (Bad Client, Low Disk Space...) and later fixes it and moves it to a new day. Each morning finished days are closed out and a new day is scheduled at the end. Every action prints in the worker terminal as [Team] ....
  • Read-only API. In demo mode the server refuses every route that writes (and get-software, which takes free-text input) with 403. The terminal output is the real console of both processes, mirrored to Data\Logs by a Write-Host proxy.
  • Visitor changes stay in their browser. web\demo\demo.js intercepts the dashboard's write requests and keeps the visitor's tags, articles and images in IndexedDB, laid over the server's data (so the team's changes keep showing up around them). Their actions print in the terminal panel exactly as the services would, marked with a small dot. Nobody else sees them, and nothing a visitor types reaches the server, so there's nothing to moderate or inject. DEMO > Reset my changes clears it.

Without -Demo, none of this applies: demo.js passes every request straight through and the dashboard works against the real backend.

Running it

Demo (Docker / Coolify)

docker compose up -d --build        # http://localhost:8080

On Coolify, create a resource from this repository using the Dockerfile (or Docker Compose) build pack, set the exposed port to 8080, and attach a domain. The container reseeds the simulated fleet on every start and needs no volumes or secrets. Times are shown in TZ (default America/Chicago); set it to your own timezone. The demo worker runs 6 AM to 9 PM every day (Demo.BusinessHours in the config) and sleeps overnight; the simulated team works 7:30 AM to 5 PM on weekdays (Demo.Team), so migrations run then.

Demo (local PowerShell 7)

pwsh ./Demo/New-DemoData.ps1 -Force
pwsh ./USMT-Worker.ps1 -Demo        # terminal 1
pwsh ./USMT-Server.ps1 -Demo        # terminal 2 -> http://localhost:8080

Live, against your environment

Requirements: Windows PowerShell 5.1, the ConfigMgr console (for the ConfigurationManager module), read access to the site database, AD read access, and admin-share access to devices.

  1. Edit Config\usmt-dash.psd1: site code, SMS Provider, SQL server and database, and (if you extend hardware inventory with an asset/location class) the view and column map under AssetInventory.
  2. Start the worker, then the server:
    .\USMT-Worker.ps1          # add -v for per-device tracing
    .\USMT-Server.ps1          # binding http://*:8080/ may need: netsh http add urlacl url=http://*:8080/ user=DOMAIN\account
    
  3. Optionally restrict access by listing allowed IPs or hostnames in Data\Whitelisted-IPs.txt.
  4. Put baseline-image apps in Data\Blacklisted-Apps.txt so only specialty software shows as missing. See Demo\Blacklisted-Apps.txt for the syntax (plain patterns, Suite = a*, b*, Rule: DEVICE* = app*, Combo: Label = a* && b*).

Configuration

Everything lives in Config\usmt-dash.psd1; each setting is commented there. The ones you're most likely to change:

Setting Default What it does
Worker.CycleSeconds 180 Pause between cycles during business hours
Worker.BusinessHours 07:30-17:30, Mon-Fri When the worker cycles; outside it sleeps, with a midnight pitstop
Worker.PresenceSweepMinutes 60 How often completed and Skip USMT devices (which the sweep doesn't ping) are pinged
Worker.LivePublishSeconds 10 How often swept rows are published during a cycle
Worker.SoftwareCacheHours 24 How long a software inventory is reused
Worker.DiskSkipList none Devices whose admin share hangs; disk and log checks are skipped
SCCM.AssetInventory v_GS_ASSET_INFO0 Optional custom inventory view for location, department, room type...
SCCM.Monitors v_GS_WMIMONITORID Attached monitors (needs WmiMonitorID in hardware inventory)
Demo.BusinessHours 06:00-21:00, every day When the demo worker runs
Demo.DeviceCount / CycleSeconds 200 / 45 Size of the simulated fleet and its cycle pause
Demo.Team 07:30-17:00, Mon-Fri; 10 days ahead, 3 kept; 12 per day; an action every 4-12 min The simulated team's hours, schedule window and pace

Adapting it to a different toolset

All infrastructure access goes through a provider. To support another platform (Intune, a different CMDB, PDQ, ...), add Providers\<Name>.ps1, implement these functions, and set Provider = '<Name>' in the config:

Function Returns
Initialize-Provider One-time setup (import modules, ...)
Open-ProviderCycle / Close-ProviderCycle Per-cycle session (connections)
pingDevice <name> Online, Timed Out, Not Found or DNS Issue
Resolve-DeviceIP <name> Current IP, or $null
Get-DiskSpace <name> @{ Total = '238 GB'; Free = '80 GB' }
Get-LogInfo <name> <logName> Latest USMT progress log line (via the shared parser)
Get-DeviceAssociations <name> Objects with SourceComputer, DestinationComputer, MigrationStatus (0/1/2)
Get-DeviceInventory <name> <session> Hashtable of detail fields (OU, Groups, Model, RAM, MonitorCount, Monitors, Location, ...), plus InAD / InSCCM ($true / $false)
Get-InstalledSoftware <name> Rows of Name, Version, Publisher, Source, InstallDate
Get-DeviceLastSeen <name> <session> (optional) When the device was last active, used to backfill "last online" the first time it's tracked

Providers\Demo.ps1 is a complete working example that needs no infrastructure.

Project layout

USMT-Server.ps1          web server + JSON API
USMT-Worker.ps1          polling worker
Get-Software.ps1         per-device software lookup + cache
Config/usmt-dash.psd1    configuration
Lib/Common.ps1           config loader, console-log mirror, shared I/O
Providers/SCCM.ps1       ConfigMgr / SQL / AD / SMB data source
Providers/Demo.ps1       simulated fleet
Demo/                    demo seed script + sample app blacklist
Documentation/           knowledge-base articles and images
web/                     dashboard, terminal panel / demo sandbox, vendored libraries
Dockerfile, docker/      container for the hosted demo
Data/                    runtime state (created on first run, not committed): Data.json,
                         Worker-State.json, Advanced-Cache.json, Software-Cache.json,
                         Presence.json, Snapshots.json, Completed.json, Logs/

License

USMT Dash is released under the MIT License.

Third-party code

Vendored in web/vendor/ under their own licenses: marked (MIT), highlight.js (BSD-3-Clause), html2pdf.js (MIT).

S
Description
Real-time USMT / ConfigMgr migration dashboard in PowerShell: HttpListener API, polling worker, live demo
https://usmt.yoyos.sh
Readme MIT
3.1 MiB
0 Stars 1 Watchers 0 Forks
Languages
HTML 39.3%
JavaScript 29.2%
PowerShell 23.1%
CSS 8.1%
Dockerfile 0.2%