Fix user-context collection, split agent log destinations, widen upload cap
Mapped drives stopped reporting because the user-context path failed silently in three separate places, and none of them left a trace to read back. - Probe: launch via -EncodedCommand instead of staging a temp .ps1 and running powershell.exe -File. A missing or quarantined script file fails BEFORE any statement runs, so the script's own try/catch never sees it and the only symptom is exit -196608. - Spool: pin to the ShopFloor profile. Resolving it through WTSQueryUserToken is privileged, endpoint protection blocks it, and on failure the tray (writer) and service (reader) silently disagree about the path. - Spool: consume after a successful parse, so a stopped tray cannot keep re-reporting stale mapped drives as current. Unparseable files are left in place for the next cycle. - Logging: user-session components cannot write to Program Files or to the SYSTEM-owned ProgramData log directory, and the file sink degrades to a silent no-op when it cannot create its directory. Tray and probe now log to C:\Users\ShopFloor\ShopDB\Logs, installs transcript to ProgramData, and the sink records a SinkProblem instead of swallowing UnauthorizedAccessException. - Installer: provision the operator spool/log directories with ShopFloor:Modify before the tray first runs, and stop the transcript via trap so a throw cannot orphan exactly the failed install worth reading. - Upload cap 64 -> 128 MB: the self-contained tray publishes at ~69 MB and was rejected on SIZE, but the size check returns before the PE is parsed, so the operator only ever saw a version-detection failure. AGENT-CONTRACT.md gains a normative section defining the three security contexts, their log destinations, the fixed spool path and consume-on-read. Agent 1.8.0 -> 1.8.1, tray 1.1.0 -> 1.1.1 so the deployed build is identifiable from a log line.
This commit is contained in:
@@ -157,10 +157,49 @@ function Uninstall-PreviousAgent {
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
|
||||
# Installer transcript. Deliberately SEPARATE from both the service log
|
||||
# (%ProgramData%\ShopDB\EndpointAgent\logs\agent-<date>.log) and the tray log
|
||||
# (C:\Users\ShopFloor\ShopDB\Logs\<datetime>-trayagent.log): the installer runs
|
||||
# elevated and one-shot, and mixing its output into the service's rolling log
|
||||
# made an install indistinguishable from runtime behaviour when reading back a
|
||||
# failure. This runs as admin, so ProgramData is writable here.
|
||||
$installLogDir = Join-Path $env:ProgramData 'ShopDB\Logs'
|
||||
try {
|
||||
New-Item -ItemType Directory -Path $installLogDir -Force | Out-Null
|
||||
$installLog = Join-Path $installLogDir (
|
||||
'{0}-shopdbinstalls.log' -f (Get-Date -Format 'yyyyMMdd-HHmmss'))
|
||||
Start-Transcript -Path $installLog -Append | Out-Null
|
||||
$transcriptStarted = $true
|
||||
}
|
||||
catch {
|
||||
# Never let logging block an install.
|
||||
$transcriptStarted = $false
|
||||
Write-Warning "could not start the install transcript: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
# The shop-floor operator profile. The tray and the user-context probe run as
|
||||
# this local account on every shop-floor endpoint, and it is the one place both
|
||||
# the operator (writer) and the SYSTEM service (reader) can reach. Defined here
|
||||
# so the install can provision the directories and report their location.
|
||||
$shopFloorProfile = Join-Path (Join-Path $env:SystemDrive 'Users') 'ShopFloor'
|
||||
|
||||
# Stop the transcript on ANY terminating error, not just the success path. The
|
||||
# script throws on several validation and smoke-test failures, and those are
|
||||
# precisely the installs worth reading back afterwards - an orphaned transcript
|
||||
# would truncate exactly the failure it was started to capture.
|
||||
trap {
|
||||
if ($transcriptStarted) {
|
||||
try { Stop-Transcript | Out-Null } catch { }
|
||||
$script:transcriptStarted = $false
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
Write-Host "== ShopDB Endpoint Agent install ==" -ForegroundColor Cyan
|
||||
Write-Host "BaseUrl : $BaseUrl"
|
||||
Write-Host "InstallDir : $InstallDir"
|
||||
Write-Host "ServiceName : $ServiceName"
|
||||
if ($transcriptStarted) { Write-Host "InstallLog : $installLog" }
|
||||
|
||||
# 0. Clear the Mark-of-the-Web on the SOURCE payload first. The operator
|
||||
# typically extracts installer.zip into Downloads, so every file carries a
|
||||
@@ -188,6 +227,43 @@ Write-Host "`n[1/5] Provisioning registry $regPath (Registry64) ..."
|
||||
Set-Hklm64Value -SubKey "SOFTWARE\GE\ShopDB" -Name "BaseUrl" -Value $BaseUrl
|
||||
Set-Hklm64Value -SubKey "SOFTWARE\GE\ShopDB" -Name "CollectorKey" -Value $CollectorKey
|
||||
|
||||
# 1a. Provision the user-context spool and tray log directories in the operator
|
||||
# profile, and grant the operator Modify on them.
|
||||
#
|
||||
# This is the handoff point between the two security contexts (AGENT-CONTRACT.md
|
||||
# section 5): the tray WRITES here as the ShopFloor operator, and the SYSTEM
|
||||
# service READS and then removes the spool. The operator cannot write to
|
||||
# Program Files or to the SYSTEM-owned ProgramData log directory, and the file
|
||||
# log sink degrades to a silent no-op when it cannot create its directory - so
|
||||
# without this step a tray-side failure leaves no trace anywhere on the machine.
|
||||
# Creating them here, elevated, means the directories exist and are writable
|
||||
# before the tray ever starts.
|
||||
Write-Host "[1a/5] Provisioning operator spool/log directories under $shopFloorProfile ..."
|
||||
if (Test-Path $shopFloorProfile) {
|
||||
foreach ($dir in @(
|
||||
(Join-Path $shopFloorProfile 'ShopDB'),
|
||||
(Join-Path $shopFloorProfile 'ShopDB\Logs'))) {
|
||||
try {
|
||||
New-Item -ItemType Directory -Force -Path $dir | Out-Null
|
||||
$acl = Get-Acl $dir
|
||||
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
||||
'ShopFloor', 'Modify',
|
||||
'ContainerInherit,ObjectInherit', 'None', 'Allow')
|
||||
$acl.SetAccessRule($rule)
|
||||
Set-Acl -Path $dir -AclObject $acl
|
||||
Write-Host " provisioned $dir (ShopFloor: Modify)"
|
||||
}
|
||||
catch {
|
||||
# Non-fatal: telemetry, commands and inventory are unaffected. Only
|
||||
# user-context (mapped drives, idle time) depends on this path.
|
||||
Write-Warning " could not provision $dir : $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Warning " no ShopFloor profile at $shopFloorProfile; user-context (mapped drives, idle time) will be unavailable until that user has logged in once."
|
||||
}
|
||||
|
||||
# 2. Copy agent files to the install directory. The agent and tray are
|
||||
# single-file self-contained executables, so the .exe plus appsettings.json is
|
||||
# the whole payload; there are no side-by-side runtime DLLs to copy.
|
||||
@@ -352,4 +428,10 @@ else {
|
||||
|
||||
Write-Host "`nInstall complete." -ForegroundColor Green
|
||||
Write-Host "Service '$ServiceName' is running and set to auto-start."
|
||||
Write-Host "Logs: $logDir"
|
||||
# The three log destinations are separate on purpose; see AGENT-CONTRACT.md.
|
||||
Write-Host "Service log : $logDir"
|
||||
Write-Host "Tray log : $(Join-Path $shopFloorProfile 'ShopDB\Logs')"
|
||||
if ($transcriptStarted) {
|
||||
Write-Host "Install log : $installLog"
|
||||
try { Stop-Transcript | Out-Null } catch { }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user