Vestis CL&QL Web Display Image Monthly Update - Technical Deep Dive
FollowPurpose
This article documents how the Vestis QL image refresh system actually works under the hood - the PowerShell script, the Ninja task that runs it, and the Ninja tasks that distribute new content each month. For the monthly update process (what L1/L2 does when a ticket comes in), see the SOP article instead.
System overview
Two independent pieces work together:
- Distribution (Ninja, manual) - pushes new content from Spindle out to plant servers' staging folders
- Processing (Ninja, automatic/nightly) - runs
process-vestis-images.ps1on each plant server, which reads whatever landed in staging and applies it to the live display folders
Neither piece needs to know about QL/CL or English/French directly - that distinction is handled entirely by which Ninja distribution task is run against which target group, and which language file is attached to it. The script itself only ever looks at filenames.
Folder structure (per plant server)
| Purpose | Path |
|---|---|
| Staging (Ninja delivers files here) | D:\temp\SpindleImages |
| Random destination | D:\Spindle\WorkTrack\Web\Web Display\Messages\Random |
| Monthly destination | D:\Spindle\WorkTrack\Web\Web Display\Messages\Monthly\<number> |
| Archive (previous content, retained 1 year) | ...\Archive\Random\<timestamp> and ...\Archive\Monthly\<number>\<timestamp> |
| Quarantine (couldn't be safely routed) | D:\temp\SpindleImages\Skipped |
| Internal working folder (script scratch space) | D:\temp\SpindleImages\_work |
| Log file | D:\temp\SpindleImages\process-log.txt |
The PowerShell script: process-vestis-images.ps1
Runs nightly on every plant server. Logic, in order:
- Ensures required folders exist (staging, work, skipped, Random, Monthly, Archive) - creates them if missing, so it works even on a server that's never had this structure before.
- Clears stale work folders left over from any previous run that didn't finish cleanly.
- Scans staging for input files - both
.zipfiles and raw image files (.jpg,.jpeg,.png,.gif,.bmp,.webp). - Routes each file by filename:
- A number found anywhere in the filename -> destination is
Monthly\<that number> - No number in the filename -> destination is
Random - Zips are extracted into a temp work folder first; images are copied into one (no extraction needed).
- Language words (English/French) anywhere in the name are ignored for routing purposes.
- A number found anywhere in the filename -> destination is
- Resolves conflicts per destination - if two different source files in the same run both resolve to the same destination (e.g. two files that both look like month 8), the more recently modified one wins. The loser is moved to
\Skippedfor manual review rather than silently dropped. - Swaps in the new content for each winning destination:
- Archives whatever currently exists at that destination into a timestamped folder under
Archive\... - Moves the new content into place as the destination folder
- Archives whatever currently exists at that destination into a timestamped folder under
- Deletes the source file from staging once it's been successfully applied. If something errors partway through, the source file is left in staging (not deleted) so it can be retried or investigated - it does not silently disappear on failure.
- Runs retention cleanup - deletes archive subfolders older than 365 days (configurable via
$RetentionDaysnear the top of the script). - Logs every step with a timestamp to
process-log.txt.
The script is intentionally idempotent-safe: running it on a night with nothing new in staging just logs "nothing to do" and exits - there's no harm in it running every single night regardless of whether content actually changed.
Ninja task: nightly processing
- Schedule: Daily, 12:01 AM
- Action: runs
process-vestis-images.ps1 - Targets: all plants, including QL
- Maintenance: none required under normal operation. This task should not need to be touched month to month - only the distribution tasks (below) get updated routinely.
Ninja tasks: distribution
Two separate tasks, split by language/plant group:
| Task | Targets | Content |
|---|---|---|
RK - Vestis | Distribute Random Images - All US and CL except QL | All plants except QL | English files |
RK - Vestis | Distribute Random Images - All QL | QL group | French files |
- Schedule type:
Run Once Immediately- this fires once automatically when the task is first saved/assigned to a device. Resaving the task does not reliably re-trigger delivery to devices that have already successfully received a prior push - Ninja appears to track per-device completion for this trigger type. Monthly update mechanism (reliable method): use the bulk device-run action rather than resaving the distribution task:
- Go to Devices in NinjaOne
- Filter to the target group (QL for French, All US and CL except QL for English)
- Select all matching devices via the checkboxes
- Use the Run menu at the top of the filtered list -> Automation -> Script
- Pick the relevant distribution script and run it against all selected devices at once
Do this for both groups each month (English and French), even if only one language's content changed.
Legacy system (retired)
Two older scheduled tasks previously handled this via an installer-driven EXE:
Run Vestis EXE from SpindleMessagesAdmin(ran monthly, executed a Vestis installer EXE)Archive Vestis EXEs(ran monthly, archived old EXE files)
Both are disabled (not deleted) on migrated plants - kept in place for reference/rollback rather than removed outright.
Where to make changes
- Script logic changes (new file types, new routing rules, retention period): edit
process-vestis-images.ps1directly. Test on one plant before rolling out broadly. - Monthly content changes: never require script changes - only the Ninja distribution task's attached files and the bulk device-run action.
- New plant onboarding: no plant-specific configuration needed in the script itself; just ensure the new plant is targeted by the correct distribution task(s) (English or French) and the nightly processing task.
Comments
0 comments
Please sign in to leave a comment.