Aktionen

Zabbix PowerShell JSON und LLD per system.run

Aus znilwiki

Changelog:

  • 12.10.2023 erste Version

Vorwort

Ich wollte bei einem Kunden eine Überwachung eines Failoverclusters einrichten - und wollte weder extra dafür ein Skript schreiben noch den Agenten per UserParameter um Funktionen erweitern.
Ich musste etwas probieren, aber dann war es doch ganz leicht (wenn man es erst einmal verstanden hat).


Konfiguration des ZabbixAgenten

Ich setze hier den ZabbixAgent2 in der Version 6.0 ein, der normale Agent (also nicht 2) nutzt die gleichen Einstzellungen.
In der Konfigurationsdatei für den Agenten müssen/sollten folgende Optionen gesetzt sein:

DenyKey=system.run[rmdir]  
AllowKey=system.run[*]

Das erlaubt jeden Befehl per system.run außer dem Befehl rmdir.
Alternativ könnte ihr auch nur PowerShell erlauben: AllowKey=system.run[powershell]

Nach der Änderung müsstet Ihr den Dienst des Agenten einmal neu starten.


Konfiguration des RAW-Items

Hier die einzelnen Schritte bis zum fertigen RAW-Item.
Als RAW-Item bezeichne ich das Item welches später das JSON holt. Dieses JSON nutzen wird dann sowohl für das Discovery / LLD als auch um die anderen Werte in Items zu schreiben. Etwas was ich an Zabbix wirklich genial finde, wir stellen nur eine Frage an das Zielsystem und füllen duzende Items mit der Antwort.


PowerShell-Befehl austüfteln

Habt Geduld - ich erkläre den Aufbau des PowerShell Befehls nicht ohne Grund so genau!
Zunächst habe ich auf dem Zielsystem natürlich den PowerShell-Befehl ausgeklingelt. In diesem Fall will ich die vorhandenen Clusterknoten ermitteln und den Status dann per Items abfragen.
Gestartet habe ich zunächst mit dem Befehl

Get-ClusterNode -Cluster FILECLUSTER

Ausgabe:

Name          State Type
----          ----- ----
FILESERVER-A  Up    Node
FILESERVER-B  Up    Node

Das ist noch etwas Dünn, wir wollen alle Informationen, deshlab

Get-ClusterNode -Cluster FILECLUSTER| Select-Object *

Ausgabe:

BuildNumber           : 20348
Cluster               : FILECLUSTER
CSDVersion            :
Description           :
DrainStatus           : NotInitiated
DrainTarget           : 4294967295
DrainErrorCode        : 0
DynamicWeight         : 0
Id                    : 1
MajorVersion          : 10
MinorVersion          : 0
Name                  : FILESERVER-A
NeedsPreventQuorum    : 0
NodeHighestVersion    : 720900
NodeInstanceID        : 00000000-0000-0000-0000-000000000001
NodeLowestVersion     : 720900
NodeName              : FILESERVER-A
NodeWeight            : 1
FaultDomain           : {Site:Site 192.168.0.0/24, Rack:, Chassis:}
Model                 : VMware7,1
Manufacturer          : VMware, Inc.
SerialNumber          : VMware-42 19 37 83 8b 83 e6 18-ad b3 73 bf da eb cb e1
State                 : Up
StatusInformation     : Normal
Type                  : Node
UniqueID              : ad2d4545-37b6-4c2c-b78f-9ecf72cd459c
DetectedCloudPlatform : None

BuildNumber           : 20348
Cluster               : FILECLUSTER
CSDVersion            :
Description           :
DrainStatus           : NotInitiated
DrainTarget           : 4294967295
DrainErrorCode        : 0
DynamicWeight         : 1
Id                    : 2
MajorVersion          : 10
MinorVersion          : 0
Name                  : FILESERVER-B
NeedsPreventQuorum    : 0
NodeHighestVersion    : 720900
NodeInstanceID        : 00000000-0000-0000-0000-000000000002
NodeLowestVersion     : 720900
NodeName              : FILESERVER-B
NodeWeight            : 1
FaultDomain           : {Site:Site 192.168.0.0/24, Rack:, Chassis:}
Model                 : VMware7,1
Manufacturer          : VMware, Inc.
SerialNumber          : VMware-42 19 2f 2a ed fa 97 ca-39 76 ff 79 0f fc 0e 78
State                 : Up
StatusInformation     : Normal
Type                  : Node
UniqueID              : 5f4e5ff5-1452-47fb-b9f8-b919d7815a39
DetectedCloudPlatform : None

Super, eine Frage, alle Antworten, z.B. der State
Damit wir das in Zabbix besser weiter verarbeiten können brauchen wir ein JSON, also Umwandeln:

Get-ClusterNode -Cluster FILECLUSTER| Select-Object * | ConvertTo-Json

Die Ausgabe ist sehr umfangreich, auch die Unterobjekte wie Cluster und FaultDomain werden noch mal extra aufgeschlüsselt:

       "NodeWeight":  1,
       "FaultDomain":  [
                           "Site:Site 192.168.0.0/24",
                           "Rack:",
                           "Chassis:"
                       ],
       "Model":  "VMware7,1",
       "Manufacturer":  "VMware, Inc.",

Das brauche ich in diesem Fall gar nicht. Die Tiefe beim Aufschlüssel lässt sich begrenzen, also

Get-ClusterNode -Cluster FILECLUSTER | Select-Object * | ConvertTo-Json -Depth 1

und schon ist die Ausgabe wieder kürzer:

[
    {
        "BuildNumber":  20348,
        "Cluster":  "FILECLUSTER",
        "CSDVersion":  "",
        "Description":  "",
        "DrainStatus":  0,
        "DrainTarget":  4294967295,
        "DrainErrorCode":  0,
        "DynamicWeight":  0,
        "Id":  "1",
        "MajorVersion":  10,
        "MinorVersion":  0,
        "Name":  "FILESERVER-A",
        "NeedsPreventQuorum":  0,
        "NodeHighestVersion":  720900,
        "NodeInstanceID":  "00000000-0000-0000-0000-000000000001",
        "NodeLowestVersion":  720900,
        "NodeName":  "FILESERVER-A",
        "NodeWeight":  1,
        "FaultDomain":  "Site:Site 192.168.0.0/24 Rack: Chassis:",
        "Model":  "VMware7,1",
        "Manufacturer":  "VMware, Inc.",
        "SerialNumber":  "VMware-42 19 37 83 8b 83 e6 18-ad b3 73 bf da eb cb e1",
        "State":  0,
        "StatusInformation":  0,
        "Type":  0,
        "UniqueID":  "ad2d4545-37b6-4c2c-b78f-9ecf72cd459c",
        "DetectedCloudPlatform":  0
    },
    {
        "BuildNumber":  20348,
        "Cluster":  "FILECLUSTER",
        "CSDVersion":  "",
        "Description":  "",
        "DrainStatus":  0,
        "DrainTarget":  4294967295,
        "DrainErrorCode":  0,
        "DynamicWeight":  1,
        "Id":  "2",
        "MajorVersion":  10,
        "MinorVersion":  0,
        "Name":  "FILESERVER-B",
        "NeedsPreventQuorum":  0,
        "NodeHighestVersion":  720900,
        "NodeInstanceID":  "00000000-0000-0000-0000-000000000002",
        "NodeLowestVersion":  720900,
        "NodeName":  "FILESERVER-B",
        "NodeWeight":  1,
        "FaultDomain":  "Site:Site 192.168.0.0/24 Rack: Chassis:",
        "Model":  "VMware7,1",
        "Manufacturer":  "VMware, Inc.",
        "SerialNumber":  "VMware-42 19 2f 2a ed fa 97 ca-39 76 ff 79 0f fc 0e 78",
        "State":  0,
        "StatusInformation":  0,
        "Type":  0,
        "UniqueID":  "5f4e5ff5-1452-47fb-b9f8-b919d7815a39",
        "DetectedCloudPlatform":  0
    }
]

Das könnte man nun schon so in Zabbix übernehmen. Aber mich störte noch eines:
Aus

State                 : Up

wird im JSON ein

"State":  0,

0 heißt in diesem Fall wohl Up - aber ich weis gar nicht welche anderen Werte es gäbe. Und hätte deshalb an dieser Steller lieber den Text.
Dieses Umwandeln macht er aber nur wenn man nach JSON wandelt, würde man nach CSV wandeln passiert das nicht. Das machen wir uns zu nutze:

Get-ClusterNode -Cluster FILECLUSTER| Select-Object * | ConvertTo-Csv | ConvertFrom-Csv | ConvertTo-Json -Depth 1

Wir wandeln das Ergebnis also zurerst ins CSV-Format, dann wieder zurück und zuletzt doch ins JSON Format.
Und schon haben wir den Text statt der Zahlen:

[
    {
        "BuildNumber":  "20348",
        "Cluster":  "FILECLUSTER",
        "CSDVersion":  "",
        "Description":  "",
        "DrainStatus":  "NotInitiated",
        "DrainTarget":  "4294967295",
        "DrainErrorCode":  "0",
        "DynamicWeight":  "0",
        "Id":  "1",
        "MajorVersion":  "10",
        "MinorVersion":  "0",
        "Name":  "FILESERVER-A",
        "NeedsPreventQuorum":  "0",
        "NodeHighestVersion":  "720900",
        "NodeInstanceID":  "00000000-0000-0000-0000-000000000001",
        "NodeLowestVersion":  "720900",
        "NodeName":  "FILESERVER-A",
        "NodeWeight":  "1",
        "FaultDomain":  "System.String[]",
        "Model":  "VMware7,1",
        "Manufacturer":  "VMware, Inc.",
        "SerialNumber":  "VMware-42 19 37 83 8b 83 e6 18-ad b3 73 bf da eb cb e1",
        "State":  "Up",
        "StatusInformation":  "Normal",
        "Type":  "Node",
        "UniqueID":  "ad2d4545-37b6-4c2c-b78f-9ecf72cd459c",
        "DetectedCloudPlatform":  "None"
    },
    {
        "BuildNumber":  "20348",
        "Cluster":  "FILECLUSTER",
        "CSDVersion":  "",
        "Description":  "",
        "DrainStatus":  "NotInitiated",
        "DrainTarget":  "4294967295",
        "DrainErrorCode":  "0",
        "DynamicWeight":  "1",
        "Id":  "2",
        "MajorVersion":  "10",
        "MinorVersion":  "0",
        "Name":  "FILESERVER-B",
        "NeedsPreventQuorum":  "0",
        "NodeHighestVersion":  "720900",
        "NodeInstanceID":  "00000000-0000-0000-0000-000000000002",
        "NodeLowestVersion":  "720900",
        "NodeName":  "FILESERVER-B",
        "NodeWeight":  "1",
        "FaultDomain":  "System.String[]",
        "Model":  "VMware7,1",
        "Manufacturer":  "VMware, Inc.",
        "SerialNumber":  "VMware-42 19 2f 2a ed fa 97 ca-39 76 ff 79 0f fc 0e 78",
        "State":  "Up",
        "StatusInformation":  "Normal",
        "Type":  "Node",
        "UniqueID":  "5f4e5ff5-1452-47fb-b9f8-b919d7815a39",
        "DetectedCloudPlatform":  "None"
    }
]

Damit wäre der Befehl an dieser Stelle schon fertig. Diesen Befehl können wir problemlos in unser Item einfügen.


PowerShell-Befehl hat Kommas oder andere Sonderzeichen

Important.png
Hinweis:Das nachfolgende könnt Ihr auch Überspringen, aber vielleicht könnt Ihr das ja mal brauchen


Man könnte nun auch das Select-Object anpassen das nur die nötigsten Informationen gesammelt werden:

Get-ClusterNode -Cluster FILECLUSTER | Select-Object NodeName,State | ConvertTo-Csv | ConvertFrom-Csv | ConvertTo-Json -Depth 1

Sammelt nur die Daten von NodeName,State, die Ausgabe wäre:

[
    {
        "NodeName":  "FILESERVER-A",
        "State":  "Up"
    },
    {
        "NodeName":  "FILESERVER-B",
        "State":  "Up"
    }
]

Aber es gibt dann doch ein Problem. Wir wollen den PowerShell-Befehl ja in unser Item unter system.run[ ... ] einfügen. Und dort wird das Komma für die Unterscheidung der Parameter in Zabbix genutzt. Das habe ich eine Weile probiert - das escapen geht an dieser Stelle nicht und auch mit " ... " hatte ich Probleme. Dies Lösung in diesem Fall wäre folgende:

$myContent = "Get-ClusterNode -Cluster FILECLUSTER | Select-Object NodeName,State | ConvertTo-Csv | ConvertFrom-Csv | ConvertTo-Json -Depth 1"
$base64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($myContent))
write-host $base64

Da kommt dann "Kauderwelsch":

RwBlAHQALQBDAGwAdQBzAHQAZQByAE4AbwBkAGUAIAAtAEMAbAB1AHMAdABlAHIAIABCAEUAUgBMAEkATgBDAEwAVQBTAFQARQBSACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIABOAG8AZABlAE4AYQBtAGUALABTAHQAYQB0AGUAIAB8ACAAQwBvAG4AdgBlAHIAdABUAG8ALQBDAHMAdgAgAHwAIABDAG8AbgB2AGUAcgB0AEYAcgBvAG0ALQBDAHMAdgAgAHwAIABDAG8AbgB2AGUAcgB0AFQAbwAtAEoAcwBvAG4AIAAtAEQAZQBwAHQAaAAgADEA

Der aber als gültiger PowerShell-Befehl genutzt werden kann (z.B. in einer Eingabeaufforderung):

powershell.exe -noprofile -nologo -encodedCommand "RwBlAHQALQBDAGwAdQBzAHQAZQByAE4AbwBkAGUAIAAtAEMAbAB1AHMAdABlAHIAIABCAEUAUgBMAEkATgBDAEwAVQBTAFQARQBSACAAfAAgAFMAZQBsAGUAYwB0AC0ATwBiAGoAZQBjAHQAIABOAG8AZABlAE4AYQBtAGUALABTAHQAYQB0AGUAIAB8ACAAQwBvAG4AdgBlAHIAdABUAG8ALQBDAHMAdgAgAHwAIABDAG8AbgB2AGUAcgB0AEYAcgBvAG0ALQBDAHMAdgAgAHwAIABDAG8AbgB2AGUAcgB0AFQAbwAtAEoAcwBvAG4AIAAtAEQAZQBwAHQAaAAgADEA"

Ergibt die gleiche Ausgabe wie zuvor. Gefunden habe ich die Idee dazu hier: https://serverthings.com/zabbix-running-powershell-scripts-on-hosts-with-system-run/


Das eigentlich Item

ImageRAWITEM202310121003.png Der Typ ist Zabbix agent oder Zabbix agent (actice), zum Testen ist der Agent ohne active einfacher weil wir den Wert dann vom Server aus anfordern können.
Der Key ist:
system.run[powershell.exe -noprofile -nologo "Get-ClusterNode -Cluster {$CLUSTERNAME}| Select-Object * | ConvertTo-Csv | ConvertFrom-Csv | ConvertTo-Json -Compress"]