Zabbix. Список установленных программ.

Будем использовать Zabbix для слежения за списком установленных программ.

Замечание 1.
Способ установки Python 2.7 описан тут: https://www.mihanik.net/tihaja-ustanovka-python-2-7/

Замечание 2.

Способ становки Zabbix-агента (клиента) описан тут: https://www.mihanik.net/ustanovka-zabbix-klienta/

1. Собираем данные по установленным программам при помощи скрипта.

Скрипт написан на Python 2.7, он собирает данные по установленным программам и формирует 2 файла:

    • непосредственно сам список установленных программ — C:\Zabbix\scripts\soft_list\soft_list.lst
    • список изменений в установленных программах (что удалено/установлено) — C:\Zabbix\scripts\soft_list\soft_diff.lst

Скрипт сохранён под именем C:\Zabbix\scripts\soft_list\programmlist.py.

Текст скрипта:
# -*- coding: utf-8 -*-
import os
import sys
import ctypes
import errno
import _winreg
import codecs

#
# Функция проверки наличия прав администратора
# Вход: нет
# Выход: true - есть права администратора,
#        false - нет прав администратора
#
def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False
# 
# Функция удаления дубликатов из списка
# Вход: список
# Выход: список без дубликатов
#
def DuplicateRemoval ( InboundList ):
    OutboundList = []
    for i in InboundList:
        if i not in OutboundList:
            OutboundList.append(i)
    return OutboundList

# 
# Функция загрузки списка из файла
# Вход: путь к файлу со списком
# Выход: список
#
def ListLoad ( InboundFileName ):
    OutboundList = []
    
    # Если указанного файла не существует возвращаем пустой список
    if not os.path.exists( InboundFileName ):
        return OutboundList
    
    if not os.path.isfile( InboundFileName ):
        return OutboundList
    
    # Читаем файл построчно
    f = open( InboundFileName,'r')
    try:
        OutboundList = f.read().splitlines()
    except Exception:
        pass
    finally:
        f.close()

    return OutboundList

# 
# Функция сохранения списка в файл
# Вход: список, путь к файлу
# Выход: true - успешная запись, false - ошибка при записи
#
def ListSave ( OutboundList, OutboundFileName ):
    
    # Записываем файл построчно
   f = open( OutboundFileName,'w')
   try:
     for line in OutboundList:
           f.write(line + '\n')
   except Exception:
       MyResult = False
   else:
       MyResult = True
   finally:
      f.close()
    
   return MyResult

# 
# Функция получения списка установленных программ
# Вход: нет
# Выход: список установленных программ
#
def ProgramList():
    
    # Определим разрядность системы
    try:
        os.environ["PROGRAMFILES(X86)"]
        proc_arch = 64
    except:
        proc_arch = 32
    
    # Зададим режимы просмотра реестра
    if proc_arch == 32:
        arch_keys = []
    elif proc_arch == 64:
        arch_keys = [ _winreg.KEY_WOW64_32KEY, _winreg.KEY_WOW64_64KEY ]
    
    # Зададим ветки реестра, где нужно искать информацию об установленных программах
    keys = [ r'hklm\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
             r'hklm\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
             r'hkcu\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall']
    
    List = []
    
    for arch_key in arch_keys:
        for MyKey in keys:
            # ветки hkcu\...\Uninstall может не быть. Предусмотрим вероятность срабатывания исключения
            try:
               if 'hkcu' not in MyKey:
                  key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, MyKey[5:], 0, _winreg.KEY_READ | arch_key)
               else:
                  key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, MyKey[5:], 0, _winreg.KEY_READ | arch_key)
            except Exception:
               pass
            else:
               for i in xrange(0, _winreg.QueryInfoKey(key)[0]):
                   skey_name = _winreg.EnumKey(key, i)
                   skey = _winreg.OpenKey(key, skey_name)
                   try:
                       List.append ( _winreg.QueryValueEx(skey, 'DisplayName')[0] )
                   except OSError as e:
                       if e.errno == errno.ENOENT:
                           # DisplayName не существует в этом разделе
                           pass
                   finally:
                       skey.Close()
    
    List.sort()
    NewList = DuplicateRemoval (List)
    return NewList

################################################################################
#   Начало программы
################################################################################
def main(argv=None):
   # Проверяем наличие прав админа.
   if not is_admin ():
      sys.exit ('Not enough permissions to run the script !!!')
   
   # Устанавливаем кодировку по умолчанию.
   reload(sys)
   sys.setdefaultencoding('utf8')
   
   # Зададим имена файлам, которые будем использовать
   MySoftListFile=r'C:\Zabbix\scripts\soft_list\soft_list.lst'
   MySoftDiffFile=r'C:\Zabbix\scripts\soft_list\soft_diff.lst'
   
   # Получаем список установленных программ
   List = ProgramList()
   
   # Получаем прежний список из файла
   OldList = ListLoad ( MySoftListFile )
   
   # Ищем установленные/удалённые программы
   DeletedList = []
   InstalledList = []
   
   # Получаем список свежеустановленных программ
   for i in List:
       if i not in OldList:
            InstalledList.append(i)
   
   # Получаем список удалённых программ
   for i in OldList:
       if i not in List:
            DeletedList.append(i)
   
   # Сохраняем список изменений в diff-файл построчно
   f = open( MySoftDiffFile,'w')
   try:
      f.write('Deleted:'+ '\n')
      for line in DeletedList:
         f.write(line + '\n')
      f.write('\n')
      f.write('Installed:'+ '\n')
      for line in InstalledList:
         f.write(line + '\n')
   except Exception:
      pass
   finally:
      f.close()
   
   # Сохраняем текущий список установленных программ в файл
   if not ListSave (List, MySoftListFile):
      sys.exit ('Unable to save installed program list!!!')

if __name__ == "__main__":
    sys.exit(main())

2. Устанавливаем скрипт в системе.

Скрипт лучше запускать каждый час, — не слишком часто, но и не слишком редко.

Запланировать выполнение скрипта можно при помощи планировщика Windows. Задание планировщика можно создать вручную, а можно и при помощи bat-файла.

Я обычно это делаю при помощи «батника», имя своему батнику я дал C:\Zabbix\scripts\soft_list\INSTALL_get_soft_list.bat .

Текст скрипта:
@echo off

Rem Предполагаем, что на Windows XP скрипт запускается администратором.
Rem Для более старших систем это неверно.

rem Получаем версию ОС
ver | find "5.1."

rem Windows XP ?
If %errorlevel%==0  (
	rem Пропускаем проверку админских прав
	GOTO SKIPADMIN
 )
 
SET HasAdminRights=0

FOR /F %%i IN ('WHOAMI /PRIV /NH') DO (
	IF "%%i"=="SeTakeOwnershipPrivilege" SET HasAdminRights=1
)

IF NOT %HasAdminRights%==1 (
	ECHO .
	ECHO Not enough permissions to run the script !!!
	ECHO .
	GOTO END
)

:SKIPADMIN

rem Получаем версию ОС
rem Windows XP ?

ver | find "5.1."

If %errorlevel%==0  (
	rem Windows XP
	SCHTASKS /Create /RU "NT AUTHORITY\SYSTEM" /SC HOURLY /ST 00:00:00 /TN "InstalledSoftware" /TR "python \"C:\zabbix\scripts\soft_list\programmlist.py\""
 ) else (
	rem НЕ Windows XP
	SCHTASKS /Create /RU "NT AUTHORITY\SYSTEM" /SC DAILY /ST 00:00 /RI 60 /DU 24:00 /TN "InstalledSoftware" /TR "python \"C:\zabbix\scripts\soft_list\programmlist.py\"" /RL HIGHEST /F
 )

cd "C:\Zabbix\scripts\soft_list"

python "C:\zabbix\scripts\soft_list\programmlist.py"

:END

EXIT /B

3. Формируем шаблон в Zabbix.

В Zabbix при этом добавляем несложный шаблон.

Сначала приведу описание шаблона в картинках.

Имя шаблона: Active Computer — Python — ProgramList


Добавляем группу элементов данных…


Теперь добавляем 3 элемента данных…




И, наконец, несложный триггер, который будет срабатывать при изменении списка программ…


А вот и готовый для импорта заархивированный файл с описанным выше шаблоном:  zbx_export_template_programlist.xml

Описанные ранее скрипты можно найти в этом архиве: soft_list

4. Сбор данных.

Осталось назначить созданный шаблон соответствующему узлу сети и ждать начала поступления данных. 🙂

Всё.

🙂