Zabbix:Template Windows Service Auto-Discover - Windows Dienste automatisch entdecken
Aus znilwiki
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 Dateiservdisc-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 dieservdisc-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):
Und die Trigger dazu (Auszug):
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