- Add -Reboot/-MinUptimeDays parameter set to reboot high-uptime PCs via API - Add WebClient fallback for TLS/SSL connection errors on API calls - Add per-user registry scanning (HKU hives) for installed app detection - Add Dashboard and Lobby Display kiosk app detection (app IDs 82, 83) - Add SkipCertificateCheck support for PowerShell 7+ - Increase session timeouts (20s connect, 120s operation) - Increase default ThrottleLimit from 10 to 25 - Add Install-KioskApp.ps1 and user registry detection test scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
274 lines
9.2 KiB
PowerShell
274 lines
9.2 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Remote installer for Dashboard and Lobby Display kiosk applications.
|
|
|
|
.DESCRIPTION
|
|
Deploys GE Aerospace Dashboard or Lobby Display kiosk installers to remote PCs via WinRM.
|
|
Pushes the installer file and runs it silently.
|
|
|
|
.PARAMETER ComputerName
|
|
Single computer name, IP address, or array of computers to target.
|
|
|
|
.PARAMETER ComputerListFile
|
|
Path to text file containing computer names/IPs (one per line).
|
|
|
|
.PARAMETER App
|
|
Which application to install: Dashboard or LobbyDisplay
|
|
|
|
.PARAMETER InstallerPath
|
|
Path to the installer .exe file. If not specified, looks in the script directory.
|
|
|
|
.PARAMETER Credential
|
|
PSCredential for remote authentication. Prompts if not provided.
|
|
|
|
.PARAMETER Uninstall
|
|
Uninstall the application instead of installing.
|
|
|
|
.EXAMPLE
|
|
# Install Dashboard on a single PC
|
|
.\Install-KioskApp.ps1 -ComputerName "PC001" -App Dashboard
|
|
|
|
.EXAMPLE
|
|
# Install Lobby Display on multiple PCs from file
|
|
.\Install-KioskApp.ps1 -ComputerListFile ".\lobby-pcs.txt" -App LobbyDisplay
|
|
|
|
.EXAMPLE
|
|
# Uninstall Dashboard from a PC
|
|
.\Install-KioskApp.ps1 -ComputerName "PC001" -App Dashboard -Uninstall
|
|
|
|
.NOTES
|
|
Author: Shop Floor Tools
|
|
Requires: PowerShell 5.1+, WinRM enabled on targets, Admin credentials
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Position=0)]
|
|
[string[]]$ComputerName,
|
|
|
|
[Parameter()]
|
|
[string]$ComputerListFile,
|
|
|
|
[Parameter(Mandatory=$true)]
|
|
[ValidateSet('Dashboard', 'LobbyDisplay')]
|
|
[string]$App,
|
|
|
|
[Parameter()]
|
|
[string]$InstallerPath,
|
|
|
|
[Parameter()]
|
|
[PSCredential]$Credential,
|
|
|
|
[Parameter()]
|
|
[string]$DnsSuffix = "logon.ds.ge.com",
|
|
|
|
[Parameter()]
|
|
[switch]$Uninstall
|
|
)
|
|
|
|
# =============================================================================
|
|
# Configuration
|
|
# =============================================================================
|
|
$AppConfig = @{
|
|
'Dashboard' = @{
|
|
InstallerName = 'GEAerospaceDashboardSetup.exe'
|
|
AppName = 'GE Aerospace Dashboard'
|
|
UninstallGuid = '{9D9EEE25-4D24-422D-98AF-2ADEDA4745ED}'
|
|
}
|
|
'LobbyDisplay' = @{
|
|
InstallerName = 'GEAerospaceLobbyDisplaySetup.exe'
|
|
AppName = 'GE Aerospace Lobby Display'
|
|
UninstallGuid = '{42FFB952-0B72-493F-8869-D957344CA305}'
|
|
}
|
|
}
|
|
|
|
$config = $AppConfig[$App]
|
|
|
|
# =============================================================================
|
|
# Helper Functions
|
|
# =============================================================================
|
|
|
|
function Write-Log {
|
|
param([string]$Message, [string]$Level = "INFO")
|
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
$color = switch ($Level) {
|
|
"ERROR" { "Red" }
|
|
"WARNING" { "Yellow" }
|
|
"SUCCESS" { "Green" }
|
|
"TASK" { "Cyan" }
|
|
default { "White" }
|
|
}
|
|
Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color
|
|
}
|
|
|
|
# =============================================================================
|
|
# Main Execution
|
|
# =============================================================================
|
|
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " Kiosk App Installer - $($config.AppName)" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# Get credentials
|
|
if (-not $Credential) {
|
|
Write-Log "Enter credentials for remote PCs:" -Level "INFO"
|
|
$Credential = Get-Credential -Message "Enter admin credentials for remote PCs"
|
|
if (-not $Credential) {
|
|
Write-Log "Credentials required. Exiting." -Level "ERROR"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Build computer list
|
|
$computers = @()
|
|
|
|
if ($ComputerListFile) {
|
|
if (Test-Path $ComputerListFile) {
|
|
$computers = Get-Content $ComputerListFile | Where-Object { $_.Trim() -and -not $_.StartsWith("#") }
|
|
} else {
|
|
Write-Log "Computer list file not found: $ComputerListFile" -Level "ERROR"
|
|
exit 1
|
|
}
|
|
} elseif ($ComputerName) {
|
|
$computers = $ComputerName
|
|
} else {
|
|
Write-Log "No computers specified. Use -ComputerName or -ComputerListFile" -Level "ERROR"
|
|
exit 1
|
|
}
|
|
|
|
if ($computers.Count -eq 0) {
|
|
Write-Log "No computers to process." -Level "ERROR"
|
|
exit 1
|
|
}
|
|
|
|
Write-Log "Target computers: $($computers.Count)" -Level "INFO"
|
|
Write-Log "Action: $(if ($Uninstall) { 'Uninstall' } else { 'Install' })" -Level "TASK"
|
|
Write-Host ""
|
|
|
|
# Find installer if not specified (for install only)
|
|
if (-not $Uninstall) {
|
|
if (-not $InstallerPath) {
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$InstallerPath = Join-Path $scriptDir $config.InstallerName
|
|
}
|
|
|
|
if (-not (Test-Path $InstallerPath)) {
|
|
Write-Log "Installer not found: $InstallerPath" -Level "ERROR"
|
|
Write-Log "Please specify -InstallerPath or place $($config.InstallerName) in the script directory" -Level "ERROR"
|
|
exit 1
|
|
}
|
|
|
|
Write-Log "Installer: $InstallerPath" -Level "INFO"
|
|
}
|
|
|
|
# Build FQDNs
|
|
$targetFQDNs = $computers | ForEach-Object {
|
|
if ($_ -like "*.*") { $_ } else { "$_.$DnsSuffix" }
|
|
}
|
|
|
|
# Create session options
|
|
$sessionOption = New-PSSessionOption -OpenTimeout 30000 -OperationTimeout 300000 -NoMachineProfile
|
|
|
|
$successCount = 0
|
|
$failCount = 0
|
|
|
|
foreach ($fqdn in $targetFQDNs) {
|
|
Write-Host ""
|
|
Write-Log "Processing: $fqdn" -Level "TASK"
|
|
|
|
try {
|
|
# Create session
|
|
$session = New-PSSession -ComputerName $fqdn -Credential $Credential -SessionOption $sessionOption -Authentication Negotiate -ErrorAction Stop
|
|
|
|
if ($Uninstall) {
|
|
# Uninstall
|
|
Write-Log " Uninstalling $($config.AppName)..." -Level "INFO"
|
|
|
|
$result = Invoke-Command -Session $session -ScriptBlock {
|
|
param($guid, $appName)
|
|
$result = @{ Success = $false; Output = ""; Error = $null }
|
|
|
|
# Find uninstaller
|
|
$uninstallPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$guid`_is1"
|
|
if (-not (Test-Path $uninstallPath)) {
|
|
$uninstallPath = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$guid`_is1"
|
|
}
|
|
|
|
if (Test-Path $uninstallPath) {
|
|
$uninstallString = (Get-ItemProperty $uninstallPath).UninstallString
|
|
if ($uninstallString) {
|
|
# Run uninstaller silently
|
|
$uninstallExe = $uninstallString -replace '"', ''
|
|
$proc = Start-Process -FilePath $uninstallExe -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART" -Wait -PassThru -WindowStyle Hidden
|
|
$result.Success = ($proc.ExitCode -eq 0)
|
|
$result.Output = "Exit code: $($proc.ExitCode)"
|
|
} else {
|
|
$result.Error = "No uninstall string found"
|
|
}
|
|
} else {
|
|
$result.Error = "$appName not found in registry"
|
|
}
|
|
|
|
return $result
|
|
} -ArgumentList $config.UninstallGuid, $config.AppName
|
|
|
|
} else {
|
|
# Install
|
|
$remoteTempPath = "C:\Windows\Temp\$($config.InstallerName)"
|
|
|
|
Write-Log " Pushing installer to remote PC..." -Level "INFO"
|
|
Copy-Item -Path $InstallerPath -Destination $remoteTempPath -ToSession $session -Force -ErrorAction Stop
|
|
|
|
Write-Log " Running installer silently..." -Level "INFO"
|
|
$result = Invoke-Command -Session $session -ScriptBlock {
|
|
param($installerPath)
|
|
$result = @{ Success = $false; Output = ""; Error = $null }
|
|
|
|
try {
|
|
$proc = Start-Process -FilePath $installerPath -ArgumentList "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART" -Wait -PassThru -WindowStyle Hidden
|
|
$result.Success = ($proc.ExitCode -eq 0)
|
|
$result.Output = "Exit code: $($proc.ExitCode)"
|
|
|
|
# Clean up installer
|
|
Remove-Item $installerPath -Force -ErrorAction SilentlyContinue
|
|
} catch {
|
|
$result.Error = $_.Exception.Message
|
|
}
|
|
|
|
return $result
|
|
} -ArgumentList $remoteTempPath
|
|
}
|
|
|
|
# Close session
|
|
Remove-PSSession $session -ErrorAction SilentlyContinue
|
|
|
|
# Process result
|
|
if ($result.Success) {
|
|
Write-Log "[OK] $fqdn - $($result.Output)" -Level "SUCCESS"
|
|
$successCount++
|
|
} else {
|
|
$errorMsg = if ($result.Error) { $result.Error } else { $result.Output }
|
|
Write-Log "[FAIL] $fqdn - $errorMsg" -Level "ERROR"
|
|
$failCount++
|
|
}
|
|
|
|
} catch {
|
|
Write-Log "[FAIL] ${fqdn}: $($_.Exception.Message)" -Level "ERROR"
|
|
$failCount++
|
|
if ($session) { Remove-PSSession $session -ErrorAction SilentlyContinue }
|
|
}
|
|
}
|
|
|
|
# Summary
|
|
Write-Host ""
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " SUMMARY" -ForegroundColor Cyan
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|
|
Write-Host " App: $($config.AppName)" -ForegroundColor White
|
|
Write-Host " Action: $(if ($Uninstall) { 'Uninstall' } else { 'Install' })" -ForegroundColor White
|
|
Write-Host " Successful: $successCount" -ForegroundColor Green
|
|
Write-Host " Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "White" })
|
|
Write-Host ("=" * 70) -ForegroundColor Cyan
|