Three fixes from the 579C144 diagnostics, all the same shape: a Display kiosk
being treated as a machine-tool bay.
desktopApps: configuring "none" produced "everything"
"Display-Dashboard": { "desktopApps": [] }
06-OrganizeDesktop.ps1 tested $null -ne $cfgApps -AND .Count -gt 0, so an
explicitly empty list fell through to the hardcoded fallback and the kiosk was
given UDC, eDNC, NTLARS, WJ Shopfloor and Defect_Tracker shortcuts.
Get-ProfileValue returns $null only when the key is ABSENT from both the profile
and site-config, so $null is the real "not configured" signal and an empty array
means what it says. startupItems carried the same test - harmless today because
its else branch has no fallback list, but commented so the two cannot drift.
Machine-number prompt on a machine with no machine number
Register-CheckMachineNumberTask.ps1 armed 'Prompt Machine Number' (AtLogOn,
BUILTIN\Users) whenever the number was the 9999 placeholder - which is always
true on a Display, because startnet only collects a real number for machine-tool
types. A logon dialog on a kiosk with no keyboard. Now skips PC types that have
no machine number by design, and clears any stale task.
S: mapper on a share-less PC
Displays are Entra-joined with local accounts and no SFLD credentials, so mapping
S: can only fail, once per logon, forever. Run-ShopfloorSetup already gated the
CALL on $noEnforceTypes, but the bay registered it anyway at 15:07:55 with no
"Skipping" line in the log - so something in the finalization phase reaches the
registrar past that gate. Rather than chase the caller, the registrar now gates
itself and removes a stale Run entry. The call-site gate stays; this makes the
outcome correct regardless of who invokes it.
That bypass is worth understanding separately - the same pattern would defeat any
call-site gate in the finalization phase.
All three parse clean and are deployed byte-identical to the share.
116 lines
5.0 KiB
PowerShell
116 lines
5.0 KiB
PowerShell
# Register-MapSfldShare.ps1 - Stage Map-SfldShare.ps1 + register an
|
|
# HKLM\Run entry that maps S: for any interactive user (SupportUser,
|
|
# ShopFloor, any future end-user accounts).
|
|
#
|
|
# Why HKLM\Run instead of a scheduled task: Run fires at Explorer
|
|
# startup in the logged-in user's interactive session with their full
|
|
# token + HKCU mounted. No principal/LogonType/group-SID plumbing, no
|
|
# "task fires in session 0 but drive not visible to Explorer" class of
|
|
# bugs. Works for every BUILTIN\Users member with no extra logic.
|
|
#
|
|
# Why not the vendor's ConsumeCredentials.ps1: it calls
|
|
# New-StoredCredential -Persist LocalMachine (needs admin) before net
|
|
# use. ShopFloor is non-admin, so the cred-store fails and net use has
|
|
# no auth. Our Map-SfldShare.ps1 reads HKLM creds directly and passes
|
|
# them inline to net use /user: -- no Credential Manager needed.
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
|
|
# PC types that are self-contained and must NOT map S:. A Display kiosk is
|
|
# Entra-joined with local accounts and no SFLD credentials, so the mapping can
|
|
# only ever fail - once per logon, forever.
|
|
#
|
|
# Run-ShopfloorSetup.ps1 already gates the CALL to this script on the same list,
|
|
# but on 579C144 (2026-08-06) the Run entry was registered on a Display anyway:
|
|
#
|
|
# [15:07:55] Set HKLM:\...\Run\GE Map SFLD Share = ...Map-SfldShare.ps1
|
|
#
|
|
# with no "Skipping S: drive logon mapper" in the log, so something in the
|
|
# finalization phase reaches this script past the call-site gate. Gating here as
|
|
# well makes the outcome correct regardless of who invokes it.
|
|
$selfContainedTypes = @('Display', 'gea-shopfloor-display')
|
|
$pcTypeFile = 'C:\Enrollment\pc-type.txt'
|
|
$pcType = ''
|
|
if (Test-Path -LiteralPath $pcTypeFile) {
|
|
$pcType = (Get-Content -LiteralPath $pcTypeFile -First 1 -ErrorAction SilentlyContinue)
|
|
if ($pcType) { $pcType = $pcType.Trim() }
|
|
}
|
|
if ($pcType -and ($selfContainedTypes -contains $pcType)) {
|
|
$d = 'C:\Logs\SFLD'
|
|
if (-not (Test-Path $d)) { New-Item -ItemType Directory -Path $d -Force -EA SilentlyContinue | Out-Null }
|
|
Add-Content -Path (Join-Path $d 'register-mapshare.log') -EA SilentlyContinue `
|
|
-Value ("[{0}] [INFO] PC type '{1}' is self-contained - not registering the S: mapper." -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $pcType)
|
|
Write-Host "PC type '$pcType' is self-contained - skipping S: drive mapper."
|
|
# Remove a stale entry from an earlier image or an earlier code path.
|
|
try {
|
|
$runKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
|
|
if ((Get-ItemProperty -Path $runKey -Name 'GE Map SFLD Share' -EA SilentlyContinue)) {
|
|
Remove-ItemProperty -Path $runKey -Name 'GE Map SFLD Share' -Force -EA Stop
|
|
Write-Host " removed stale 'GE Map SFLD Share' Run entry."
|
|
}
|
|
} catch { }
|
|
return
|
|
}
|
|
|
|
$installRoot = 'C:\Program Files\GE\SfldShare'
|
|
$mapScript = Join-Path $installRoot 'Map-SfldShare.ps1'
|
|
$logDir = 'C:\Logs\SFLD'
|
|
$logFile = Join-Path $logDir 'register-mapshare.log'
|
|
$runKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
|
|
$runValue = 'GE Map SFLD Share'
|
|
$legacyTask = 'GE Shopfloor Map S: Drive'
|
|
|
|
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null }
|
|
|
|
function Write-RegLog {
|
|
param([string]$Message)
|
|
$line = '[{0}] [INFO] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message
|
|
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
|
|
Write-Host $line
|
|
}
|
|
|
|
Write-RegLog '=== Register-MapSfldShare start ==='
|
|
|
|
# Stage our Map-SfldShare.ps1 to a persistent location
|
|
if (-not (Test-Path $installRoot)) {
|
|
New-Item -Path $installRoot -ItemType Directory -Force | Out-Null
|
|
}
|
|
$src = Join-Path $PSScriptRoot 'lib\Map-SfldShare.ps1'
|
|
if (Test-Path $src) {
|
|
Copy-Item -Path $src -Destination $mapScript -Force
|
|
Write-RegLog "Staged $src -> $mapScript"
|
|
} else {
|
|
Write-RegLog "Map-SfldShare.ps1 not found at $src - cannot register"
|
|
exit 1
|
|
}
|
|
|
|
# Remove the legacy scheduled task if it exists (left behind by older
|
|
# imaging runs that used the scheduled-task approach).
|
|
if (Get-ScheduledTask -TaskName $legacyTask -ErrorAction SilentlyContinue) {
|
|
try {
|
|
Unregister-ScheduledTask -TaskName $legacyTask -Confirm:$false -ErrorAction Stop
|
|
Write-RegLog "Removed legacy scheduled task '$legacyTask'"
|
|
} catch {
|
|
Write-RegLog "Failed to remove legacy task '$legacyTask': $_"
|
|
}
|
|
}
|
|
|
|
# Register HKLM\Run entry. Runs at Explorer startup for every
|
|
# interactive user in that user's session.
|
|
try {
|
|
$command = '"{0}" -NoProfile -ExecutionPolicy Bypass -File "{1}"' -f `
|
|
"$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe", $mapScript
|
|
|
|
if (-not (Test-Path $runKey)) {
|
|
New-Item -Path $runKey -Force | Out-Null
|
|
}
|
|
New-ItemProperty -Path $runKey -Name $runValue -Value $command -PropertyType String -Force | Out-Null
|
|
Write-RegLog "Set $runKey\$runValue = $command"
|
|
} catch {
|
|
Write-RegLog "FAILED to register Run key: $_"
|
|
exit 1
|
|
}
|
|
|
|
Write-RegLog '=== Register-MapSfldShare end ==='
|
|
exit 0
|