mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 00:17:39 +01:00
582 lines
22 KiB
PowerShell
582 lines
22 KiB
PowerShell
# Pulse Host Agent Installation Script for Windows
|
|
#
|
|
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
|
# │ DEPRECATED: This script installs the legacy pulse-host-agent. │
|
|
# │ Please use the unified agent instead: │
|
|
# │ │
|
|
# │ $env:PULSE_URL="http://pulse-server:7655" │
|
|
# │ $env:PULSE_TOKEN="your-token" │
|
|
# │ irm http://pulse-server:7655/install.ps1 | iex │
|
|
# │ │
|
|
# │ The unified agent provides: │
|
|
# │ - Combined host + Docker monitoring in one binary │
|
|
# │ - Automatic updates │
|
|
# │ - Simplified management │
|
|
# └─────────────────────────────────────────────────────────────────────────────┘
|
|
#
|
|
# Usage:
|
|
# iwr -useb http://pulse-server:7655/install-host-agent.ps1 | iex
|
|
# OR with parameters:
|
|
# $url = "http://pulse-server:7655"; $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:7655)"
|
|
}
|
|
$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 ""
|