Add Acrobat Reader logon enforcer (cross-PC-type), provtool.exe arg fix
Acrobat Reader enforcement:
- playbook/shopfloor-setup/common/ is the cross-PC-type staging dir. Mirrors
CMM/ structure (enforce script + its Install-FromManifest copy + manifest
template + register script).
- Acrobat-Enforce.ps1 runs as SYSTEM on every logon, reads
acrobatSharePath from site-config.common, mounts the SFLD share with
the same HKLM-backed credential lookup CMM-Enforce uses, hands the
acrobat-manifest.json from the share to Install-FromManifest.
- Install-FromManifest extended with Type=CMD so it can invoke vendor-
supplied .cmd wrappers (Install-AcroReader.cmd does a two-step MSI+MSP
install that does not fit MSI/EXE types cleanly). cmd.exe /c wraps it
because UseShellExecute=false cannot launch .cmd directly.
- Register-AcrobatEnforce.ps1 stages scripts to C:\Program Files\GE\Acrobat
and registers "GE Acrobat Enforce" scheduled task. Called from
Run-ShopfloorSetup.ps1 right before the enrollment (PPKG) step so it
applies to every PC type, not just CMM.
- acrobat-manifest.template.json is the repo reference; the authoritative
copy lives on the SFLD share at
\\tsgwp00525.wjs.geaerospace.net\shared\dt\shopfloor\common\acrobat\
Bumping Acrobat updates = drop new MSP on share, bump DetectionValue in
manifest; enforcer catches every PC on next logon.
- site-config.json: add "common": { "acrobatSharePath": ... }. Uses a
new top-level block rather than a PC-type-specific one since Acrobat
applies everywhere.
Initial install still happens via the preinstall flow
(Install-AcroReader.cmd during WinPE). The enforcer is the ongoing-
updates side; on a freshly-imaged PC detection passes and it no-ops.
Also in this commit:
- run-enrollment.ps1: provtool.exe argument syntax fix. First test
returned 0x80004005 E_FAIL in 1s because /ppkg: and /log: are not
valid provtool flags; the cmdlet's internal call used positional
path + /quiet + /source. Switched to that syntax.
This commit is contained in:
@@ -233,6 +233,20 @@ if (Test-Path -LiteralPath $monitorScript) {
|
||||
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoLogonCount /t REG_DWORD /d 4 /f | Out-Null
|
||||
Write-Host "Auto-logon set to 4 remaining logins."
|
||||
|
||||
# --- Register cross-PC-type enforcers (Acrobat, etc.) ---
|
||||
# These run on every logon regardless of PC type, mounting the SFLD share
|
||||
# for version-pinned app enforcement. Initial install already handled by
|
||||
# preinstall flow; enforcers only kick in when detection fails.
|
||||
$commonSetupDir = Join-Path $PSScriptRoot 'common'
|
||||
$registerAcrobat = Join-Path $commonSetupDir 'Register-AcrobatEnforce.ps1'
|
||||
if (Test-Path -LiteralPath $registerAcrobat) {
|
||||
Write-Host ""
|
||||
Write-Host "=== Registering Acrobat enforcer ==="
|
||||
try { & $registerAcrobat } catch { Write-Warning "Acrobat enforce registration failed: $_" }
|
||||
} else {
|
||||
Write-Host "Register-AcrobatEnforce.ps1 not found (optional) - skipping"
|
||||
}
|
||||
|
||||
# --- Run enrollment (PPKG install) ---
|
||||
# Enrollment is the LAST thing we do. Install-ProvisioningPackage triggers
|
||||
# an immediate reboot -- everything after this call is unlikely to execute.
|
||||
|
||||
134
playbook/shopfloor-setup/common/Acrobat-Enforce.ps1
Normal file
134
playbook/shopfloor-setup/common/Acrobat-Enforce.ps1
Normal file
@@ -0,0 +1,134 @@
|
||||
# Acrobat-Enforce.ps1 - On-logon Adobe Acrobat Reader DC enforcer.
|
||||
#
|
||||
# Cross-PC-type companion to CMM-Enforce.ps1. Runs under a SYSTEM scheduled
|
||||
# task triggered at user logon on every PC regardless of PC type, mounts the
|
||||
# tsgwp00525 SFLD share (common\acrobat path) using SFLD creds provisioned
|
||||
# by Azure DSC, reads acrobat-manifest.json from the share, and hands off to
|
||||
# Install-FromManifest.ps1 which installs anything whose detection fails.
|
||||
#
|
||||
# Initial Acrobat install happens at imaging time via the preinstall flow
|
||||
# (playbook/preinstall/...). This enforcer is the ongoing-updates side: when
|
||||
# Adobe publishes a new quarterly DC patch, drop the new .msp on the share,
|
||||
# bump DetectionValue in acrobat-manifest.json, and every PC catches up on
|
||||
# its next logon. On a freshly-imaged PC, detection passes immediately and
|
||||
# this script no-ops.
|
||||
#
|
||||
# Graceful degradation mirrors CMM-Enforce:
|
||||
# - SFLD creds missing (Azure DSC has not run yet) -> log + exit 0
|
||||
# - Share unreachable (network, VPN) -> log + exit 0
|
||||
# - Install failure -> log + exit 0
|
||||
#
|
||||
# Never returns non-zero to the task scheduler; failures show up in the log.
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
$installRoot = 'C:\Program Files\GE\Acrobat'
|
||||
$libPath = Join-Path $installRoot 'lib\Install-FromManifest.ps1'
|
||||
$logDir = 'C:\Logs\Acrobat'
|
||||
$logFile = Join-Path $logDir ('enforce-{0}.log' -f (Get-Date -Format 'yyyyMMdd'))
|
||||
# Use a drive letter that does not clash with CMM-Enforce's S: drive so the
|
||||
# two enforcers can run concurrently at logon without fighting for the mount.
|
||||
$driveLetter = 'T:'
|
||||
|
||||
if (-not (Test-Path $logDir)) {
|
||||
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
function Write-EnforceLog {
|
||||
param([string]$Message, [string]$Level = 'INFO')
|
||||
$line = "[{0}] [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
|
||||
Write-Host $line
|
||||
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-EnforceLog "================================================================"
|
||||
Write-EnforceLog "=== Acrobat-Enforce session start (PID $PID, user $env:USERNAME) ==="
|
||||
Write-EnforceLog "================================================================"
|
||||
|
||||
# --- Load site-config for acrobatSharePath ---
|
||||
# Dot-source the same Get-PCProfile.ps1 used at imaging time. It walks
|
||||
# C:\Enrollment\site-config.json into $pcProfile / $siteConfig script vars.
|
||||
$getProfileScript = 'C:\Enrollment\shopfloor-setup\Shopfloor\lib\Get-PCProfile.ps1'
|
||||
if (-not (Test-Path $getProfileScript)) {
|
||||
Write-EnforceLog "Get-PCProfile.ps1 not found at $getProfileScript - cannot locate share" "ERROR"
|
||||
exit 0
|
||||
}
|
||||
. $getProfileScript
|
||||
|
||||
# Acrobat share lives under the site-config "common" section, which applies
|
||||
# to every PC type (unlike cmmSharePath which is CMM-only).
|
||||
if (-not $siteConfig -or -not $siteConfig.common -or -not $siteConfig.common.acrobatSharePath) {
|
||||
Write-EnforceLog "No common.acrobatSharePath in site-config - nothing to enforce" "WARN"
|
||||
exit 0
|
||||
}
|
||||
|
||||
$sharePath = $siteConfig.common.acrobatSharePath
|
||||
Write-EnforceLog "Share: $sharePath"
|
||||
|
||||
function Get-SFLDCredential {
|
||||
param([string]$ServerName)
|
||||
$basePath = 'HKLM:\SOFTWARE\GE\SFLD\Credentials'
|
||||
if (-not (Test-Path $basePath)) { return $null }
|
||||
|
||||
foreach ($entry in Get-ChildItem -Path $basePath -ErrorAction SilentlyContinue) {
|
||||
$props = Get-ItemProperty -Path $entry.PSPath -ErrorAction SilentlyContinue
|
||||
if (-not $props -or -not $props.TargetHost) { continue }
|
||||
if ($props.TargetHost -eq $ServerName -or
|
||||
$props.TargetHost -like "$ServerName.*" -or
|
||||
$ServerName -like "$($props.TargetHost).*") {
|
||||
return @{
|
||||
Username = $props.Username
|
||||
Password = $props.Password
|
||||
TargetHost = $props.TargetHost
|
||||
KeyName = $entry.PSChildName
|
||||
}
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
$serverName = ($sharePath -replace '^\\\\', '') -split '\\' | Select-Object -First 1
|
||||
$cred = Get-SFLDCredential -ServerName $serverName
|
||||
|
||||
if (-not $cred -or -not $cred.Username -or -not $cred.Password) {
|
||||
Write-EnforceLog "No SFLD credential for $serverName yet (Azure DSC has not provisioned it) - will retry at next logon"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-EnforceLog "Credential: $($cred.KeyName) (user: $($cred.Username))"
|
||||
|
||||
# --- Mount the share ---
|
||||
net use $driveLetter /delete /y 2>$null | Out-Null
|
||||
|
||||
$netResult = & net use $driveLetter $sharePath /user:$($cred.Username) $($cred.Password) /persistent:no 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-EnforceLog "net use failed (exit $LASTEXITCODE): $netResult" "WARN"
|
||||
Write-EnforceLog "Share unreachable - probably off-network. Will retry at next logon."
|
||||
exit 0
|
||||
}
|
||||
Write-EnforceLog "Mounted $sharePath as $driveLetter"
|
||||
|
||||
try {
|
||||
$manifestOnShare = Join-Path $driveLetter 'acrobat-manifest.json'
|
||||
if (-not (Test-Path $manifestOnShare)) {
|
||||
Write-EnforceLog "acrobat-manifest.json not found on share - nothing to enforce" "WARN"
|
||||
return
|
||||
}
|
||||
|
||||
if (-not (Test-Path $libPath)) {
|
||||
Write-EnforceLog "Install-FromManifest.ps1 not found at $libPath" "ERROR"
|
||||
return
|
||||
}
|
||||
|
||||
Write-EnforceLog "Handing off to Install-FromManifest.ps1 (InstallerRoot=$driveLetter)"
|
||||
& $libPath -ManifestPath $manifestOnShare -InstallerRoot $driveLetter -LogFile $logFile
|
||||
$rc = $LASTEXITCODE
|
||||
Write-EnforceLog "Install-FromManifest returned $rc"
|
||||
}
|
||||
finally {
|
||||
net use $driveLetter /delete /y 2>$null | Out-Null
|
||||
Write-EnforceLog "Unmounted $driveLetter"
|
||||
Write-EnforceLog "=== Acrobat-Enforce session end ==="
|
||||
}
|
||||
|
||||
exit 0
|
||||
93
playbook/shopfloor-setup/common/Register-AcrobatEnforce.ps1
Normal file
93
playbook/shopfloor-setup/common/Register-AcrobatEnforce.ps1
Normal file
@@ -0,0 +1,93 @@
|
||||
# Register-AcrobatEnforce.ps1 - One-time setup for the Acrobat Reader
|
||||
# logon-enforce scheduled task. Called by each PC type's shopfloor setup
|
||||
# (Run-ShopfloorSetup.ps1) after the baseline imaging steps, once per
|
||||
# fresh install. Idempotent: re-running just refreshes the staged scripts
|
||||
# and re-registers the task.
|
||||
#
|
||||
# Parallel to CMM\09-Setup-CMM.ps1 steps 3-4 (stage Install-FromManifest +
|
||||
# Acrobat-Enforce, register the "GE Acrobat Enforce" task) but without any
|
||||
# imaging-time install step - initial Acrobat install is already handled by
|
||||
# the preinstall flow.
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
$installRoot = 'C:\Program Files\GE\Acrobat'
|
||||
$runtimeLib = Join-Path $installRoot 'lib\Install-FromManifest.ps1'
|
||||
$runtimeEnforce = Join-Path $installRoot 'Acrobat-Enforce.ps1'
|
||||
$logDir = 'C:\Logs\Acrobat'
|
||||
$setupLog = Join-Path $logDir 'setup.log'
|
||||
|
||||
# Source on the imaged client (staged there by WinPE startnet.cmd via
|
||||
# shopfloor-setup\common\).
|
||||
$sourceLib = Join-Path $PSScriptRoot 'lib\Install-FromManifest.ps1'
|
||||
$sourceEnforce = Join-Path $PSScriptRoot 'Acrobat-Enforce.ps1'
|
||||
|
||||
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null }
|
||||
if (-not (Test-Path $installRoot)) { New-Item -Path $installRoot -ItemType Directory -Force | Out-Null }
|
||||
if (-not (Test-Path (Join-Path $installRoot 'lib'))) {
|
||||
New-Item -Path (Join-Path $installRoot 'lib') -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
function Write-SetupLog {
|
||||
param([string]$Message, [string]$Level = 'INFO')
|
||||
$line = "[{0}] [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
|
||||
Write-Host $line
|
||||
Add-Content -Path $setupLog -Value $line -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-SetupLog "=== Register-AcrobatEnforce start ==="
|
||||
|
||||
# Stage scripts to their runtime location under Program Files so the
|
||||
# scheduled task can run them as SYSTEM.
|
||||
foreach ($pair in @(
|
||||
@{ Src = $sourceLib; Dst = $runtimeLib },
|
||||
@{ Src = $sourceEnforce; Dst = $runtimeEnforce }
|
||||
)) {
|
||||
if (-not (Test-Path $pair.Src)) {
|
||||
Write-SetupLog "Source not found: $($pair.Src) - cannot stage" "ERROR"
|
||||
continue
|
||||
}
|
||||
Copy-Item -Path $pair.Src -Destination $pair.Dst -Force
|
||||
Write-SetupLog "Staged $($pair.Src) -> $($pair.Dst)"
|
||||
}
|
||||
|
||||
# Register scheduled task. Unregister any stale copy first so re-imaging is
|
||||
# idempotent.
|
||||
$taskName = 'GE Acrobat Enforce'
|
||||
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
|
||||
if ($existing) {
|
||||
Write-SetupLog "Removing existing scheduled task '$taskName'"
|
||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-SetupLog "Registering scheduled task '$taskName' (logon trigger, SYSTEM)"
|
||||
try {
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute 'powershell.exe' `
|
||||
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$runtimeEnforce`""
|
||||
|
||||
$trigger = New-ScheduledTaskTrigger -AtLogOn
|
||||
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
||||
# ExecutionTimeLimit 30 min - Acrobat DC patches are smaller than PC-DMIS
|
||||
# bundles; 30 min is plenty and keeps a stuck install from lingering.
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-AllowStartIfOnBatteries `
|
||||
-DontStopIfGoingOnBatteries `
|
||||
-StartWhenAvailable `
|
||||
-ExecutionTimeLimit (New-TimeSpan -Minutes 30) `
|
||||
-MultipleInstances IgnoreNew
|
||||
|
||||
Register-ScheduledTask `
|
||||
-TaskName $taskName `
|
||||
-Action $action `
|
||||
-Trigger $trigger `
|
||||
-Principal $principal `
|
||||
-Settings $settings `
|
||||
-Description 'GE Acrobat: enforce Adobe Acrobat Reader DC version against tsgwp00525 SFLD share on user logon' | Out-Null
|
||||
|
||||
Write-SetupLog "Scheduled task registered"
|
||||
} catch {
|
||||
Write-SetupLog "Failed to register scheduled task: $_" "ERROR"
|
||||
}
|
||||
|
||||
Write-SetupLog "=== Register-AcrobatEnforce end ==="
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Version": "1.0",
|
||||
"_comment": "Adobe Acrobat Reader DC enforcement manifest. This is the TEMPLATE kept in the repo; the authoritative copy lives on the SFLD share at \\\\tsgwp00525.wjs.geaerospace.net\\shared\\dt\\shopfloor\\common\\acrobat\\acrobat-manifest.json. Acrobat-Enforce.ps1 reads the share copy on every user logon via the 'GE Acrobat Enforce' scheduled task. To push a new Acrobat update to the entire fleet: drop the new AcroRdrDCUpd*.msp in the share, update Install-AcroReader.cmd to reference its filename, bump DetectionValue below to the new version string, and the enforcer catches every PC on next logon. Initial install still happens during preinstall (not this enforcer) - the enforcer only kicks in when detection fails, which on fresh installs is never.",
|
||||
"Applications": [
|
||||
{
|
||||
"_comment": "Two-step install (MSI + MST transform, then MSP patch) done via the vendor-shipped Install-AcroReader.cmd wrapper. Wrapper uses %~dp0 so it finds AcroRead.msi, AcroRead.mst, Data1.cab, and AcroRdrDCUpd*.msp next to itself on the share. DetectionValue pins the patched version: bump it when replacing the MSP.",
|
||||
"Name": "Adobe Acrobat Reader DC",
|
||||
"Installer": "Install-AcroReader.cmd",
|
||||
"Type": "CMD",
|
||||
"LogFile": "C:\\Logs\\Acrobat\\install.log",
|
||||
"DetectionMethod": "Registry",
|
||||
"DetectionPath": "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}",
|
||||
"DetectionName": "DisplayVersion",
|
||||
"DetectionValue": "25.001.20531"
|
||||
}
|
||||
]
|
||||
}
|
||||
258
playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1
Normal file
258
playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1
Normal file
@@ -0,0 +1,258 @@
|
||||
# Install-FromManifest.ps1 - Generic JSON-manifest installer for cross-PC-type
|
||||
# apps enforced from the SFLD share (Acrobat Reader DC today; others later).
|
||||
#
|
||||
# Duplicated from CMM\lib\Install-FromManifest.ps1 with a few differences:
|
||||
# - adds Type=CMD (cmd.exe /c wrapper, needed for Acrobat's two-step
|
||||
# MSI + MSP install that the vendor ships as Install-AcroReader.cmd)
|
||||
# - unchanged otherwise; a future pass will unify both libraries.
|
||||
#
|
||||
# Called from:
|
||||
# - Acrobat-Enforce.ps1 on logon with InstallerRoot=<mounted tsgwp00525 share>
|
||||
#
|
||||
# Returns via exit code: 0 if every required app is either already installed
|
||||
# or installed successfully; non-zero if any install failed.
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$ManifestPath,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$InstallerRoot,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
$logDir = Split-Path -Parent $LogFile
|
||||
if (-not (Test-Path $logDir)) {
|
||||
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
|
||||
}
|
||||
|
||||
function Write-InstallLog {
|
||||
param(
|
||||
[Parameter(Mandatory=$true, Position=0)]
|
||||
[string]$Message,
|
||||
[Parameter(Position=1)]
|
||||
[ValidateSet('INFO','WARN','ERROR')]
|
||||
[string]$Level = 'INFO'
|
||||
)
|
||||
$stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$line = "[$stamp] [$Level] $Message"
|
||||
Write-Host $line
|
||||
try {
|
||||
$fs = New-Object System.IO.FileStream(
|
||||
$LogFile,
|
||||
[System.IO.FileMode]::Append,
|
||||
[System.IO.FileAccess]::Write,
|
||||
[System.IO.FileShare]::Read,
|
||||
4096,
|
||||
[System.IO.FileOptions]::WriteThrough
|
||||
)
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($line + "`r`n")
|
||||
$fs.Write($bytes, 0, $bytes.Length)
|
||||
$fs.Flush()
|
||||
$fs.Dispose()
|
||||
} catch {
|
||||
Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
Write-InstallLog "================================================================"
|
||||
Write-InstallLog "=== Install-FromManifest session start (PID $PID) ==="
|
||||
Write-InstallLog "Manifest: $ManifestPath"
|
||||
Write-InstallLog "InstallerRoot: $InstallerRoot"
|
||||
Write-InstallLog "================================================================"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $ManifestPath)) {
|
||||
Write-InstallLog "Manifest not found: $ManifestPath" "ERROR"
|
||||
exit 2
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $InstallerRoot)) {
|
||||
Write-InstallLog "InstallerRoot not found: $InstallerRoot" "ERROR"
|
||||
exit 2
|
||||
}
|
||||
|
||||
try {
|
||||
$config = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json
|
||||
} catch {
|
||||
Write-InstallLog "Failed to parse manifest: $_" "ERROR"
|
||||
exit 2
|
||||
}
|
||||
|
||||
if (-not $config.Applications) {
|
||||
Write-InstallLog "No Applications in manifest - nothing to do"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-InstallLog "Manifest lists $($config.Applications.Count) app(s)"
|
||||
|
||||
function Test-AppInstalled {
|
||||
param($App)
|
||||
|
||||
if (-not $App.DetectionMethod) { return $false }
|
||||
|
||||
try {
|
||||
switch ($App.DetectionMethod) {
|
||||
"Registry" {
|
||||
if (-not (Test-Path $App.DetectionPath)) { return $false }
|
||||
if ($App.DetectionName) {
|
||||
$value = Get-ItemProperty -Path $App.DetectionPath -Name $App.DetectionName -ErrorAction SilentlyContinue
|
||||
if (-not $value) { return $false }
|
||||
if ($App.DetectionValue) {
|
||||
return ($value.$($App.DetectionName) -eq $App.DetectionValue)
|
||||
}
|
||||
return $true
|
||||
}
|
||||
return $true
|
||||
}
|
||||
"File" {
|
||||
return Test-Path $App.DetectionPath
|
||||
}
|
||||
default {
|
||||
Write-InstallLog " Unknown detection method: $($App.DetectionMethod)" "WARN"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-InstallLog " Detection check threw: $_" "WARN"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
$installed = 0
|
||||
$skipped = 0
|
||||
$failed = 0
|
||||
|
||||
foreach ($app in $config.Applications) {
|
||||
cmd /c "shutdown /a 2>nul" *>$null
|
||||
|
||||
Write-InstallLog "==> $($app.Name)"
|
||||
|
||||
if (Test-AppInstalled -App $app) {
|
||||
Write-InstallLog " Already installed at expected version - skipping"
|
||||
$skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
$installerPath = Join-Path $InstallerRoot $app.Installer
|
||||
if (-not (Test-Path -LiteralPath $installerPath)) {
|
||||
Write-InstallLog " Installer file not found: $installerPath" "ERROR"
|
||||
$failed++
|
||||
continue
|
||||
}
|
||||
|
||||
Write-InstallLog " Installing from $installerPath"
|
||||
if ($app.InstallArgs) {
|
||||
Write-InstallLog " InstallArgs: $($app.InstallArgs)"
|
||||
}
|
||||
|
||||
try {
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.UseShellExecute = $false
|
||||
$psi.CreateNoWindow = $true
|
||||
$psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
|
||||
|
||||
$msiLog = $null
|
||||
|
||||
if ($app.Type -eq "MSI") {
|
||||
$safeName = $app.Name -replace '[^a-zA-Z0-9]','_'
|
||||
$msiLog = Join-Path $logDir "msi-$safeName.log"
|
||||
if (Test-Path $msiLog) { Remove-Item $msiLog -Force -ErrorAction SilentlyContinue }
|
||||
|
||||
$psi.FileName = "msiexec.exe"
|
||||
$psi.Arguments = "/i `"$installerPath`""
|
||||
if ($app.InstallArgs) { $psi.Arguments += " " + $app.InstallArgs }
|
||||
$psi.Arguments += " /L*v `"$msiLog`""
|
||||
Write-InstallLog " msiexec verbose log: $msiLog"
|
||||
}
|
||||
elseif ($app.Type -eq "EXE") {
|
||||
$psi.FileName = $installerPath
|
||||
if ($app.InstallArgs) { $psi.Arguments = $app.InstallArgs }
|
||||
if ($app.LogFile) {
|
||||
Write-InstallLog " Installer log: $($app.LogFile)"
|
||||
}
|
||||
}
|
||||
elseif ($app.Type -eq "CMD") {
|
||||
# .cmd/.bat scripts cannot be executed directly via
|
||||
# ProcessStartInfo with UseShellExecute=false; route through
|
||||
# cmd.exe /c. Vendor-provided two-step install wrappers
|
||||
# (Install-AcroReader.cmd) fit here naturally.
|
||||
$psi.FileName = "cmd.exe"
|
||||
$psi.Arguments = "/c `"$installerPath`""
|
||||
if ($app.InstallArgs) { $psi.Arguments += " " + $app.InstallArgs }
|
||||
if ($app.LogFile) {
|
||||
Write-InstallLog " Installer log: $($app.LogFile)"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-InstallLog " Unsupported Type: $($app.Type) - skipping" "ERROR"
|
||||
$failed++
|
||||
continue
|
||||
}
|
||||
|
||||
$proc = [System.Diagnostics.Process]::Start($psi)
|
||||
$proc.WaitForExit()
|
||||
$exitCode = $proc.ExitCode
|
||||
|
||||
if ($exitCode -eq 0 -or $exitCode -eq 1641 -or $exitCode -eq 3010) {
|
||||
Write-InstallLog " Exit code $exitCode - SUCCESS"
|
||||
if ($exitCode -eq 3010) { Write-InstallLog " (Reboot pending for $($app.Name))" }
|
||||
if ($exitCode -eq 1641) { Write-InstallLog " (Installer initiated a reboot for $($app.Name))" }
|
||||
$installed++
|
||||
}
|
||||
else {
|
||||
Write-InstallLog " Exit code $exitCode - FAILED" "ERROR"
|
||||
|
||||
if (($app.Type -eq "EXE" -or $app.Type -eq "CMD") -and $app.LogFile -and (Test-Path $app.LogFile)) {
|
||||
Write-InstallLog " --- last 30 lines of $($app.LogFile) ---"
|
||||
Get-Content $app.LogFile -Tail 30 -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-InstallLog " $_"
|
||||
}
|
||||
Write-InstallLog " --- end installer log tail ---"
|
||||
}
|
||||
|
||||
if ($app.Type -eq "MSI" -and $msiLog -and (Test-Path $msiLog)) {
|
||||
Write-InstallLog " --- meaningful lines from $msiLog ---"
|
||||
$patterns = @(
|
||||
'Note: 1: ',
|
||||
'return value 3',
|
||||
'Error \d+\.',
|
||||
'CustomAction .* returned actual error',
|
||||
'Failed to ',
|
||||
'Installation failed',
|
||||
'1: 2262',
|
||||
'1: 2203',
|
||||
'1: 2330'
|
||||
)
|
||||
$regex = ($patterns -join '|')
|
||||
$matches = Select-String -Path $msiLog -Pattern $regex -ErrorAction SilentlyContinue |
|
||||
Select-Object -First 30
|
||||
if ($matches) {
|
||||
foreach ($m in $matches) { Write-InstallLog " $($m.Line.Trim())" }
|
||||
} else {
|
||||
Get-Content $msiLog -Tail 25 -ErrorAction SilentlyContinue | ForEach-Object {
|
||||
Write-InstallLog " $_"
|
||||
}
|
||||
}
|
||||
Write-InstallLog " --- end MSI log scan ---"
|
||||
}
|
||||
|
||||
$failed++
|
||||
}
|
||||
} catch {
|
||||
Write-InstallLog " Install threw: $_" "ERROR"
|
||||
$failed++
|
||||
}
|
||||
}
|
||||
|
||||
Write-InstallLog "============================================"
|
||||
Write-InstallLog "Install-FromManifest complete: $installed installed, $skipped skipped, $failed failed"
|
||||
Write-InstallLog "============================================"
|
||||
|
||||
cmd /c "shutdown /a 2>nul" *>$null
|
||||
|
||||
if ($failed -gt 0) { exit 1 }
|
||||
exit 0
|
||||
@@ -55,15 +55,20 @@ Rename-Computer -NewName $newName -Force -ErrorAction SilentlyContinue
|
||||
$ppkgLogDir = "C:\Logs\PPKG"
|
||||
New-Item -ItemType Directory -Path $ppkgLogDir -Force -ErrorAction SilentlyContinue | Out-Null
|
||||
$provtool = Join-Path $env:SystemRoot 'System32\provtool.exe'
|
||||
$provArgs = "/ppkg:`"$($ppkgFile.FullName)`" /quiet /log:`"$ppkgLogDir\provtool.log`""
|
||||
# Arg order matches what the Install-ProvisioningPackage cmdlet invokes
|
||||
# internally (observed in ProvEventLog.txt): positional path, then /quiet,
|
||||
# then /source. No /log: or /ppkg: prefix - those are not valid provtool
|
||||
# flags and caused 0x80004005 E_FAIL in the first test.
|
||||
$provArgs = @("`"$($ppkgFile.FullName)`"", "/quiet", "/source", "BPRT")
|
||||
Log "Installing provisioning package via provtool.exe (no PowerShell timeout)..."
|
||||
Log "Command: $provtool $provArgs"
|
||||
Log "PPKG diagnostic logs -> $ppkgLogDir"
|
||||
Log "Command: $provtool $($provArgs -join ' ')"
|
||||
Log "PPKG diagnostic logs -> $ppkgLogDir (provtool writes them automatically)"
|
||||
try {
|
||||
$p = Start-Process -FilePath $provtool -ArgumentList $provArgs -Wait -PassThru -NoNewWindow -ErrorAction Stop
|
||||
Log "provtool.exe exit code: $($p.ExitCode)"
|
||||
if ($p.ExitCode -ne 0) {
|
||||
Log "WARNING: provtool.exe returned non-zero exit code. Check $ppkgLogDir\provtool.log for details."
|
||||
$hex = '0x{0:X8}' -f $p.ExitCode
|
||||
Log "WARNING: provtool.exe returned non-zero exit code ($hex). Check $ppkgLogDir for diagnostic bundle."
|
||||
}
|
||||
} catch {
|
||||
Log "ERROR: Failed to launch provtool.exe: $_"
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
"excludeShortcuts": [ "WJ_Office.lnk", "IBM_qks.lnk", "mmcs.lnk" ]
|
||||
},
|
||||
|
||||
"common": {
|
||||
"_comment": "Cross-PC-type share paths used by logon enforcers (Acrobat-Enforce, future analogues). One SFLD share path per app; enforcer mounts the share with SFLD creds from HKLM:\\SOFTWARE\\GE\\SFLD\\Credentials and applies acrobat-manifest.json etc.",
|
||||
"acrobatSharePath": "\\\\tsgwp00525.wjs.geaerospace.net\\shared\\dt\\shopfloor\\common\\acrobat"
|
||||
},
|
||||
|
||||
"startupItems": [
|
||||
{ "label": "UDC", "type": "exe", "target": "C:\\Program Files\\UDC\\UDC.exe" },
|
||||
{ "label": "eDNC", "type": "exe", "target": "C:\\Program Files (x86)\\Dnc\\bin\\DncMain.exe" },
|
||||
|
||||
Reference in New Issue
Block a user