Generalize USMT Dash and add a containerized demo mode
Remove all environment-specific data and make the dashboard adoptable anywhere, then add a demo that runs the real PowerShell services. - Strip cached device data, internal knowledge-base articles and screenshots, server/site names and the old project notes; replace the knowledge base with generic sample articles and diagrams - Move every environment value into Config/usmt-dash.psd1 - Route all infrastructure access in the worker and Get-Software.ps1 through a provider (Providers/SCCM.ps1), with a config-driven asset inventory view instead of a hard-coded custom class - Add Providers/Demo.ps1: a deterministic simulated fleet whose migrations progress in real time, and Demo/New-DemoData.ps1 to seed it - Server: -Demo makes the API read-only; add get-config and get-logs; serve static files only from web/, Documentation/ and an allowlist of Data/ files with a path-traversal guard; gzip large responses - Mirror both services' console output to Data/Logs via a Write-Host proxy, and add a terminal panel to the dashboard that streams it - Demo sandbox (web/demo/demo.js): visitor changes to tags, articles and images are kept in the visitor's browser and echoed in the terminals; nothing a visitor does reaches the server - Vendor marked, highlight.js and html2pdf.js instead of loading CDNs - Dockerfile and compose file for hosting the demo (Coolify-ready)
This commit is contained in:
commit
1c531bc2ab
56 files changed
+10232
No files matched your search
@@ -0,0 +1,38 @@
|
||||
Microsoft 365 Apps for enterprise *
|
||||
Microsoft Edge
|
||||
Microsoft Edge WebView2 Runtime
|
||||
Microsoft OneDrive
|
||||
Microsoft Teams*
|
||||
Microsoft Visual C++ *
|
||||
Microsoft Intune Management Extension
|
||||
Microsoft Policy Platform
|
||||
Microsoft .NET *
|
||||
Microsoft Windows Desktop Runtime *
|
||||
Configuration Manager Client
|
||||
Google Chrome
|
||||
Adobe Acrobat Reader*
|
||||
7-Zip *
|
||||
Endpoint Protection Agent
|
||||
Intel(R) Management Engine Components
|
||||
Realtek Audio Driver
|
||||
Windows Driver Package *
|
||||
HP Support Assistant
|
||||
Dell Command | Update
|
||||
Lenovo Vantage Service
|
||||
Cisco Secure Client*
|
||||
Adobe Acrobat Pro = Adobe Acrobat (64-bit), Adobe Acrobat DC*
|
||||
QuickBooks = QuickBooks Desktop*, QuickBooks Database Server Manager*, Intuit Data Protect*
|
||||
Salesforce = Salesforce for Outlook*, Salesforce Data Loader*
|
||||
Adobe Creative Cloud = Adobe Creative Cloud, Adobe Photoshop*, Adobe Illustrator*, Adobe InDesign*
|
||||
Microsoft SQL Server Management Studio = SQL Server Management Studio*, SSMS Post Install Tasks*
|
||||
Microsoft Power BI = Microsoft Power BI Desktop*, Power Pivot for Excel*
|
||||
Microsoft Visio = Microsoft Visio*
|
||||
Microsoft Project = Microsoft Project*
|
||||
Python = Python 3.*, Python Launcher*
|
||||
Wireshark = Wireshark*, Npcap*
|
||||
Zoom = Zoom Workplace*, Zoom*
|
||||
Slack = Slack*
|
||||
Tableau = Tableau 20*
|
||||
Notepad++ = Notepad++*
|
||||
Rule: PUB* = Kiosk Launcher*, Shared PC Cleanup Utility*
|
||||
Combo: Finance Reporting Bundle = QuickBooks Desktop* && Microsoft Power BI Desktop*
|
||||
@@ -0,0 +1,122 @@
|
||||
# Seeds Data\ with a simulated fleet for demo mode: tracking tags (Tracked-Devices.json),
|
||||
# the app blacklist, and a pre-warmed software cache so the first worker cycle is quick.
|
||||
# Run before starting the services. -Force wipes any existing state first.
|
||||
param(
|
||||
[switch]$Force,
|
||||
[string]$ConfigPath
|
||||
)
|
||||
|
||||
$root = Split-Path $PSScriptRoot -Parent
|
||||
. (Join-Path $root "Lib/Common.ps1")
|
||||
$config = Import-DashConfig -Path $ConfigPath -Demo
|
||||
$v = $false
|
||||
. (Join-Path $root "Providers/Demo.ps1")
|
||||
. (Join-Path $root "Lib/Common.ps1") # re-import so nothing in the provider shadows the helpers
|
||||
|
||||
$dataDir = Join-Path $root "Data"
|
||||
if ($Force -and (Test-Path $dataDir)) {
|
||||
Get-ChildItem $dataDir -Recurse -File | Remove-Item -Force
|
||||
}
|
||||
if (-not (Test-Path $dataDir)) { New-Item -ItemType Directory -Path $dataDir | Out-Null }
|
||||
|
||||
$trackedPath = Join-Path $dataDir "Tracked-Devices.json"
|
||||
if ((Test-Path $trackedPath) -and -not $Force) {
|
||||
Write-Host "Demo data already present (use -Force to reseed)." -ForegroundColor DarkGray
|
||||
return
|
||||
}
|
||||
|
||||
$count = [int]$config.Demo.DeviceCount
|
||||
Write-Host "Seeding demo fleet ($count tracked devices)..." -ForegroundColor Cyan
|
||||
|
||||
# --- 1. Generate source devices ---
|
||||
function New-FleetName([int]$i, [string]$prefix) { return "$prefix-$(New-DemoSerial "fleet$i")" }
|
||||
|
||||
$skipPublic = [System.Collections.Generic.List[string]]::new()
|
||||
$skipLaptops = [System.Collections.Generic.List[string]]::new()
|
||||
$pool = [System.Collections.Generic.List[string]]::new()
|
||||
$i = 0
|
||||
while ($skipPublic.Count -lt 14) { $skipPublic.Add((New-FleetName ($i++) "PUBDT")) }
|
||||
while ($skipLaptops.Count -lt 16) { $skipLaptops.Add((New-FleetName ($i++) (Get-DemoPick "lt$i" 'p' @("PRVLT", "PRVLT", "PRVLT", "PUBLT")))) }
|
||||
while ($pool.Count -lt ($count - $skipPublic.Count - $skipLaptops.Count - 3)) {
|
||||
$r = Get-DemoRand "mix$i" 'prefix'
|
||||
$prefix = if ($r -lt 0.55) { "PRVDT" } elseif ($r -lt 0.80) { "PUBDT" } elseif ($r -lt 0.95) { "PRVLT" } else { "PUBLT" }
|
||||
$pool.Add((New-FleetName ($i++) $prefix))
|
||||
}
|
||||
|
||||
# Manual "Source, Destination" pairs - hardware exceptions with no SCCM association
|
||||
$manual = @()
|
||||
for ($m = 0; $m -lt 3; $m++) {
|
||||
$src = New-FleetName ($i++) "PRVDT"
|
||||
$dst = New-FleetName ($i++) "PRVDT"
|
||||
$manual += "$src, $dst"
|
||||
}
|
||||
|
||||
# --- 2. Date-batch tags (recent and upcoming working days) ---
|
||||
function Get-Workday([datetime]$from, [int]$offset) {
|
||||
$d = $from.Date; $step = [math]::Sign($offset); $left = [math]::Abs($offset)
|
||||
while ($left -gt 0) { $d = $d.AddDays($step); if ($d.DayOfWeek -ne 'Saturday' -and $d.DayOfWeek -ne 'Sunday') { $left-- } }
|
||||
return $d
|
||||
}
|
||||
$today = Get-Date
|
||||
$dateTags = @(-4, -3, -2, -1, 0, 1) | ForEach-Object { (Get-Workday $today $_).ToString("M/d") }
|
||||
|
||||
$tags = [ordered]@{}
|
||||
foreach ($t in $dateTags) { $tags[$t] = [System.Collections.Generic.List[string]]::new() }
|
||||
for ($n = 0; $n -lt $pool.Count; $n++) { $tags[$dateTags[$n % $dateTags.Count]].Add($pool[$n]) }
|
||||
$tags["Manual Pairs"] = [System.Collections.Generic.List[string]]::new([string[]]$manual)
|
||||
|
||||
# Department tags overlap the date batches, like a tech grouping their own work
|
||||
$byDept = @{}
|
||||
foreach ($d in $pool) {
|
||||
$dep = (Get-DemoProfile $d).Department.Name
|
||||
if (-not $byDept.ContainsKey($dep)) { $byDept[$dep] = [System.Collections.Generic.List[string]]::new() }
|
||||
$byDept[$dep].Add($d)
|
||||
}
|
||||
foreach ($pair in @(@("Accounting", "Accounting"), @("Sales Floor", "Sales"), @("Exec Suite", "Executive"))) {
|
||||
if ($byDept.ContainsKey($pair[1])) { $tags[$pair[0]] = [System.Collections.Generic.List[string]]::new([string[]]@($byDept[$pair[1]] | Select-Object -First 12)) }
|
||||
}
|
||||
$tags["Broken WMI"] = [System.Collections.Generic.List[string]]::new([string[]]@($pool | Where-Object { (Get-DemoRand $_ 'wmi') -lt 0.03 } | Select-Object -First 5))
|
||||
$tags["Public PCs"] = $skipPublic
|
||||
$tags["Laptops"] = $skipLaptops
|
||||
|
||||
$skipTags = @("Public PCs", "Laptops")
|
||||
$tracked = [ordered]@{}
|
||||
foreach ($t in $tags.Keys) {
|
||||
$tracked[$t] = [ordered]@{ devices = @($tags[$t]); skipUSMT = ($skipTags -contains $t) }
|
||||
}
|
||||
($tracked | ConvertTo-Json -Depth 5) | Set-Content -Path $trackedPath -Encoding UTF8
|
||||
|
||||
# --- 3. Blacklist / suite rules ---
|
||||
Copy-Item (Join-Path $PSScriptRoot "Blacklisted-Apps.txt") (Join-Path $dataDir "Blacklisted-Apps.txt") -Force
|
||||
$blacklist = @(Get-Content (Join-Path $dataDir "Blacklisted-Apps.txt") | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() })
|
||||
|
||||
# --- 4. Pre-warmed software cache (the worker would otherwise build it one device at a time) ---
|
||||
$allDevices = [System.Collections.Generic.List[string]]::new()
|
||||
foreach ($d in ($pool + $skipPublic + $skipLaptops)) {
|
||||
$allDevices.Add($d)
|
||||
$p = Get-DemoProfile $d
|
||||
if (-not $p.NoAssoc -and -not ($skipPublic -contains $d) -and -not ($skipLaptops -contains $d)) { $allDevices.Add((Get-DemoDestName $d)) }
|
||||
}
|
||||
foreach ($pair in $manual) { $allDevices.AddRange([string[]]@($pair -split '\s*,\s*')) }
|
||||
|
||||
$stamp = (Get-Date).ToString("o")
|
||||
$cache = [ordered]@{}
|
||||
foreach ($d in $allDevices) {
|
||||
$rows = foreach ($row in (Get-InstalledSoftware -ComputerName $d)) {
|
||||
$isSpecialty = $true
|
||||
foreach ($bad in $blacklist) { if ($row.Name -like $bad) { $isSpecialty = $false; break } }
|
||||
[PSCustomObject]@{ Name = $row.Name; Version = $row.Version; Publisher = $row.Publisher; Source = $row.Source; InstallDate = $row.InstallDate; IsSpecialty = $isSpecialty }
|
||||
}
|
||||
$cache[$d] = [PSCustomObject]@{ Timestamp = $stamp; Data = @($rows) }
|
||||
}
|
||||
[System.IO.File]::WriteAllText((Join-Path $dataDir "Software-Cache.json"), ($cache | ConvertTo-Json -Depth 6 -Compress), [System.Text.Encoding]::UTF8)
|
||||
|
||||
# --- 5. Empty state for the rest ---
|
||||
Set-Content -Path (Join-Path $dataDir "Completed.json") -Value "[]" -Encoding UTF8
|
||||
Set-Content -Path (Join-Path $dataDir "Data.json") -Value "[]" -Encoding UTF8
|
||||
Set-Content -Path (Join-Path $dataDir "Advanced-Cache.json") -Value "{}" -Encoding UTF8
|
||||
Set-Content -Path (Join-Path $dataDir "Worker-State.json") -Value '{"Status":"Preparing...","Current":0,"Total":0,"LastUpdated":"Never","NextCycle":"Calculating...","LastDuration":"N/A"}' -Encoding UTF8
|
||||
Set-Content -Path (Join-Path $dataDir "Whitelisted-IPs.txt") -Value "" -Encoding UTF8
|
||||
|
||||
$unique = @($tracked.Values | ForEach-Object { $_.devices } | ForEach-Object { $_ }) | Select-Object -Unique
|
||||
Write-Host "Seeded $($tracked.Count) tags, $(@($unique).Count) roster entries, $($cache.Count) software inventories." -ForegroundColor Green
|
||||
Reference in new issue
Block a user