mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 00:17:39 +01:00
Fixes #755. Adds interactive pauses and graphical popups (where available) to installer scripts when critical errors occur, ensuring troubleshooting guides are readable. Also clarifies 'build from source' instructions.
568 lines
20 KiB
PowerShell
568 lines
20 KiB
PowerShell
# Pulse Host Agent Installation Script for Windows
|
|
#
|
|
# Usage:
|
|
# iwr -useb http://pulse-server:7656/install-host-agent.ps1 | iex
|
|
# OR with parameters:
|
|
# $url = "http://pulse-server:7656"; $token = "your-token"; iwr -useb "$url/install-host-agent.ps1" | iex
|
|
#
|
|
# Parameters can be passed via environment variables or script parameters
|
|
|
|
param(
|
|
[string]$PulseUrl = $env:PULSE_URL,
|
|
[string]$Token = $env:PULSE_TOKEN,
|
|
[string]$Interval = $env:PULSE_INTERVAL,
|
|
[string]$AgentId = $env:PULSE_AGENT_ID,
|
|
[string]$InstallPath = "C:\Program Files\Pulse",
|
|
[string]$Arch = $env:PULSE_ARCH,
|
|
[switch]$NoService
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Write-PulseMessage {
|
|
param(
|
|
[string]$Label,
|
|
[string]$Message,
|
|
[ConsoleColor]$Color
|
|
)
|
|
|
|
if ($Label) {
|
|
Write-Host ("[{0}] {1}" -f $Label, $Message) -ForegroundColor $Color
|
|
} else {
|
|
Write-Host $Message -ForegroundColor $Color
|
|
}
|
|
}
|
|
|
|
function PulseSuccess { param([string]$msg) Write-PulseMessage -Label 'OK' -Message $msg -Color 'Green' }
|
|
function PulseError { param([string]$msg) Write-PulseMessage -Label 'FAIL' -Message $msg -Color 'Red' }
|
|
function PulseInfo { param([string]$msg) Write-PulseMessage -Label 'INFO' -Message $msg -Color 'Cyan' }
|
|
function PulseWarn { param([string]$msg) Write-PulseMessage -Label 'WARN' -Message $msg -Color 'Yellow' }
|
|
|
|
function Write-InstallerEvent {
|
|
param(
|
|
[string]$SourceName,
|
|
[string]$Message,
|
|
[ValidateSet('Information', 'Warning', 'Error')] [string]$EntryType = 'Information',
|
|
[int]$EventId = 1000
|
|
)
|
|
|
|
if (-not $SourceName) { return }
|
|
|
|
try {
|
|
Write-EventLog -LogName Application -Source $SourceName -EventId $EventId -EntryType $EntryType -Message $Message
|
|
} catch {
|
|
PulseWarn "Unable to write installer event log entry: $_"
|
|
}
|
|
}
|
|
|
|
try {
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13
|
|
} catch {
|
|
# Ignore if platform does not expose TLS 1.3
|
|
}
|
|
|
|
function Get-RecentAgentEvents {
|
|
param(
|
|
[string]$ProviderName,
|
|
[int]$Max = 5
|
|
)
|
|
try {
|
|
return Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = $ProviderName } -MaxEvents $Max -ErrorAction Stop
|
|
} catch {
|
|
return Get-EventLog -LogName Application -Source $ProviderName -Newest $Max -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
function Test-AgentRegistration {
|
|
param(
|
|
[string]$PulseUrl,
|
|
[string]$Hostname,
|
|
[string]$Token
|
|
)
|
|
|
|
if (-not $Token) {
|
|
return $null
|
|
}
|
|
|
|
try {
|
|
$encodedHostname = [System.Uri]::EscapeDataString($Hostname)
|
|
$lookupUri = "$PulseUrl/api/agents/host/lookup?hostname=$encodedHostname"
|
|
$headers = @{ Authorization = "Bearer $Token" }
|
|
$response = Invoke-RestMethod -Uri $lookupUri -Headers $headers -Method Get -ErrorAction Stop
|
|
return $response.host
|
|
} catch {
|
|
return $null
|
|
}
|
|
}
|
|
|
|
function Resolve-PulseArchitectureCandidate {
|
|
param(
|
|
[string]$CandidateValue,
|
|
[string]$Source
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($CandidateValue)) {
|
|
return $null
|
|
}
|
|
|
|
$normalized = $CandidateValue.Trim()
|
|
$upper = $normalized.ToUpperInvariant()
|
|
|
|
$mapping = $null
|
|
if ($upper -match 'ARM64|AARCH64') {
|
|
$mapping = @{ OsLabel = 'Arm64'; DownloadArch = 'arm64' }
|
|
} elseif ($upper -match 'AMD64|X64|64-BIT') {
|
|
$mapping = @{ OsLabel = 'X64'; DownloadArch = 'amd64' }
|
|
} elseif ($upper -match 'X86|I386|IA32|32-BIT') {
|
|
$mapping = @{ OsLabel = 'X86'; DownloadArch = '386' }
|
|
}
|
|
|
|
if (-not $mapping) {
|
|
return $null
|
|
}
|
|
|
|
return [pscustomobject]@{
|
|
OsLabel = $mapping.OsLabel
|
|
DownloadArch = $mapping.DownloadArch
|
|
RawValue = $normalized
|
|
Source = $Source
|
|
UsedHeuristic = $false
|
|
ObservedValues = @()
|
|
}
|
|
}
|
|
|
|
function Get-PulseArchitecture {
|
|
param(
|
|
[string]$OverrideArch
|
|
)
|
|
|
|
$observations = @()
|
|
|
|
$candidateSources = @()
|
|
if ($OverrideArch) {
|
|
$candidateSources += @{ Name = 'Override'; Getter = { $OverrideArch } }
|
|
}
|
|
|
|
$candidateSources += @(
|
|
@{ Name = 'RuntimeInformation.OSArchitecture'; Getter = { [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture } },
|
|
@{ Name = 'mscorlib.RuntimeInformation.OSArchitecture'; Getter = { [System.Runtime.InteropServices.RuntimeInformation,mscorlib]::OSArchitecture } },
|
|
@{ Name = 'Win32_OperatingSystem (CIM)'; Getter = { (Get-CimInstance -ClassName Win32_OperatingSystem -Property OSArchitecture -ErrorAction Stop).OSArchitecture } },
|
|
@{ Name = 'Win32_OperatingSystem (WMI)'; Getter = { (Get-WmiObject -Class Win32_OperatingSystem -ErrorAction Stop).OSArchitecture } },
|
|
@{ Name = 'PROCESSOR_ARCHITEW6432'; Getter = { $env:PROCESSOR_ARCHITEW6432 } },
|
|
@{ Name = 'PROCESSOR_ARCHITECTURE'; Getter = { $env:PROCESSOR_ARCHITECTURE } },
|
|
@{ Name = 'PROCESSOR_IDENTIFIER'; Getter = { $env:PROCESSOR_IDENTIFIER } }
|
|
)
|
|
|
|
foreach ($source in $candidateSources) {
|
|
try {
|
|
$value = & $source.Getter
|
|
} catch {
|
|
continue
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($value)) {
|
|
continue
|
|
}
|
|
|
|
$valueString = $value.ToString().Trim()
|
|
$observations += [pscustomobject]@{ Source = $source.Name; Value = $valueString }
|
|
|
|
$resolved = Resolve-PulseArchitectureCandidate -CandidateValue $valueString -Source $source.Name
|
|
if ($resolved) {
|
|
$resolved.ObservedValues = $observations
|
|
return $resolved
|
|
}
|
|
}
|
|
|
|
$armHint = $observations | Where-Object { $_.Value -match 'ARM' } | Select-Object -First 1
|
|
if ($armHint) {
|
|
return [pscustomobject]@{
|
|
OsLabel = 'Arm64'
|
|
DownloadArch = 'arm64'
|
|
RawValue = $armHint.Value
|
|
Source = "$($armHint.Source) heuristic"
|
|
UsedHeuristic = $true
|
|
ObservedValues = $observations
|
|
}
|
|
}
|
|
|
|
$is64BitOS = $null
|
|
try { $is64BitOS = [Environment]::Is64BitOperatingSystem } catch { }
|
|
if ($is64BitOS -eq $null) {
|
|
$is64BitOS = [System.IntPtr]::Size -ge 8
|
|
}
|
|
|
|
if ($is64BitOS -eq $true) {
|
|
return [pscustomobject]@{
|
|
OsLabel = 'X64'
|
|
DownloadArch = 'amd64'
|
|
RawValue = 'Environment.Is64BitOperatingSystem=True'
|
|
Source = 'Environment heuristic'
|
|
UsedHeuristic = $true
|
|
ObservedValues = $observations
|
|
}
|
|
}
|
|
|
|
if ($is64BitOS -eq $false) {
|
|
return [pscustomobject]@{
|
|
OsLabel = 'X86'
|
|
DownloadArch = '386'
|
|
RawValue = 'Environment.Is64BitOperatingSystem=False'
|
|
Source = 'Environment heuristic'
|
|
UsedHeuristic = $true
|
|
ObservedValues = $observations
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
Write-Host ""
|
|
$banner = "=" * 59
|
|
Write-Host $banner -ForegroundColor Cyan
|
|
Write-Host " Pulse Host Agent - Windows Installation" -ForegroundColor Cyan
|
|
Write-Host $banner -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# Check if running as Administrator
|
|
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
if (-not $isAdmin) {
|
|
PulseError "This script must be run as Administrator"
|
|
PulseInfo "Right-click PowerShell and select 'Run as Administrator'"
|
|
exit 1
|
|
}
|
|
|
|
# Interactive prompts if parameters not provided
|
|
if (-not $PulseUrl) {
|
|
$PulseUrl = Read-Host "Enter Pulse server URL (e.g., http://pulse.example.com:7656)"
|
|
}
|
|
$PulseUrl = $PulseUrl.TrimEnd('/')
|
|
|
|
if (-not $Token) {
|
|
PulseWarn "No API token provided - agent will attempt to connect without authentication"
|
|
$response = Read-Host "Continue without token? (y/N)"
|
|
if ($response -ne 'y' -and $response -ne 'Y') {
|
|
$Token = Read-Host "Enter API token"
|
|
}
|
|
}
|
|
|
|
if (-not $Interval) {
|
|
$Interval = "30s"
|
|
}
|
|
|
|
PulseInfo "Configuration:"
|
|
Write-Host " Pulse URL: $PulseUrl"
|
|
Write-Host " Token: $(if ($Token) { '***' + $Token.Substring([Math]::Max(0, $Token.Length - 4)) } else { 'none' })"
|
|
Write-Host " Agent ID: $(if ($AgentId) { $AgentId } else { 'machine-id (default)' })"
|
|
Write-Host " Interval: $Interval"
|
|
Write-Host " Install Path: $InstallPath"
|
|
if ($Arch) {
|
|
Write-Host " Architecture Override: $Arch"
|
|
}
|
|
Write-Host ""
|
|
|
|
# Determine architecture (support both legacy Windows PowerShell and pwsh)
|
|
if ($Arch) {
|
|
if (-not (Resolve-PulseArchitectureCandidate -CandidateValue $Arch -Source "Override")) {
|
|
PulseError "Invalid architecture override '$Arch'. Supported values: amd64, arm64, 386"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
$archInfo = Get-PulseArchitecture -OverrideArch $Arch
|
|
$observedSummary = $null
|
|
if ($archInfo -and $archInfo.ObservedValues) {
|
|
$observedSummary = ($archInfo.ObservedValues | ForEach-Object { "$($_.Source)='$($_.Value)'" }) -join "; "
|
|
}
|
|
|
|
if (-not $archInfo -or -not $archInfo.DownloadArch) {
|
|
PulseError "Unable to determine operating system architecture"
|
|
if ($observedSummary) {
|
|
PulseInfo "Observed architecture values: $observedSummary"
|
|
}
|
|
PulseInfo "Specify -Arch amd64|arm64|386 or set PULSE_ARCH to override detection."
|
|
exit 1
|
|
}
|
|
|
|
$osArch = $archInfo.OsLabel
|
|
$arch = $archInfo.DownloadArch
|
|
$rawArchValue = $archInfo.RawValue
|
|
$archSource = if ($archInfo.Source) { $archInfo.Source } else { "auto-detected" }
|
|
$downloadUrl = "$PulseUrl/download/pulse-host-agent?platform=windows&arch=$arch"
|
|
|
|
PulseInfo "System Information:"
|
|
if ($rawArchValue -and $rawArchValue -ne $osArch) {
|
|
Write-Host " OS Architecture: $osArch (reported as '$rawArchValue')"
|
|
} else {
|
|
Write-Host " OS Architecture: $osArch"
|
|
}
|
|
Write-Host " Detection Source: $archSource"
|
|
Write-Host " Download Architecture: $arch"
|
|
Write-Host " Download URL: $downloadUrl"
|
|
Write-Host ""
|
|
|
|
if ($archInfo.UsedHeuristic) {
|
|
PulseWarn "Architecture detected via fallback ($archSource). If this looks wrong, rerun with -Arch amd64|arm64|386 or set PULSE_ARCH."
|
|
if ($observedSummary) {
|
|
PulseInfo "Observed architecture values: $observedSummary"
|
|
}
|
|
}
|
|
|
|
PulseInfo "Downloading agent binary from $downloadUrl..."
|
|
try {
|
|
# Create install directory
|
|
if (-not (Test-Path $InstallPath)) {
|
|
New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
|
|
}
|
|
|
|
$agentPath = Join-Path $InstallPath "pulse-host-agent.exe"
|
|
|
|
# Download binary
|
|
Invoke-WebRequest -Uri $downloadUrl -OutFile $agentPath -UseBasicParsing
|
|
PulseSuccess "Downloaded agent to $agentPath"
|
|
|
|
# Validate PE header
|
|
$fileBytes = [System.IO.File]::ReadAllBytes($agentPath)
|
|
$fileSizeMB = [math]::Round($fileBytes.Length / 1MB, 2)
|
|
PulseInfo "File size: $fileSizeMB MB ($($fileBytes.Length) bytes)"
|
|
|
|
if ($fileBytes.Length -lt 64) {
|
|
throw "Downloaded file is too small ($($fileBytes.Length) bytes) - expected Windows PE executable"
|
|
}
|
|
|
|
# Check for MZ signature (PE header)
|
|
if ($fileBytes[0] -ne 0x4D -or $fileBytes[1] -ne 0x5A) {
|
|
$firstBytes = ($fileBytes[0..15] | ForEach-Object { $_.ToString("X2") }) -join " "
|
|
throw "Downloaded file is not a valid Windows executable (missing MZ signature). First bytes: $firstBytes"
|
|
}
|
|
|
|
# Get PE header offset (at 0x3C)
|
|
$peOffset = [BitConverter]::ToUInt32($fileBytes, 0x3C)
|
|
if ($peOffset -ge $fileBytes.Length - 6) {
|
|
throw "Invalid PE header offset in downloaded file"
|
|
}
|
|
|
|
# Check PE signature
|
|
if ($fileBytes[$peOffset] -ne 0x50 -or $fileBytes[$peOffset+1] -ne 0x45) {
|
|
throw "Downloaded file has invalid PE signature"
|
|
}
|
|
|
|
# Check machine type (should be 0x8664 for x64, 0xAA64 for ARM64)
|
|
$machineType = [BitConverter]::ToUInt16($fileBytes, $peOffset + 4)
|
|
$expectedMachine = switch ($arch) {
|
|
'amd64' { 0x8664 }
|
|
'arm64' { 0xAA64 }
|
|
'386' { 0x014C }
|
|
default { 0x0000 }
|
|
}
|
|
|
|
if ($machineType -ne $expectedMachine) {
|
|
$machineStr = "0x" + $machineType.ToString("X4")
|
|
$expectedStr = "0x" + $expectedMachine.ToString("X4")
|
|
throw "Downloaded binary is for wrong architecture (got $machineStr, expected $expectedStr for $arch)"
|
|
}
|
|
|
|
PulseSuccess "Verified PE executable for $osArch architecture"
|
|
|
|
# Verify checksum
|
|
PulseInfo "Verifying checksum..."
|
|
$checksumUrl = "$PulseUrl/download/pulse-host-agent.sha256?platform=windows&arch=$arch"
|
|
try {
|
|
$expectedChecksum = (Invoke-WebRequest -Uri $checksumUrl -UseBasicParsing).Content.Trim().Split()[0]
|
|
$actualChecksum = (Get-FileHash -Path $agentPath -Algorithm SHA256).Hash.ToLower()
|
|
|
|
if ($actualChecksum -ne $expectedChecksum.ToLower()) {
|
|
throw "Checksum mismatch! Expected: $expectedChecksum, Got: $actualChecksum"
|
|
}
|
|
PulseSuccess "Checksum verified: $actualChecksum"
|
|
} catch {
|
|
PulseWarn "Could not verify checksum: $_"
|
|
PulseInfo "Continuing anyway (PE header was validated)"
|
|
}
|
|
|
|
$agentArgs = @("--url", "`"$PulseUrl`"", "--interval", $Interval)
|
|
if ($Token) {
|
|
$agentArgs += @("--token", "`"$Token`"")
|
|
}
|
|
if ($AgentId) {
|
|
$agentArgs += @("--agent-id", "`"$AgentId`"")
|
|
}
|
|
$serviceBinaryPath = "`"$agentPath`" $($agentArgs -join ' ')"
|
|
$manualCommand = "& `"$agentPath`" $($agentArgs -join ' ')"
|
|
} catch {
|
|
PulseError "Failed to download agent: $_"
|
|
|
|
# Try to show a popup if running in a GUI environment
|
|
try {
|
|
Add-Type -AssemblyName System.Windows.Forms
|
|
[System.Windows.Forms.MessageBox]::Show("Failed to download agent.`n`n$_", "Pulse Installation Failed", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) | Out-Null
|
|
} catch {
|
|
# Ignore if GUI not available
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Press Enter to exit..." -ForegroundColor Yellow
|
|
Read-Host
|
|
exit 1
|
|
}
|
|
|
|
# Create configuration
|
|
$configPath = Join-Path $InstallPath "config.json"
|
|
$config = @{
|
|
url = $PulseUrl
|
|
interval = $Interval
|
|
}
|
|
if ($Token) {
|
|
$config.token = $Token
|
|
}
|
|
if ($AgentId) {
|
|
$config.agentId = $AgentId
|
|
}
|
|
|
|
$config | ConvertTo-Json | Set-Content $configPath
|
|
PulseSuccess "Created configuration at $configPath"
|
|
|
|
# Stop existing service if running
|
|
$serviceName = "PulseHostAgent"
|
|
$existingService = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
|
|
if ($existingService) {
|
|
PulseInfo "Stopping existing service..."
|
|
Stop-Service -Name $serviceName -Force
|
|
PulseSuccess "Stopped existing service"
|
|
}
|
|
|
|
if (-not $NoService) {
|
|
PulseInfo "Installing native Windows service with built-in service support..."
|
|
|
|
try {
|
|
if ($existingService) {
|
|
PulseInfo "Removing existing service..."
|
|
sc.exe delete $serviceName | Out-Null
|
|
Start-Sleep -Seconds 2
|
|
}
|
|
|
|
# Create the service using New-Service
|
|
New-Service -Name $serviceName `
|
|
-BinaryPathName $serviceBinaryPath `
|
|
-DisplayName "Pulse Host Agent" `
|
|
-Description "Monitors system metrics and reports to Pulse monitoring server" `
|
|
-StartupType Automatic | Out-Null
|
|
|
|
PulseSuccess "Created Windows service '$serviceName'"
|
|
|
|
# Register Windows Event Log source
|
|
try {
|
|
if (-not ([System.Diagnostics.EventLog]::SourceExists($serviceName))) {
|
|
New-EventLog -LogName Application -Source $serviceName
|
|
PulseSuccess "Registered Event Log source"
|
|
}
|
|
} catch {
|
|
PulseWarn "Could not register Event Log source (not critical): $_"
|
|
}
|
|
|
|
Write-InstallerEvent -SourceName $serviceName -Message "Pulse Host Agent installer registered service version $(Get-Item $agentPath).VersionInfo.FileVersion" -EventId 1000
|
|
|
|
# Configure service recovery options (restart on failure)
|
|
sc.exe failure $serviceName reset= 86400 actions= restart/60000/restart/60000/restart/60000 | Out-Null
|
|
PulseSuccess "Configured automatic restart on failure"
|
|
|
|
# Start the service
|
|
PulseInfo "Starting service..."
|
|
Start-Service -Name $serviceName
|
|
Start-Sleep -Seconds 3
|
|
|
|
$status = (Get-Service -Name $serviceName).Status
|
|
if ($status -eq 'Running') {
|
|
PulseSuccess "Service started successfully!"
|
|
|
|
PulseInfo "Waiting 10 seconds to validate agent reporting..."
|
|
Start-Sleep -Seconds 10
|
|
|
|
$hostname = $env:COMPUTERNAME
|
|
$lookupHost = Test-AgentRegistration -PulseUrl $PulseUrl -Hostname $hostname -Token $Token
|
|
if ($lookupHost) {
|
|
PulseSuccess "Agent successfully registered with Pulse (host '$hostname')."
|
|
if ($lookupHost.status) {
|
|
$lastSeen = $lookupHost.lastSeen
|
|
if ($lastSeen -is [DateTime]) {
|
|
$lastSeen = $lastSeen.ToString("u")
|
|
}
|
|
PulseInfo ("Pulse reports status: {0} (last seen {1})" -f $lookupHost.status, $lastSeen)
|
|
}
|
|
PulseInfo "Check your Pulse dashboard - this host should appear shortly."
|
|
$statusForLog = if ($lookupHost.status) { $lookupHost.status } else { 'unknown' }
|
|
Write-InstallerEvent -SourceName $serviceName -Message "Installer verified host '$hostname' reporting to Pulse (status: $statusForLog)." -EventId 1010
|
|
} elseif ($Token) {
|
|
PulseWarn "Agent is running but the lookup endpoint has not confirmed registration yet."
|
|
PulseInfo "It may take another moment for metrics to appear in the dashboard."
|
|
Write-InstallerEvent -SourceName $serviceName -Message "Installer could not yet confirm host '$hostname' registration with Pulse." -EntryType Warning -EventId 1011
|
|
} else {
|
|
PulseInfo "Registration check skipped (no API token available)."
|
|
Write-InstallerEvent -SourceName $serviceName -Message "Installer skipped registration lookup (no API token provided)." -EventId 1012
|
|
}
|
|
|
|
$recentLogs = Get-RecentAgentEvents -ProviderName $serviceName -Max 5
|
|
if ($recentLogs) {
|
|
PulseInfo "Recent service events:"
|
|
$recentLogs | Select-Object -First 3 | ForEach-Object {
|
|
$time = $_.TimeCreated
|
|
if (-not $time) { $time = $_.TimeGenerated }
|
|
Write-Host (" [{0}] {1}" -f $time.ToString("u"), $_.Message)
|
|
}
|
|
} else {
|
|
PulseWarn "No recent Application log entries were found for $serviceName."
|
|
}
|
|
} else {
|
|
PulseWarn "Service status: $status"
|
|
PulseInfo "Checking service logs..."
|
|
$recentLogs = Get-RecentAgentEvents -ProviderName $serviceName -Max 5
|
|
if ($recentLogs) {
|
|
$recentLogs | ForEach-Object {
|
|
$time = $_.TimeCreated
|
|
if (-not $time) { $time = $_.TimeGenerated }
|
|
Write-Host (" [{0}] {1}" -f $time.ToString("u"), $_.Message)
|
|
}
|
|
} else {
|
|
PulseWarn "No Application log entries were found for $serviceName."
|
|
}
|
|
}
|
|
|
|
} catch {
|
|
PulseError "Failed to create/start service: $_"
|
|
PulseInfo "You can start the agent manually with:"
|
|
Write-Host " $manualCommand"
|
|
Write-Host ""
|
|
PulseInfo "Or check Windows Event Viewer (Application log) for error details."
|
|
exit 1
|
|
}
|
|
} else {
|
|
PulseInfo "Skipping service installation (--NoService flag)"
|
|
Write-Host ""
|
|
PulseInfo "To start the agent manually:"
|
|
Write-Host " $manualCommand"
|
|
}
|
|
|
|
Write-Host ""
|
|
$successBanner = "=" * 59
|
|
Write-Host $successBanner -ForegroundColor Green
|
|
PulseSuccess "Installation complete!"
|
|
Write-Host $successBanner -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
PulseInfo "Service Management Commands:"
|
|
Write-Host " Start: Start-Service -Name PulseHostAgent"
|
|
Write-Host " Stop: Stop-Service -Name PulseHostAgent"
|
|
Write-Host " Restart: Restart-Service -Name PulseHostAgent"
|
|
Write-Host " Status: Get-Service -Name PulseHostAgent | Select Status, StartType"
|
|
Write-Host " Remove: sc.exe delete PulseHostAgent"
|
|
Write-Host " Logs: Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='PulseHostAgent'} -MaxEvents 50"
|
|
Write-Host ""
|
|
|
|
PulseInfo "Files installed:"
|
|
Write-Host " Binary: $agentPath"
|
|
Write-Host " Config: $configPath"
|
|
Write-Host ""
|
|
|
|
PulseInfo "The agent is now reporting to: $PulseUrl"
|
|
Write-Host ""
|