Add Install-ZBXAgentManual.ps1 script for Zabbix Agent 2 installation with firewall configuration

This commit is contained in:
Petr Štěpán
2026-07-21 19:18:55 +02:00
parent e87612370d
commit 656a5945ce
+274
View File
@@ -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
}