Files
WindowsServer/ConfigWinRMForZabbix/Download-WinRMConfigScript.ps1
T

215 lines
8.4 KiB
PowerShell

<#
.SYNOPSIS
Downloads ConfigWinRM.ps1 and ConfigWinRM.xml to C:\WinRM and optionally sets the
Zabbix Proxy IP address as the WinRM firewall trusted host.
.DESCRIPTION
Downloads ConfigWinRM.ps1 and ConfigWinRM.xml from the internal Git server
(git.totalservice.cz, WindowsServer repo) into C:\WinRM, creating that folder if it
does not already exist. After the download, it asks for the Zabbix Proxy IP
address: if one is entered, it is written into the <FWWinRMTrustedHosts> element of
the downloaded ConfigWinRM.xml; if the prompt is left empty (Enter only),
ConfigWinRM.xml is left unchanged. The IP address can also be supplied directly via
the -ZabbixProxyIPAddress parameter to run the script non-interactively.
.PARAMETER ZabbixProxyIPAddress
Zabbix Proxy IP address to allow through the WinRM firewall rule
(FWWinRMTrustedHosts in ConfigWinRM.xml). Omit this parameter to be prompted
interactively (or to fall back to $env:ZabbixProxyIPAddress if that is set); pass
an empty string to skip the prompt and leave the existing value in ConfigWinRM.xml
unchanged.
.EXAMPLE
PS> .\Download-WinRMConfigScript.ps1
Downloads both files to C:\WinRM and interactively prompts for the Zabbix Proxy IP
address.
.EXAMPLE
PS> .\Download-WinRMConfigScript.ps1 -ZabbixProxyIPAddress '192.168.10.50'
Downloads both files and sets FWWinRMTrustedHosts to 192.168.10.50 without prompting.
.EXAMPLE
PS> irm https://s.tslab.cz/ZBXWinRM | iex
Shortest way to run the script directly from the repo without saving it locally
first; prompts interactively for the Zabbix Proxy IP address.
.NOTES
This file is intentionally plain ASCII, no BOM. A UTF-8 BOM broke the primary
"irm <url> | iex" use case: the BOM bytes were not decoded correctly across the
download pipeline, which made the parser fail to recognize the opening "<#" of
this comment block and try to run the whole header as code.
Author: Petr Stepan
Created: 2026-07-21
Version: 1.2.3
Changelog:
1.0.0 - Initial version
1.1.0 - Added $env:ZabbixProxyIPAddress fallback so a plain "irm | iex"
one-liner can supply the IP address without "& { ... } -Param" wrapping
1.2.0 - Only call exit when running as a real .ps1 file, not when run via
"irm | iex" - exit would otherwise close the caller's PowerShell window
1.2.1 - Fixed "'Path' cannot be found" under Set-StrictMode when run via
iex - use $PSCommandPath instead of $MyInvocation.MyCommand.Path
1.2.2 - Fixed the same class of error in Update-WinRMTrustedHostConfig: use
SelectSingleNode instead of dot-property access so a missing
<FWWinRMTrustedHosts> element is detected cleanly instead of throwing
a raw StrictMode error
1.2.3 - Removed UTF-8 BOM and non-ASCII characters (broke "irm | iex" -
see note above)
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false, HelpMessage = 'Zabbix Proxy IP address to allow through the WinRM firewall rule. Leave empty to keep the existing value.')]
[string]
$ZabbixProxyIPAddress
)
# --- 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
$SourceBaseUri = 'https://git.totalservice.cz/PSScripts/WindowsServer/raw/branch/main/ConfigWinRMForZabbix'
$DestinationPath = 'C:\WinRM'
$ScriptFile = Join-Path -Path $DestinationPath -ChildPath 'ConfigWinRM.ps1'
$ConfigFile = Join-Path -Path $DestinationPath -ChildPath 'ConfigWinRM.xml'
# --- Functions ---------------------------------------------------------
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 Update-WinRMTrustedHostConfig {
<#
.SYNOPSIS
Sets the <FWWinRMTrustedHosts> element of a ConfigWinRM.xml file.
.PARAMETER Path
Path to the ConfigWinRM.xml file to update.
.PARAMETER TrustedHost
Value to write into <FWWinRMTrustedHosts>.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path -Path $_ -PathType Leaf })]
[string]
$Path,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$TrustedHost
)
try {
# PreserveWhitespace keeps the original file's indentation/comments intact -
# without it, XmlDocument.Save() would reformat the whole file.
$xml = New-Object -TypeName System.Xml.XmlDocument
$xml.PreserveWhitespace = $true
$xml.Load($Path)
# SelectSingleNode (XPath), not dot-property access - a missing element would
# throw "property cannot be found" under Set-StrictMode -Version Latest instead
# of returning $null, which would skip the not-found check below entirely.
$node = $xml.SelectSingleNode('/ConfigWinRM/Options/FWWinRMTrustedHosts')
if ($null -eq $node) {
throw "Element <FWWinRMTrustedHosts> was not found in '$Path'."
}
$node.InnerText = $TrustedHost
$xml.Save($Path)
Write-Host "FWWinRMTrustedHosts set to '$TrustedHost' in '$Path'."
}
catch {
Write-Error "Failed to update '$Path': $_"
throw
}
}
# --- Main --------------------------------------------------------------
try {
if (-not (Test-Path -Path $DestinationPath)) {
New-Item -Path $DestinationPath -ItemType Directory -Force | Out-Null
Write-Host "Created directory '$DestinationPath'."
}
Invoke-RemoteFileDownload -Uri "$SourceBaseUri/ConfigWinRM.ps1" -OutFile $ScriptFile
Invoke-RemoteFileDownload -Uri "$SourceBaseUri/ConfigWinRM.xml" -OutFile $ConfigFile
# Only fall back to the environment variable / prompt when the caller did not pass
# the parameter at all - an explicit empty string ("") means "skip the prompt,
# leave ConfigWinRM.xml unchanged". $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 IP address (press Enter to keep the current value)'
}
}
if ([string]::IsNullOrWhiteSpace($ZabbixProxyIPAddress)) {
Write-Host 'No Zabbix Proxy IP address provided - ConfigWinRM.xml left unchanged.'
}
else {
$parsedIp = $null
if (-not [System.Net.IPAddress]::TryParse($ZabbixProxyIPAddress, [ref] $parsedIp)) {
throw "'$ZabbixProxyIPAddress' is not a valid IP address."
}
Update-WinRMTrustedHostConfig -Path $ConfigFile -TrustedHost $ZabbixProxyIPAddress
}
}
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
}