Add Install-ZBXAgentManual.ps1 script for Zabbix Agent 2 installation with firewall configuration
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Downloads and silently installs Zabbix Agent 2 for Windows, configured for a
|
||||
Zabbix Proxy/Server, and opens the firewall for passive checks.
|
||||
|
||||
.DESCRIPTION
|
||||
Downloads the Zabbix Agent 2 Windows MSI from cdn.zabbix.com and installs it silently via msiexec.
|
||||
Hostname is set to the local computer name; both Server and ServerActive are set to the Zabbix Proxy/Server IP
|
||||
address supplied by the caller (interactively, via -ZabbixProxyIPAddress, or via
|
||||
$env:ZabbixProxyIPAddress). After a successful install it creates a Windows
|
||||
Firewall rule allowing inbound TCP 10050 (Zabbix Agent passive checks) from that
|
||||
same IP address only. Must be run elevated (Administrator).
|
||||
|
||||
.PARAMETER ZabbixProxyIPAddress
|
||||
IP address of the Zabbix Proxy or Zabbix Server. Used for both the agent's Server
|
||||
and ServerActive settings, and as the only allowed remote address for the TCP
|
||||
10050 firewall rule. Omit this parameter to be prompted interactively (or to fall
|
||||
back to $env:ZabbixProxyIPAddress if that is set).
|
||||
|
||||
.PARAMETER ZabbixAgentMsiUri
|
||||
URI of the Zabbix Agent 2 Windows MSI to download and install. Defaults to version
|
||||
7.2.13. Override this to install a different version/build, e.g. one of the other
|
||||
files listed under https://cdn.zabbix.com/zabbix/binaries/stable/<major.minor>/<version>/.
|
||||
|
||||
.EXAMPLE
|
||||
PS> .\Install-ZBXAgentManual.ps1
|
||||
Downloads and installs Zabbix Agent 2, prompting for the Zabbix Proxy/Server IP
|
||||
address.
|
||||
|
||||
.EXAMPLE
|
||||
PS> .\Install-ZBXAgentManual.ps1 -ZabbixProxyIPAddress '192.168.10.50'
|
||||
Downloads and installs Zabbix Agent 2 non-interactively.
|
||||
|
||||
.EXAMPLE
|
||||
PS> .\Install-ZBXAgentManual.ps1 -ZabbixProxyIPAddress '192.168.10.50' -ZabbixAgentMsiUri 'https://cdn.zabbix.com/zabbix/binaries/stable/7.0/7.0.14/zabbix_agent2-7.0.14-windows-amd64-openssl.msi'
|
||||
Installs a different Zabbix Agent 2 version instead of the default 7.2.13.
|
||||
|
||||
.EXAMPLE
|
||||
PS> irm https://s.tslab.cz/ZBXAgent | iex
|
||||
Shortest way to run this directly from the repo without saving it locally first;
|
||||
prompts interactively for the Zabbix Proxy/Server IP address and installs the
|
||||
default MSI version.
|
||||
|
||||
.NOTES
|
||||
Author: Petr Stepan
|
||||
Created: 2026-07-21
|
||||
Version: 1.1.0
|
||||
Changelog:
|
||||
1.0.0 - Initial version
|
||||
1.1.0 - Made ZabbixAgentMsiUri a parameter (was hardcoded) so a different
|
||||
agent version can be installed without editing the script
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $false, HelpMessage = 'IP address of the Zabbix Proxy or Server. Used for Server, ServerActive and the TCP 10050 firewall rule.')]
|
||||
[string]
|
||||
$ZabbixProxyIPAddress,
|
||||
|
||||
[Parameter(Mandatory = $false, HelpMessage = 'URI of the Zabbix Agent 2 Windows MSI to download and install.')]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$ZabbixAgentMsiUri = 'https://cdn.zabbix.com/zabbix/binaries/stable/7.2/7.2.13/zabbix_agent2-7.2.13-windows-amd64-openssl.msi'
|
||||
)
|
||||
|
||||
# --- Safety preamble -------------------------------------------------------
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Older Windows Server releases don't enable TLS 1.2 by default, which would make the
|
||||
# download below fail silently against a TLS-1.2-only server.
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
$MsiPath = Join-Path -Path $env:TEMP -ChildPath (Split-Path -Path $ZabbixAgentMsiUri -Leaf)
|
||||
$MsiLogPath = Join-Path -Path $env:TEMP -ChildPath 'zabbix_agent2_install.log'
|
||||
$FirewallRuleName = 'Zabbix Agent (TCP-In)'
|
||||
$FirewallPort = 10050
|
||||
|
||||
# --- Functions ---------------------------------------------------------
|
||||
|
||||
function Test-CurrentUserIsAdministrator {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Returns whether the current process is running elevated (Administrator).
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object -TypeName System.Security.Principal.WindowsPrincipal -ArgumentList $identity
|
||||
return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
function Invoke-RemoteFileDownload {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Downloads a single file from a URI to a local path.
|
||||
.PARAMETER Uri
|
||||
Source URI to download from.
|
||||
.PARAMETER OutFile
|
||||
Local destination file path.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$Uri,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$OutFile
|
||||
)
|
||||
|
||||
try {
|
||||
Invoke-WebRequest -Uri $Uri -OutFile $OutFile -UseBasicParsing
|
||||
Write-Host "Downloaded '$Uri' to '$OutFile'."
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to download '$Uri' to '$OutFile': $_"
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
function Install-ZabbixAgentMsi {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Silently installs the Zabbix Agent 2 MSI with the given Hostname/Server settings.
|
||||
.PARAMETER MsiPath
|
||||
Path to the downloaded zabbix_agent2 MSI file.
|
||||
.PARAMETER HostName
|
||||
Value for the agent's Hostname setting.
|
||||
.PARAMETER ServerAddress
|
||||
Value used for both the Server and ServerActive settings.
|
||||
.PARAMETER LogPath
|
||||
Path for the msiexec verbose install log.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
|
||||
[string]
|
||||
$MsiPath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$HostName,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$ServerAddress,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$LogPath
|
||||
)
|
||||
|
||||
# Passed as an array, not one concatenated string, so Start-Process quotes each
|
||||
# argument correctly even if $MsiPath/$LogPath contain spaces.
|
||||
$msiArgs = @(
|
||||
'/i', $MsiPath
|
||||
'/qn'
|
||||
"HOSTNAME=$HostName"
|
||||
"SERVER=$ServerAddress"
|
||||
"SERVERACTIVE=$ServerAddress"
|
||||
'/l*v', $LogPath
|
||||
)
|
||||
|
||||
Write-Host "Installing Zabbix Agent 2 (Hostname=$HostName, Server/ServerActive=$ServerAddress)..."
|
||||
$process = Start-Process -FilePath 'msiexec.exe' -ArgumentList $msiArgs -Wait -PassThru -NoNewWindow
|
||||
|
||||
# 3010 = success, reboot required - still a successful install.
|
||||
if ($process.ExitCode -notin @(0, 3010)) {
|
||||
throw "msiexec failed with exit code $($process.ExitCode). See log: $LogPath"
|
||||
}
|
||||
|
||||
Write-Host "Zabbix Agent 2 installed successfully (msiexec exit code $($process.ExitCode))."
|
||||
}
|
||||
|
||||
function New-ZabbixAgentFirewallRule {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an inbound firewall rule for the Zabbix Agent listener, restricted to
|
||||
a single remote address. Does nothing if a rule with the same name already
|
||||
exists.
|
||||
.PARAMETER Name
|
||||
Display name of the firewall rule.
|
||||
.PARAMETER Port
|
||||
TCP port to allow.
|
||||
.PARAMETER RemoteAddress
|
||||
Only remote address allowed to reach that port.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$Name,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[int]
|
||||
$Port,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]
|
||||
$RemoteAddress
|
||||
)
|
||||
|
||||
if (Get-NetFirewallRule -DisplayName $Name -ErrorAction SilentlyContinue) {
|
||||
Write-Host "Firewall rule '$Name' already exists - skipping."
|
||||
return
|
||||
}
|
||||
|
||||
New-NetFirewallRule -DisplayName $Name -Direction Inbound -Action Allow -Protocol TCP `
|
||||
-LocalPort $Port -RemoteAddress $RemoteAddress -Group 'Zabbix Agent' `
|
||||
-Description "Allow the Zabbix Proxy/Server ($RemoteAddress) to reach the Zabbix Agent passive check listener." | Out-Null
|
||||
|
||||
Write-Host "Firewall rule '$Name' created (TCP $Port, remote address $RemoteAddress)."
|
||||
}
|
||||
|
||||
# --- Main --------------------------------------------------------------
|
||||
try {
|
||||
if (-not (Test-CurrentUserIsAdministrator)) {
|
||||
throw 'This script must be run elevated (as Administrator).'
|
||||
}
|
||||
|
||||
# Only fall back to the environment variable / prompt when the caller did not pass
|
||||
# the parameter at all. $env:ZabbixProxyIPAddress lets a plain "irm <url> | iex"
|
||||
# one-liner supply a value without needing "& { ... } -Param" - piping into iex
|
||||
# runs the downloaded param() block as a plain statement with no bound arguments,
|
||||
# so -ZabbixProxyIPAddress itself can only be set that way when the script is
|
||||
# invoked as a real command (locally or via "& { ... }").
|
||||
if (-not $PSBoundParameters.ContainsKey('ZabbixProxyIPAddress')) {
|
||||
if ($env:ZabbixProxyIPAddress) {
|
||||
$ZabbixProxyIPAddress = $env:ZabbixProxyIPAddress
|
||||
}
|
||||
else {
|
||||
$ZabbixProxyIPAddress = Read-Host -Prompt 'Enter Zabbix Proxy/Server IP address'
|
||||
}
|
||||
}
|
||||
|
||||
$parsedIp = $null
|
||||
if (-not [System.Net.IPAddress]::TryParse($ZabbixProxyIPAddress, [ref] $parsedIp)) {
|
||||
throw "'$ZabbixProxyIPAddress' is not a valid IP address."
|
||||
}
|
||||
|
||||
Invoke-RemoteFileDownload -Uri $ZabbixAgentMsiUri -OutFile $MsiPath
|
||||
|
||||
Install-ZabbixAgentMsi -MsiPath $MsiPath -HostName $env:COMPUTERNAME -ServerAddress $ZabbixProxyIPAddress -LogPath $MsiLogPath
|
||||
|
||||
New-ZabbixAgentFirewallRule -Name $FirewallRuleName -Port $FirewallPort -RemoteAddress $ZabbixProxyIPAddress
|
||||
|
||||
Remove-Item -Path $MsiPath -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
catch {
|
||||
Write-Error "Script failed: $_"
|
||||
# $PSCommandPath is only set when this runs as an actual .ps1 file. When run via
|
||||
# "irm <url> | iex" there is no backing file - the code executes in the caller's
|
||||
# own session, so calling exit here would close their whole PowerShell window
|
||||
# instead of just ending this script.
|
||||
if ($PSCommandPath) {
|
||||
exit 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ($PSCommandPath) {
|
||||
exit 0
|
||||
}
|
||||
Reference in New Issue
Block a user