Aktionen

Zabbix:Template Windows Service Auto-Discover - Windows Dienste automatisch entdecken

Aus znilwiki

Important.png
Hinweis: Ab Zabbix 3.x ist diese Funktion nativ in Zabbix eingebaut. Siehe Zabbix:Template Windows Service Auto-Discover Zabbix 3.x - Windows Dienste Service Discovery mit Bordmitteln


Aktuelle Version der servdisc.exe ist 4.0.1.1
Download: Zabbix-Service-Discovery.zip

History:

  • 10.11.2013 V1: first Version
  • 13.11.2013 V1.1: add a Routine for german Special Chars (replace 'ä' by 'ae' and so on)
  • 02.12.2013 V2: add a file 'servdisc-ignore.txt' - add names of services that are irritating, they will not be add
  • 17.03.2014 V3: you can use the long or short Service-Name in the servdisc.ignore.txt (It's a "Text in Text" Test)
  • 17.03.2014 V3.0.0.1: servdisc.exe ignore all "Trigger Start" Services
  • 25.03.2014 V3.0.0.2: Fixed Error with servdisc-ignore.txt
  • 08.04.2015 V4: You can set Trigger for the services to Information and Critical if you like
  • 08.04.2015 V4: You can include Services (Running Status will be ignored)
  • 11.05.2015 V4.0.1.1: Filter Special Chars from Display Name (for Service-Names with Paths)



Vorwort

Auf der Suche nach einer Idee wie man die laufenden Windows-Dienste in Zabbix automatisch erfassen kann fand ich das hier im Internet:
https://github.com/q1x/zabbix-templates/tree/master/service-discovery
Ein PowerShell Script mit passenden Template welches Automatisch alle Windows-Dienste findet die auf automatischen Start stehen - perfekt!
Ok, das PowerShell Skript hätte eine Anpassung der Sicherheitseinstellungen benötigt - aber auf der Seite gibt es auch ein VBS-Skript das genau das gleiche kann.

Leider kam nach einen ersten Test die Ernüchterung - das klappt nicht so wie geplant, es gab dauernd eine Fehlermeldung "No JSON Object" etc..

Nach einigen Experimenten kam ich darauf das die Rückmeldung schlicht zu groß war. Auf meinem Testserver kommt eine Rückmeldung von ca. 28.000 Zeichen, in irgendwelchen Foren fand ich den Hinweis das Zabbix bei bestimmten Datenbanken nur maximal 4.000 Zeichen auf einmal in einer JSON-Rückmeldung unterstützt.

Nach einem kurzen Test im welchen ich die Zahl der zurückgegeben Dienste beschränkte hatte ich Erfolg!

Erfasst werden wie beim Original nur Dienste die auf 'Automatisch' oder auf 'Automatisch (Verzögerter Start)' stehen.
Im Zip-Archiv findet Ihr neben der servdisc.exe das Template in Englisch.
Das Template findet Ihr nach dem Import in der Hostgruppe 'Templates' mit den Namen

znil Template Windows Dienste Service Discovery ACTIVE AGENT

Also nur eine Active Agent Version.

Achtung, er sucht nur alle 60 Minuten / einmal die Stunde in meiner gewählten Vorseinstellung!
Es wird also eine Weile dauern bis er alle Dienste hat.

Da ich den Zabbix-Agenten nicht überreden konnte die deutschen Umlaute korrekt zu übernehmen hab ich zum zweitbesten gegriffen was mir einfiel:
Alle Umlaute in den Dienstnamen werden durch alternative Schreibweisen ersetzt, also ein 'ä' wird zu 'ae' usw. - So wird es lesbarer


Features

Die neue Version V4 hat folgende Features:

  • Im Standard findet er nur alle Dienste die auf "Automatisch" stehen und zum Zeitpunkt der Erkennung auch laufen UND in der Registry nicht als "Bei Bedarf" gekennzeichnet sind
  • servdisc-ignore.txt: Alle Dienste die hier drin stehen werden ignoriert - Pro Zeile ein Dienstname. Es findet ein "Text in Text" Vergleich statt, er prüft also ob die Zeichenfolge aus der Datei im Dienstnamen oder im Anzeigenamen vorkommt. Den Dienst "Designs" könnte man also Filtern indem man den Anzeigenamen "Designs" angibt oder den Dienstnamen "Themes"
  • servdisc-include.txt: Alle Dienste die hier drin stehen werden unabhängig von Ihrem Status aufgenommen, also auch wenn diese deaktiviert sind oder auf manuell stehen. Der Vergleich funktioniert genauso wie bei der Ignorieren Datei
  • servdisc-InfoOnly.txt: Im Normalfall werden die Trigger zu den Items mit der Stufe "Average" erzeugt. Steht der Name des Dienstes in dieser Datei hier wird der Trigger dazu mit der Stufe "Information" angelegt. Die Datei hat keinen Einfluss auf die Erkennung der Dienste, nur auf den Trigger dazu.
  • code>servdisc-Critical.txt: Wie zuvor, jedoch werden die Trigger für diese Dienste mit der Stufe "Critical" angelegt. Auch diese Datei hat keinen Einfluss auf die Erkennung der Dienste.



Download

Aktuelle Version der servdisc.exe ist 4.0.0.5
Download: Zabbix-Service-Discovery.zip



Installation


Zur Installation müsst Ihr folgendes tun:

  • Kopiert die servdisc.exe und die servdisc-ignore.txt in das Verzeichnis in dem auch schon der Zabbix-Agent ist, z.B.
C:\Program Files\Zabbix
  • Tragt in die Agenten-Konfigurationsdatei, in der Regel ist das die zabbix_agentd.win.conf die Zeile
UserParameter=service.discovery,"C:\Program Files\Zabbix\servdisc.exe"

ein - Pfad natürlich ggf. anpassen

  • Startet des Zabbix Agenten Dienst neu
net stop "zabbix agent"
net start "zabbix agent"
  • Import eine der .XML Dateien in Zabbix und verknüpft diese mit dem Host.
  • Warten! Alle 60 Minuten sollten nun Dienste gefunden werden und der Liste hinzugefügt werden. Wenn es klappt könnte es eine gute Idee sein den Wert im Template unter Discovery von den 900 Sekunden weitaus höher zu setzen, z.B. nur noch alle 12h.
  • Dienste die nicht automatisch gefundern werden sollen obwohl diese auf Automatisch stehen tragt ihr in die servdisc-ignore.txt ein. Am besten nehmt Ihr den Dienstnamen - also den Kurznamen den man ganz oben in den Eigenschaften eines Dienstes sehen kann, nicht den langen Anzeigenamen.

ACHTUNG! Die servdisc-ignore.txt enthält beriets Einträge - Prüft die einmal vorher.



Screenshots

Die gefundenen Items (Auzug):

ZabbixServiceDiscovery-001.png


Und die Trigger dazu (Auszug):

ZabbixServiceDiscovery-002.png




Quellcode

Hier noch für Interessierte der Quellcode der servdisc.exe:

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=Icon256-32.ico
#AutoIt3Wrapper_Outfile=servdisc.exe
#AutoIt3Wrapper_UseUpx=y
#AutoIt3Wrapper_Change2CUI=y
#AutoIt3Wrapper_Res_Description=Zabbix Service Auto-Discovery
#AutoIt3Wrapper_Res_Fileversion=4.0.1.2
#AutoIt3Wrapper_Res_Fileversion_AutoIncrement=y
#AutoIt3Wrapper_Res_LegalCopyright=2013 Bernhard Linz
#AutoIt3Wrapper_Res_Language=1031
#AutoIt3Wrapper_Res_Field=Website|http://znil.net
#AutoIt3Wrapper_Res_Field=Manual|http://znil.net/index.php?title=Zabbix:Template_Windows_Service_Auto-Discover_-_Windows_Dienste_automatisch_entdecken
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <Array.au3>
Opt('MustDeclareVars', 1)
; ##########################################################################################################################
; ##########################################################################################################################
; # servdisc.exe --> Tool for Auto-Discovery Windows Services in Zabbix                                                    #
; # 2013/2014/2015 Bernhard Linz    /    Bernhard@znil.de    /    http://znil.net                                          #
; # Idea and Original Powershell Script by Raymond Kuiper                                                                  #
; # Use with Template of Raymond Kuiper at https://github.com/q1x/zabbix-templates/tree/master/service-discovery           #
; #                                                                                                                        #
; # Latest Version of this Program and Template in German:                                                                 #
; # http://znil.net/index.php?title=Zabbix:Template_Windows_Service_Auto-Discover_-_Windows_Dienste_automatisch_entdecken  #
; #                                                                                                                        #
; #	         ________  .__   __.  __   __        .__   __.  _______ .___________.                                          #
; #	        |       /  |  \ |  | |  | |  |       |  \ |  | |   ____||           |                                          #
; #	        `---/  /   |   \|  | |  | |  |       |   \|  | |  |__   `---|  |----`                                          #
; #	           /  /    |  . `  | |  | |  |       |  . `  | |   __|      |  |                                               #
; #	          /  /----.|  |\   | |  | |  `----.__|  |\   | |  |____     |  |                                               #
; #	         /________||__| \__| |__| |_______(__)__| \__| |_______|    |__|                                               #
; #                                                                                                                        #
; ##########################################################################################################################
; ##########################################################################################################################

; Catch all Error while using the WMI-Interface with own Error-Function
Global $oMyError = ObjEvent("AutoIt.Error","MyErrFunc"), $f_COMError = False
; And the Error-Function itself
Func MyErrFunc()
    Local $HexNumber=hex($oMyError.number,8)
    ConsoleWriteError("We intercepted a COM Error !" & @CRLF & _
                "Number is: " & $HexNumber & @CRLF & _
                "WinDescription is: " & $oMyError.windescription & @CRLF & _
                "Source is: " & $oMyError.source & @CRLF & _
                "ScriptLine is: " & $oMyError.scriptline & @CRLF)
	Exit 1
Endfunc

; ##########################################################################################################################
; ##########################################################################################################################
; Function for catching the special characters - hey we using the old DOS-Console and the Console did'nt like them
Func _ANSI2OEM($text)
	$text = DllCall('user32.dll', 'Int', 'CharToOem', 'str', $text, 'str', '')
	Return $text[2]
EndFunc   ;==>_ANSI2OEM

; ##########################################################################################################################
; ##########################################################################################################################
; Other needed Variables
Dim $o_WMI
Dim $o_colListOfServices
Dim $s_JSONOutput
Dim $s_Service2IgnoreFile = @ScriptDir & "\servdisc-ignore.txt"
Dim $h_Service2IgnoreFile
Dim $a_Service2Ignore[1] = [ 0 ]
Dim $b_IgnoreService = False

Dim $s_Service2IncludeFile = @ScriptDir & "\servdisc-include.txt"
Dim $h_Service2IncludeFile
Dim $a_Service2Include[1] = [ 0 ]
Dim $b_IncludeService = False

Dim $s_Service2InfoOnlyFile = @ScriptDir & "\servdisc-InfoOnly.txt"
Dim $h_Service2InfoOnlyFile
Dim $a_Service2InfoOnly[1] = [ 0 ]
Dim $b_InfoOnlyService = False

Dim $s_Service2CriticalFile = @ScriptDir & "\servdisc-Critical.txt"
Dim $h_Service2CriticalFile
Dim $a_Service2Critical[1] = [ 0 ]
Dim $b_CriticalService = False

Dim $s_AllServices = ":"

Dim $i_ServiceType
Dim $s_temp
Dim $i
Dim $s_spaces = '     ' ; 5 Spaces

Dim $s_DisplayName_cleaned

; ##########################################################################################################################
; ##########################################################################################################################
; Some of the services are irritating, just ignore them - read items from a file
If FileExists($s_Service2IgnoreFile) = 1 Then
	$h_Service2IgnoreFile = FileOpen($s_Service2IgnoreFile, 0)
	While 1
		$s_temp = FileReadLine($h_Service2IgnoreFile)
		If @error = -1 Then
			ExitLoop
		Else
			If StringLen($s_temp) > 2 Then ; at least 3 or more chars
				$a_Service2Ignore[0] = $a_Service2Ignore[0] + 1
				ReDim $a_Service2Ignore[$a_Service2Ignore[0] + 1] ;That's AutoIt - Redim an Array without data-loss
				$a_Service2Ignore[$a_Service2Ignore[0]] = $s_temp
			EndIf
		EndIf
	WEnd
EndIf

; ##########################################################################################################################
; ##########################################################################################################################
; Include Services independently of Running Status / Autostart status
If FileExists($s_Service2IncludeFile) = 1 Then
	$h_Service2IncludeFile = FileOpen($s_Service2IncludeFile, 0)
	While 1
		$s_temp = FileReadLine($h_Service2IncludeFile)
		If @error = -1 Then
			ExitLoop
		Else
			If StringLen($s_temp) > 2 Then ; at least 3 or more chars
				$a_Service2Include[0] = $a_Service2Include[0] + 1
				ReDim $a_Service2Include[$a_Service2Include[0] + 1] ;That's AutoIt - Redim an Array without data-loss
				$a_Service2Include[$a_Service2Include[0]] = $s_temp
			EndIf
		EndIf
	WEnd
EndIf

; ##########################################################################################################################
; ##########################################################################################################################
; Set Trigger of some services to "Information" instead of "Average"
If FileExists($s_Service2IncludeFile) = 1 Then
	$h_Service2InfoOnlyFile = FileOpen($s_Service2InfoOnlyFile, 0)
	While 1
		$s_temp = FileReadLine($h_Service2InfoOnlyFile)
		If @error = -1 Then
			ExitLoop
		Else
			If StringLen($s_temp) > 2 Then ; at least 3 or more chars
				$a_Service2InfoOnly[0] = $a_Service2InfoOnly[0] + 1
				ReDim $a_Service2InfoOnly[$a_Service2InfoOnly[0] + 1] ;That's AutoIt - Redim an Array without data-loss
				$a_Service2InfoOnly[$a_Service2InfoOnly[0]] = $s_temp
			EndIf
		EndIf
	WEnd
EndIf

; ##########################################################################################################################
; ##########################################################################################################################
; Set Trigger of some services to "Critical" instead of "Average"
If FileExists($s_Service2CriticalFile) = 1 Then
	$h_Service2CriticalFile = FileOpen($s_Service2CriticalFile, 0)
	While 1
		$s_temp = FileReadLine($h_Service2CriticalFile)
		If @error = -1 Then
			ExitLoop
		Else
			If StringLen($s_temp) > 2 Then ; at least 3 or more chars
				$a_Service2Critical[0] = $a_Service2Critical[0] + 1
				ReDim $a_Service2Critical[$a_Service2Critical[0] + 1] ;That's AutoIt - Redim an Array without data-loss
				$a_Service2Critical[$a_Service2Critical[0]] = $s_temp
			EndIf
		EndIf
	WEnd
EndIf

; ##########################################################################################################################
; ##########################################################################################################################
; Get the WMI-Interface at local Computer (= .)
$o_WMI = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
; Test whether it worked and go on
If IsObj($o_WMI) Then
	; Get List of all Autostart-Services
	$o_colListOfServices = $o_WMI.ExecQuery("Select * from Win32_Service where Startmode='Auto'")
	; Ok, we have the List, now look if we were interrupted last Time
	; Start creating the JSON Output:
	$s_JSONOutput = '{"data":[' & '@CRLF'
	; Loop: Add the Services to JSON Output String Line for Line but nor more than 4000 Chars
	For $objService In $o_colListOfServices
		$b_IgnoreService = False
		$b_InfoOnlyService = False
		$b_CriticalService = False
		; Do nothing until we found the Service of last Run
		; Will start after the first Part found the Service of last Time or will run from beginning if there was no last time
		; Test 1: But first check if the service is one of the bad one we wanna ignore
		If $a_Service2Ignore[0] > 0 Then
			For $i = 1 To $a_Service2Ignore[0]
				;ConsoleWrite($objService.DisplayName & "<--->" & $a_Service2Ignore[$i] & @CRLF)
				If StringInStr($objService.DisplayName, $a_Service2Ignore[$i]) > 0 Then
					$b_IgnoreService = True
				EndIf
				If StringInStr($objService.Name, $a_Service2Ignore[$i]) > 0 Then
					$b_IgnoreService = True
				EndIf
			Next
		EndIf
		If $a_Service2InfoOnly[0] > 0 Then
			For $i = 1 To $a_Service2InfoOnly[0]
				;ConsoleWrite($objService.DisplayName & "<--->" & $a_Service2Ignore[$i] & @CRLF)
				If StringInStr($objService.DisplayName, $a_Service2InfoOnly[$i]) > 0 Then
					$b_InfoOnlyService = True
				EndIf
				If StringInStr($objService.Name, $a_Service2InfoOnly[$i]) > 0 Then
					$b_InfoOnlyService = True
				EndIf
			Next
		EndIf
		If $a_Service2Critical[0] > 0 Then
			For $i = 1 To $a_Service2Critical[0]
				If StringInStr($objService.DisplayName, $a_Service2Critical[$i]) > 0 Then
					$b_CriticalService = True
				EndIf
				If StringInStr($objService.Name, $a_Service2Critical[$i]) > 0 Then
					$b_CriticalService = True
				EndIf
			Next
		EndIf

		; Test 2: Just another Test - we don't want "Trigger Start" Services
		$i_ServiceType = RegRead("HKLM\SYSTEM\CurrentControlSet\Services\" & $objService.Name & "\TriggerInfo\0", "Action")
		If $i_ServiceType <> "" Then
			$b_IgnoreService = True
		EndIf
		; Test 3: Only "Running" Services
		If $objService.State <> "Running" Then
			$b_IgnoreService = True
		EndIf
		; Ok, all Tests processed, now add the service to list (if we don't wanna ignore this service)
		If $b_IgnoreService = False Then
			If ($b_InfoOnlyService = False) And ($b_CriticalService = False) Then
				$s_JSONOutput = $s_JSONOutput & _
								$s_spaces & '{' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICEDISPLAY}":"' & $objService.DisplayName & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICENAME}":"' & $objService.Name & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICESTATE}":"' & $objService.State & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICEDESC}":"-"' & '@CRLF' & _
								$s_spaces & '},' & '@CRLF'
			ElseIf $b_CriticalService = True Then
				$s_JSONOutput = $s_JSONOutput & _
								$s_spaces & '{' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#CRITICALSERVICEDISPLAY}":"' & $objService.DisplayName & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#CRITICALSERVICENAME}":"' & $objService.Name & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#CRITICALSERVICESTATE}":"' & $objService.State & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#CRITICALSERVICEDESC}":"-"' & '@CRLF' & _
								$s_spaces & '},' & '@CRLF'
			ElseIf $b_InfoOnlyService = True Then
				$s_JSONOutput = $s_JSONOutput & _
								$s_spaces & '{' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICEDISPLAY}":"' & $objService.DisplayName & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICENAME}":"' & $objService.Name & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICESTATE}":"' & $objService.State & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICEDESC}":"-"' & '@CRLF' & _
								$s_spaces & '},' & '@CRLF'
			EndIf
			$s_AllServices = $s_AllServices & $objService.Name & ":"
		EndIf
	Next
	; And now all again for finding Included Services
	$o_colListOfServices = $o_WMI.ExecQuery("Select * from Win32_Service")

	For $objService In $o_colListOfServices
		$b_IncludeService = False
		$b_InfoOnlyService = False
		If $a_Service2Include[0] > 0 Then
			For $i = 1 To $a_Service2Include[0]
				If StringInStr($objService.DisplayName, $a_Service2Include[$i]) > 0 Then
					$b_IncludeService = True
				EndIf
				If StringInStr($objService.Name, $a_Service2Include[$i]) > 0 Then
					$b_IncludeService = True
				EndIf
			Next
		EndIf
		If StringInStr($s_AllServices, ":" & $objService.Name & ":") > 0 Then
			;Service is already in List!!!
			$b_IncludeService = False
		EndIf
		If ($a_Service2InfoOnly[0] > 0) And ($b_IncludeService = True) Then
			For $i = 1 To $a_Service2InfoOnly[0]
				;ConsoleWrite($objService.DisplayName & "<--->" & $a_Service2Ignore[$i] & @CRLF)
				If StringInStr($objService.DisplayName, $a_Service2InfoOnly[$i]) > 0 Then
					$b_InfoOnlyService = True
				EndIf
				If StringInStr($objService.Name, $a_Service2InfoOnly[$i]) > 0 Then
					$b_InfoOnlyService = True
				EndIf
			Next
		EndIf
		If $b_IncludeService = True Then
			If $b_InfoOnlyService = False Then
				$s_JSONOutput = $s_JSONOutput & _
								$s_spaces & '{' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICEDISPLAY}":"' & StringRegExpReplace($objService.DisplayName,"\W","") & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICENAME}":"' & $objService.Name & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICESTATE}":"' & $objService.State & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#SERVICEDESC}":"-"' & '@CRLF' & _
								$s_spaces & '},' & '@CRLF'
			Else
				$s_JSONOutput = $s_JSONOutput & _
								$s_spaces & '{' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICEDISPLAY}":"' & StringRegExpReplace($objService.DisplayName,"\W","") & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICENAME}":"' & $objService.Name & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICESTATE}":"' & $objService.State & '",' & '@CRLF' & _
								$s_spaces & $s_spaces & '"{#INFOSERVICEDESC}":"-"' & '@CRLF' & _
								$s_spaces & '},' & '@CRLF'
			EndIf
			$s_AllServices = $s_AllServices & $objService.Name & ":"
		EndIf
	Next

	; Ok, just a Test if someone muck around us - let us stop the last time at a service and than, before next run
	; they have deleted this service so we can never find it. Or someone manipulate our Memory File
	; Delete the last -> , <-
	$s_JSONOutput = StringTrimRight($s_JSONOutput, StringLen(',' & '@CRLF'))
	$s_JSONOutput = $s_JSONOutput & ']}'
	; SpecialChars replace
	$s_JSONOutput = StringReplace($s_JSONOutput, "ä", "ae", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "Ä", "Ae", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "ö", "oe", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "Ö", "Oe", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "ü", "ue", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "Ü", "Ue", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "ß", "ss", 0, 1)
	$s_JSONOutput = StringReplace($s_JSONOutput, "@CRLF", @CRLF, 0, 1)
	ConsoleWrite(_ANSI2OEM($s_JSONOutput))

EndIf
Exit 0




Kommentare

Loading comments...