Отчёт по событиям на Windows серверах домена

    Всем привет!
    Удобно получать в почту ежедневный отчёт о событиях на серверах домена за предыдущий день. Можно и за текущий, не суть важно. Когда такие отчёты собираются за длительный промежуток времени (за несколько лет, например), можно найти, кто завёл определённого пользователя, кто пользователя добавил/удалил из группы, кто поменял пользователю пароль (или когда он сам себе поменял), логины на серверы, неудачные логины и так далее. В принципе, каждый сам для себя определяет набор событий для отчётов. Главное принцип.
    Нам, например, в почту приходит вот такой отчёт:

    Кому нужно, под катом реализация.

    Скрипт выполняется каждое утро в 4 часа. Для его работы на сервере надо установить LogParser и 7-ZIP (если файл отчёта больше 3 МБ, то он пакуется zip’ом).
    На всякий случай ссылка на полезный документ по событиям 7-ки и 2008 сервера Vista_2008_Security_Event_Descriptions.xlsx.
    У меня скрипт лежит на диске C в папке script. В папке script папка Tamplates для шаблонов. Плюс папки на F Logi_ForADReports для временных evt-файлов и Reports для html-файлов отчётов. В папке Reports также создаётся журнал работы скрипта.
    Bat-файл запуска скрипта
    net use Q: \\nas-srv\BACKUP
    cscript //nologo "c:\script\LogParser_bat_4.vbs" %1 %2 %3
    net use Q: /delete
    

    Скрипт LogParser_bat_4.vbs
    ' Ежедневный отчет по событиям на серверах
    ' Автор Лужин Кирилл
    ' luzhin.kirill@yandex.ru
    
    'On Error Resume Next
    
    const gsReportFolder = "F:\Reports\"
    const gsFrom = "admin1@domain.com"
    const gsSubject = "send report"
    const gsHelpFile = "c:\script\LogParser_bat.txt"
    const gbDebugModeON = false
    
    Dim oLogQuery
    Dim oMyInputFormat
    Dim oCSVOutputFormat 
    Dim strQuery
    Dim giErrorCode
    Dim gsFileNameLog
    Dim gsNormalDate
    Dim gsTo
    Dim gArrNumberOfFunctions
    gArrNumberOfFunctions = Array ("1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")
    
    
    gsTo = "admin1@domain.com"
    gsEMail = "n"
    ' Это для отчета за сегодня (на всякий случай):
    ' gsNormalDate = fuNormalizeSystemDate(cStr(Date))
    ' Это для отчета за вчера (нормальный режим):
    gsNormalDate = fuNormalizeSystemDate(cStr(DateAdd("d", -1, Date)))
    gsDate = gsNormalDate
    gsNumberOfFunctions = "all"
    
    
    gsCheckDate = DateAdd("d", -1, Date)
    gsLogFilename = fuGetFilename(gsCheckDate)
    
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    gsFileNameLog = gsReportFolder & gsNormalDate & ".log"
    Set objTextFileWriteLog = objFSO.OpenTextFile(gsFileNameLog, 8, True)
    
    
    ' отчет за последние 33 часа:
    fuWritedown "* Дата отчета: " & Now, 4
    gsPastDate = DateAdd("h", -33, Now) 
    fuWritedown "* Отчеты создаются начиная с " & gsPastDate, 4
    ' отчет за последние 2 дня:
    ' gsPastDate = DateAdd("d", -2, Date)
    	
    if Wscript.Arguments.Count >= 1 then
    	if lCase(Wscript.Arguments(0)) = "nothing" then
    		gArrNumberOfFunctions = Array ("0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0")
    		gsNumberOfFunctions = "nothing"
    	elseif InStr(Wscript.Arguments(0), ",") then
    		gArrNumberOfFunctions = split(Wscript.Arguments(0), ",")
    		gsNumberOfFunctions = "different"
    	elseif fuNeedHelp(lCase(Wscript.Arguments(0))) then
    		fuTypeTextfile(gsHelpFile)
    		WScript.Quit 0
    	'else gArrNumberOfFunctions = Array ("1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")
    	end if
    	
    	if Wscript.Arguments.Count >= 2 then
    		if InStr(Wscript.Arguments(1), "@") then
    			gsEMail = "y"
    			gsTo = Wscript.Arguments(1)
    		else
    			gsEMail = lCase(Wscript.Arguments(1))
    		end if
    		
    		if Wscript.Arguments.Count = 3 then
    			gsDate = Wscript.Arguments(2)
    		end if
    	end if
    end if
    
    
    fuWritedown "* Имя файла журнала: " & gsFileNameLog, 2
    
    gStartTime = fuStartTimer("")
    
    if gsNumberOfFunctions <> "nothing" then
    	gArrProcNamesList = Array (_
    		"Процедура поиска логинов администраторов", _
    		"Процедура AccauntManage", _
    		"Процедура создания статистики неудачных логинов", _
    		"Процедура управления группами", _
    		"Процедура поиска неудачных логинов", _
    		"Процедура управления паролями", _
    		"Процедура управления компьютерами", _
    		"Процедура аудита", _
    		"Процедура статистики аудита", _
    		"Процедура поиска логинов к RDP",_
    		"Процедура слежения за действиями над объектами в AD")
    		
    	gArrReportfilesList = Array (_
    		gsReportFolder & "logged_Administrator_" & gsNormalDate & ".html", _
    		gsReportFolder & "new_AD_" & gsNormalDate & ".html", _
    		gsReportFolder & "logonFailuresStats_" & gsNormalDate & ".html", _
    		gsReportFolder & "group_Manage_" & gsNormalDate & ".html", _
    		gsReportFolder & "logonFailure_" & gsNormalDate & ".html", _
    		gsReportFolder & "change_password_" & gsNormalDate & ".html", _
    		gsReportFolder & "new_Comp_AD_" & gsNormalDate & ".html", _
    		gsReportFolder & "audit_" & gsNormalDate & ".html", _
    		gsReportFolder & "auditStat_" & gsNormalDate & ".html", _
    		gsReportFolder & "logged_Rdp_" & gsNormalDate & ".html", _
    		gsReportFolder & "AD_objects_" & gsNormalDate & ".html")
    		
    
    
    	for gix = 0 to UBound(gArrNumberOfFunctions)
    		gsFunctionName = gArrProcNamesList(gix)
    		gsReportfile = gArrReportfilesList(gix)
    		
    		if gArrNumberOfFunctions(gix) = "1" then	
    			startTime = fuStartTimer(gsFunctionName)
    			gArrServerList = Array ("DC1", "DC2")
    			Select Case gix
    				Case 0: giErrorCode = fuLogonAdministrator(gArrServerList, gsReportfile)
    				Case 1: giErrorCode = fuAccauntManage(gArrServerList, gsReportfile)
    				Case 2: giErrorCode = fuLogonFailureStats(gArrServerList, gsReportfile)
    				Case 3: gArrServerList = Array ("DC1","DC2","EXCH1","EXCH2")
    						giErrorCode = fuGroupManage(gArrServerList, gsReportfile)
    				Case 4: giErrorCode = fuLogonFailures(gArrServerList, gsReportfile)
    				Case 5: giErrorCode = fuPasswordManage(gArrServerList, gsReportfile)
    				Case 6: giErrorCode = fuCompManage(gArrServerList, gsReportfile)
    				Case 7: gArrServerList = Array ("FILE-SRV1","FILE-SRV2")
    						giErrorCode = fuAudit(gArrServerList, gsReportfile)
    				Case 8: gArrServerList = Array ("FILE-SRV1","FILE-SRV2")
    						giErrorCode = fuAuditStat(gArrServerList, gsReportfile)
    				Case 9: gArrServerList = Array ("DC1","DC2","EXCH1","EXCH2")
    						giErrorCode = fuLogonRdp(gArrServerList, gsReportfile, gsFunctionName)
    				Case 10: giErrorCode = fuADObjects(gArrServerList, gsReportfile)
    				Case else fuWritedown "* Некорректный индекс: " & gix, 4
    			End Select
    			fuCheckErrorCode giErrorCode, gArrServerList, gsReportfile, gsFunctionName, startTime
    		else
    			fuWritedown gsFunctionName & " пропущена", 4
    		end if
    	next
    else
    	fuWritedown "* Выбран вариант без создания отчетов", 4
    end if
    
    fuStopTimer(gStartTime)
    
    if gsEMail = "y" then
    	fuSendReportMail gsReportFolder & "*_" & gsDate & ".*", gsFrom, gsTo, gsSubject, gsDate
    else
    	fuWritedown "* Выбран вариант без отсылания отчетов", 4
    end if
    
    fuWritedown "* Журнал сохранен в Файл '" & gsFileNameLog & "'", 1
    fuDeleteEvtxFiles "F:\Logi_ForADReports\*.evtx"
    'MsgBox "Журнал сохранен в Файл '" & gsFileNameLog & "'", vbInformation, "Информация"
    objTextFileWriteLog.Close
    
    ' Процедура поиска логинов администраторов
    function fuLogonAdministrator(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		' Create Input Format object
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		' Create Output Format object
    		' Set oCSVOutputFormat = CreateObject("MSUtil.LogQuery.CSVOutputFormat")
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		'oCSVOutputFormat.tabs = TRUE
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonAdministrator.tpl"
    
    		' Создание текста запроса
    		strQuery = "SELECT TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) as UserName, eventid, TimeGenerated, ComputerName as DC, " & _
    			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS LogonName, " & _
    			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,6,'|')) AS Domain, " & _
    			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,13,'|')) AS LogonWKS, " & _
    			"extract_token(trim(extract_token(Message, 18, ':' )), 0, ' ') as LogonIP, " & _
    			"CASE TO_INT(EXTRACT_TOKEN(Strings,10,'|')) " & _
    			"	WHEN 2 THEN  'Interactive - Intended for users who will be interactively using the machine, such as a user being logged on by a terminal server, remote shell, or similar process.'" & _
    			"	WHEN 3 THEN  'Network - Intended for high performance servers to authenticate clear text passwords. LogonUser does not cache credentials for this logon type.'" & _
    			"	WHEN 4 THEN  'Batch - Intended for batch servers, where processes may be executing on behalf of a user without their direct intervention; or for higher performance servers that process many clear-text authentication attempts at a time, such as mail or web servers. LogonUser does not cache credentials for this logon type.'" & _
    			"	WHEN 5 THEN  'Service - Indicates a service-type logon. The account provided must have the service privilege enabled.'" & _
    			"	WHEN 6 THEN  'Proxy - Indicates a proxy-type logon.'" & _
    			"	WHEN 7 THEN  'Unlock - This logon type is intended for GINA DLLs logging on users who will be interactively using the machine. This logon type allows a unique audit record to be generated that shows when the workstation was unlocked.'" & _
    			"	WHEN 8 THEN  'NetworkCleartext - Windows 2000; Windows XP and Windows Server 2003 family:  Preserves the name and password in the authentication packages, allowing the server to make connections to other network servers while impersonating the client. This allows a server to accept clear text credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.'" & _
    			"	WHEN 9 THEN  'NewCredentials - Windows 2000; Windows XP and Windows Server 2003 family:  Allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identity, but uses different credentials for other network connections.'" & _
    			"	WHEN 10 THEN 'RemoteInteractive - Terminal Server session that is both remote and interactive.'" & _
    			"	WHEN 11 THEN 'CachedInteractive - Attempt cached credentials without accessing the network.'" & _
    			"	WHEN 12 THEN 'CachedRemoteInteractive - Same as RemoteInteractive. This is used for internal auditing.'" & _
    			"	WHEN 13 THEN 'CachedUnlock - Workstation logon'" & _
    			"	ELSE EXTRACT_TOKEN(Strings,10,'|') " & _
    			"END AS LogonType, " & _
    			"extract_token(strings, 4, '|' ) as LogonProc, " & _
    			"extract_token(strings, 11, '|' ) as ProcessID " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4624;4636) " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    			"AND ((TO_LOWERCASE(LogonName) = TO_LOWERCASE('administrator')) " & _
    			" OR  (TO_LOWERCASE(LogonName) = TO_LOWERCASE('администратор')) " & _
    			" OR  (TO_LOWERCASE(LogonName) = TO_LOWERCASE('admin'))) "
    		
    		fuWritedown "* Запрос поиска логинов администраторов: '" & strQuery & "'", 4
    
    		' Execute query
    		oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    
    	fuLogonAdministrator = liErrorCode
    end function
    
    'Процедура AccauntManage
    function fuAccauntManage(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    	'lsFROM = "\\DC1\c$\WINDOWS\system32\winevt\Logs\Archive-Security-2010-08-03-09-34-11-527.evtx"
    	'lsFROM = "\\DC1\security"
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\accauntManage.tpl"
    			
    		strQuery = "select extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, Message as EventCategoryName, " & _
    			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
    			"extract_token(extract_token(Message, 8, ':' ), 1, ' ') as Name " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4720;4722;4725;4726;4738;4740;4767;4780;4781;4782) " &_
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
    
    		fuWritedown "* Запрос AccauntManage: '" & strQuery & "'", 4
    		
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    
    	fuAccauntManage = liErrorCode
    end function
    
    'Процедура создания статистики неудачных логинов
    function fuLogonFailureStats(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonFailuresStats.tpl"
    
    		strQuery = "SELECT TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS User, " & _
    		"COUNT(*) AS Total " & _
    		"INTO " & lsReport & " " & _
    		"FROM " & lsFROM & " " & _
    		"WHERE EventID IN (4625) " & _
    		"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    		"GROUP BY User " & _
    		"ORDER BY Total DESC"
    			
    		fuWritedown "* Запрос статистики неудачных логинов: '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuLogonFailureStats = liErrorCode
    end function
    
    'Процедура управления группами
    function fuGroupManage(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\groupManage.tpl"
    
    		strQuery = "SELECT extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, EventCategoryName, " & _ 
    			"extract_token(extract_token(Message, 0, ':' ), 0, '.') as EventIDName, " & _
    			"COALESCE(extract_token(extract_token(strings, 0, ',' ), 1, '='), extract_token(strings, 0, '|' ), strings) as Name, " & _
    			"COALESCE(extract_token(extract_token(strings, 0, ',' ), 1, '='), extract_token(strings, 1, '|' ), strings) as SIDName, " & _
    			"extract_token(strings, 2, '|' ) as Name_Group, " & _
    			"EventID, extract_token(ComputerName, 0, '.') " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4727;4728;4729;4730;4731;4732;4733;4734;4735;4737;4744;4745;4746;4747;4748;4749;4750;4751;4752;4753;4754;4755;4756;4757;4758;4759;4760;4761;4762;4764;4783;4784;4785;4786;4787;4788;4789;4790) " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
    			
    		fuWritedown "* Запрос управления группами: '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuGroupManage = liErrorCode
    end function
    
    'Процедура поиска неудачных логинов
    function fuLogonFailures(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonFailures.tpl"
    
    		strQuery = "SELECT COUNT(EventID) AS TotalLogonFailures, " & _
    			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS User, " & _
    			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,6,'|')) AS Domain, " & _
    			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,13,'|')) AS WorkStation, " & _
    			"CASE TO_INT(EXTRACT_TOKEN(Strings,10,'|')) " & _
    			"	WHEN 2 THEN  'Interactive - Intended for users who will be interactively using the machine, such as a user being logged on by a terminal server, remote shell, or similar process.'" & _
    			"	WHEN 3 THEN  'Network - Intended for high performance servers to authenticate clear text passwords. LogonUser does not cache credentials for this logon type.'" & _
    			"	WHEN 4 THEN  'Batch - Intended for batch servers, where processes may be executing on behalf of a user without their direct intervention; or for higher performance servers that process many clear-text authentication attempts at a time, such as mail or web servers. LogonUser does not cache credentials for this logon type.'" & _
    			"	WHEN 5 THEN  'Service - Indicates a service-type logon. The account provided must have the service privilege enabled.'" & _
    			"	WHEN 6 THEN  'Proxy - Indicates a proxy-type logon.'" & _
    			"	WHEN 7 THEN  'Unlock - This logon type is intended for GINA DLLs logging on users who will be interactively using the machine. This logon type allows a unique audit record to be generated that shows when the workstation was unlocked.'" & _
    			"	WHEN 8 THEN  'NetworkCleartext - Windows 2000; Windows XP and Windows Server 2003 family:  Preserves the name and password in the authentication packages, allowing the server to make connections to other network servers while impersonating the client. This allows a server to accept clear text credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.'" & _
    			"	WHEN 9 THEN  'NewCredentials - Windows 2000; Windows XP and Windows Server 2003 family:  Allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identity, but uses different credentials for other network connections.'" & _
    			"	WHEN 10 THEN 'RemoteInteractive - Terminal Server session that is both remote and interactive.'" & _
    			"	WHEN 11 THEN 'CachedInteractive - Attempt cached credentials without accessing the network.'" & _
    			"	WHEN 12 THEN 'CachedRemoteInteractive - Same as RemoteInteractive. This is used for internal auditing.'" & _
    			"	WHEN 13 THEN 'CachedUnlock - Workstation logon'" & _
    			"	ELSE EXTRACT_TOKEN(Strings,10,'|') " & _
    			"END AS Type " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4625) " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    			"GROUP BY User,Domain,WorkStation,Type " & _
    			"ORDER BY TotalLogonFailures DESC"
    			
    		fuWritedown "* Запрос создания статистики неудачных логинов: '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuLogonFailures = liErrorCode
    end function
    
    'Процедура управления паролями
    function fuPasswordManage(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\PasswordManage.tpl"
    		
    		strQuery = "SELECT extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, EventCategoryName, " & _
    			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
    			"extract_token(extract_token(Message, 8, ':' ), 0, ' Account ') as Name " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4723;4724;4782;4793) " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
    			
    		fuWritedown "* Запрос управления паролями: '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuPasswordManage = liErrorCode
    end function
    
    'Процедура управления компьютерами
    function fuCompManage(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\compManage.tpl"
    		
    		strQuery = "SELECT extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, EventCategoryName, " & _
    			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
    			"extract_token(extract_token(Message, 8, ':' ), 0, ' Account ') as Name " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID in (4720;4742;4743) " & _
    			"and Name like '%%$%%' " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
    			
    		fuWritedown "* Запрос управления компьютерами: '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuCompManage = liErrorCode
    end function
    
    'Процедура аудита
    function fuAudit(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\audit.tpl"
    
    		strQuery = "select TimeGenerated, EventID, " & _
    			"extract_token(Strings, 0, '|' ) as UserSID,  " & _
    			"extract_token(Strings, 6, '|' ) as ObjectName, " & _
    			"extract_token(Strings, 1, '|' ) as User, " & _
    			"extract_token(Strings, 2, '|' ) as Domain, " & _
    			"extract_token(Strings, 5, '|' ) as ObjectType, " & _
    			"extract_token(Strings, 11, '|' ) as ProgramName, " & _
    			"extract_token(Message, 0, '.' ) as Event " & _
    			"into " & lsReport & " " & _
    			"from " & lsFROM & " " & _
    			"where EventId in (4656;4659;4660;4661;4663;4691) " & _
    			"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    			"and User <> 'NT AUTHORITY\SYSTEM' " & _
    			"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
    			"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
    			"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
    			"and User <> 'FILE-SRV1$' " & _
    			"and User <> 'FILE-SRV2$' " & _
    			"order by Timegenerated"
    			
    		fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		lsFROM = fuCollectFileList(lArrServerList, true)
    		
    		if lsFROM <> "" then
    			Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    			Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    			oEVTInputFormat.direction = "BW"
    
    			Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    			oTPLOutputFormat.tpl = "c:\script\Tamplates\audit.tpl"
    
    			strQuery = "select TimeGenerated, EventID, " & _
    				"extract_token(Strings, 0, '|' ) as UserSID,  " & _
    				"extract_token(Strings, 6, '|' ) as ObjectName, " & _
    				"extract_token(Strings, 1, '|' ) as User, " & _
    				"extract_token(Strings, 2, '|' ) as Domain, " & _
    				"extract_token(Strings, 5, '|' ) as ObjectType, " & _
    				"extract_token(Strings, 11, '|' ) as ProgramName, " & _
    				"extract_token(Message, 0, '.' ) as Event " & _
    				"into " & lsReport & " " & _
    				"from " & lsFROM & " " & _
    				"where EventId in (4656;4659;4660;4661;4663;4691) " & _
    				"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    				"and User <> 'NT AUTHORITY\SYSTEM' " & _
    				"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
    				"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
    				"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
    				"and User <> 'FILE-SRV1$' " & _
    				"and User <> 'FILE-SRV2$' " & _
    				"order by Timegenerated"
    				
    			fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4
    
    			if not gbDebugModeON then
    				oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    			end if
    		else
    			fuWritedown "* Что-то с аудитом совсем плохо.", 4
    		end if
    		
    		liErrorCode = 1
    	end if
    	
    	fuAudit = liErrorCode
    end function
    
    'Процедура статистики аудита
    function fuAuditStat(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\auditStat.tpl"
    
    		strQuery = "select extract_token(Strings, 1, '|' ) as User, " & _
    			"COUNT(*) as Qty, " & _
    			"MAX(TimeGenerated) as MaxTime  " & _
    			"into " & lsReport & " " & _
    			"from " & lsFROM & " " & _
    			"where EventId in (4656;4659;4660;4661;4663;4691) " & _
    			"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    			"and User <> 'NT AUTHORITY\SYSTEM' " & _
    			"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
    			"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
    			"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
    			"group by User " & _
    			"order by User"
    		
    		fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		lsFROM = fuCollectFileList(lArrServerList, true)
    		
    		if lsFROM <> "" then
    			Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    			Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    			oEVTInputFormat.direction = "BW"
    
    			Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    			oTPLOutputFormat.tpl = "c:\script\Tamplates\auditStat.tpl"
    
    			strQuery = "select extract_token(Strings, 1, '|' ) as User, " & _
    				"COUNT(*) as Qty, " & _
    				"MAX(TimeGenerated) as MaxTime  " & _
    				"into " & lsReport & " " & _
    				"from " & lsFROM & " " & _
    				"where EventId in (4656;4659;4660;4661;4663;4691) " & _
    				"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    				"and User <> 'NT AUTHORITY\SYSTEM' " & _
    				"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
    				"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
    				"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
    				"group by User " & _
    				"order by User"
    			
    			fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4
    
    			if not gbDebugModeON then
    				oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    			end if
    		else
    			fuWritedown "* Что-то со статистикой аудитом совсем плохо.", 4
    		end if
    	
    		liErrorCode = 1
    	end if
    	
    	fuAuditStat = liErrorCode
    end function
    
    'Процедура поиска логинов к RDP
    function fuLogonRdp(lArrServerList, lsReport, lsFunctionName)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonRdp.tpl"
    
    		strQuery = "SELECT DISTINCT resolve_sid(SID) as UserName, eventid, TimeGenerated, extract_token(ComputerName, 0, '.') as NormComputerName, " & _
    			"extract_token(strings, 5, '|' ) as LogonName, " & _
    			"extract_token(strings, 13, '|' ) as LogonWKS, " & _
    			"extract_token(strings, 18, '|' ) as LogonIP, " & _
    			"case extract_token(strings, 8, '|' ) " & _
    			" WHEN '2' THEN 'interactive' " & _
    			" WHEN '3' THEN 'network' " & _
    			" WHEN '4' THEN 'batch' " & _
    			" WHEN '5' THEN 'service' " & _
    			" WHEN '7' THEN 'unlocked workstation' " & _
    			" WHEN '8' THEN 'network logon using a cleartext password' " & _
    			" WHEN '9' THEN 'impersonated logons' " & _
    			" WHEN '10' THEN 'remote access' " & _
    			" ELSE extract_token(strings, 8, '|' ) " & _
    			"end as LogonType, " & _
    			"extract_token(strings, 17, '|' ) as LogonProc, " & _
    			"extract_token(strings, 16, '|' ) as ProcessID " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4624;4625;4648;4675) " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    			"AND LogonType = 'remote access' " & _
    			"order by Timegenerated DESC"
    			
    		fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4
    
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuLogonRdp = liErrorCode
    end function
    
    function fuADObjects(lArrServerList, lsReport)
    	liErrorCode = -1
    	lsFROM = fuCollectFileList(lArrServerList, false)
    	'lsFROM = "\\DC1\c$\WINDOWS\system32\winevt\Logs\Archive-Security-2010-12-09-09-55-23-631.evtx"
    	'lsFROM = "\\DC1\security"
    
    	if lsFROM <> "" then
    		Set oLogQuery = CreateObject("MSUtil.LogQuery")
    
    		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
    		oEVTInputFormat.direction = "BW"
    
    		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
    		oTPLOutputFormat.tpl = "c:\script\Tamplates\adobjects.tpl"
    			
    		strQuery = "select extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, Message as EventCategoryName, " & _
    			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
    			"extract_token(extract_token(Message, 8, ':' ), 1, ' ') as Name " & _
    			"INTO " & lsReport & " " & _
    			"FROM " & lsFROM & " " & _
    			"WHERE EventID IN (4928;4929;4930;4931;4934;4935;4936;4937;4662;5136;5137;" & _
    			"5138;5139;5141;4932;4933) " & _
    			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
    			"AND UserName not like '%%RTCService%%' "
    
    		fuWritedown "* Запрос ADObjects: '" & strQuery & "'", 4
    		
    		if not gbDebugModeON then
    			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
    		end if
    		
    		liErrorCode = 0
    	else
    		liErrorCode = 1
    	end if
    	
    	fuADObjects = liErrorCode
    end function
    
    
    ' Служебные функции
    function fuSendReportMail(lsFileMask, lsFrom, lsTo, lsSubject, lsDate)
    	Set objEmail = CreateObject("CDO.Message")
    	objEmail.From = lsFrom
    	objEmail.To = lsTo
    	objEmail.Subject = lsSubject
    	objEmail.HTMLBody = "<span style='font-family:Tahoma,Arial,sans-serif;font-size:14pt;'>Отчёты за " & _
    		lsDate & "</span>"
    
    	fuCheckfileSizeAndZIP lsDate
    	
    	Set oLogQuery = CreateObject("MSUtil.LogQuery")
    	Set oFormat = CreateObject("MSUtil.LogQuery.FileSystemInputFormat")
    	Set oRecordSet = oLogQuery.Execute("SELECT * FROM " & lsFileMask, oFormat)
    
    	i = 0
    	While Not oRecordSet.atEnd
    	    Set oRecord = oRecordSet.getRecord()
    	    strValue = oRecord.getValue("Path")
    	    objEmail.AddAttachment strValue
    	    i = i + 1
    	    oRecordSet.moveNext
    	Wend
    	oRecordSet.Close
    
    	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver")="MAIL-SRV" 
    	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    	objEmail.Configuration.Fields.Update
    	objEmail.Send
    
    	fuWritedown "* Отчеты отправлены от '" & lsFrom & "' на '" & lsTo & "'. Количество файлов-отчетов: " & i, 4
    end function
    
    function fuCheckErrorCode(liErrorCode, lArrServerList, lsReportfile, lsFunctionName, startTime)
    	select case liErrorCode
    		case -1:  fuWritedown "* " & lsFunctionName & " не выполнилась (функция не отработала корректно)", 4
    		case 0:   fuWritedown "* " & lsFunctionName & " завершена", 4
    				  fuCheckResultFile(lsReportfile)
    		case 1:   fuWritedown "* Для серверов '" & Join(lArrServerList, ",") & "' не найдены архивные папки/файлы, по которым делать отчет (блок FROM пустой). Поиск выполняется по текущим журналам.", 4
    				  fuCheckResultFile(lsReportfile)
    		case else fuWritedown "* Непридвиденная ошибка в " & lsFunctionName & "!", 4
    	end select
    	
    	fuStopTimer(startTime)
    	fuWritedown "", 4
    end function
    
    function fuPing(NetworkDevice)
    	lBoo = false
    	set objPING = GetObject("winmgmts:{impersonationLevel=impersonate}")._
    		ExecQuery ("select * from Win32_PingStatus where address ='" & NetworkDevice & "'")
    
    	For Each PING In objPing
    		if PING.StatusCode = 0 then
    			lBoo = true
    		end if
    	next
    	
    	fuPing = lBoo
    end function
    
    function fuCollectFileList(lArrServerList, lbFindOnServer)
    	' true для поиска в текущих журналах, false для поиска в архивных журналах:
    	' lbFindOnServer = true
    	' lbFindOnServer = false
    	
    	lsTmp = Join(lArrServerList, ",")
    	fuWritedown "* Список компьютеров: " & lsTmp, 4
    	lsList = ""
    	lsListFiles = ""
    	lsTmpPath = ""
    	lbServerHaveArchive = false
    
    	for lix = 0 to UBound(lArrServerList)
    		lsServer = lArrServerList(lix)
    		fuWritedown "* Компьютер '" & lsServer & "'", 4
    		
    		if lbFindOnServer then		
    			if fuPing(lsServer) then
    				fuWritedown "* Есть в сети", 4
    				lsList = lsList & "\\" & lsServer & "\Security"
    				
    					if fuServerHaveArchive(lsServer, lsListFiles) then
    						lbServerHaveArchive = true
    						'lsList = lsList & "," & "\\" & lsServer & "\c$\WINDOWS\system32\config\archive-security-*.evtx"
    						if len(lsListFiles) <> 0 then
    							'lsList = lsList & "," & lsListFiles
    						end if
    					end if
    				
    				if lix < UBound(lArrServerList) then
    					lsList = lsList & ","
    				end if
    			else
    				fuWritedown "* Нет в сети", 4
    			end if
    		else
    			lbServerHaveArchive = false
    			
    			if Len(lsListFiles) = 0 then
    				lsListFiles = fuGetLogFolder(lsServer)
    			else
    				lsTmpPath = fuGetLogFolder(lsServer)
    				if Len(lsTmpPath) <> 0 then
    					lsListFiles = lsListFiles & "," & lsTmpPath
    				end if
    			end if
    		end if
    		
    	next
    
    	if Right(lsList, 2) = ", " then
    		lsList = Left(lsList, Len(lsList)-2)
    	end if
    	
    	'\\dc1\Security, \\dc1\c$\WINDOWS\system32\config\Archive-Security-*.evt, 
    	'\\dc2\Security, \\dc2\c$\WINDOWS\system32\config\Archive-Security-*.evt
    	
    	if lbServerHaveArchive then
    		lsList = lsList & "," & lsListFiles
    	end if
    	
    	if not lbFindOnServer then
    		lsList = lsListFiles
    	end if
    	
    	fuWritedown "* Блок FROM из функции: '" & lsList & "'", 4
    	fuCollectFileList = lsList
    end function
    
    function fuServerHaveArchive(lsServerName, lsListFiles_a)
    	Const FILE_NAME = 0
    
    	dim gbFoo
    	dim gsFilename
    
    	gbFoo = false
    
    	Set objShell = CreateObject ("Shell.Application")
    	Set objFolder = objShell.Namespace ("\\" & lsServerName & "\c$\Windows\System32\winevt\Logs")
    
    	For Each strFileName in objFolder.Items
    		gsFilename = trim(lCase(objFolder.GetDetailsOf (strFileName, FILE_NAME)))
    		' fuWritedown "* gsFilename: " & gsFilename, 1
    	    if ((InStr(gsFilename, "archive-security-")) and (Right(gsFilename, 4) = "evtx")) then
    			fuWritedown "* Архив найден! \\" & lsServerName & "\c$\Windows\System32\winevt\Logs\"&gsFilename, 4
    			if len(lsListFiles_a) = 0 then
    				lsListFiles_a = "f:\Logi_ForADReports\" & gsFilename
    			else 
    				lsListFiles_a = lsListFiles_a & "," & "f:\Logi_ForADReports\" & gsFilename
    			end if
    			fuWritedown "* lsListFiles_a: " & lsListFiles_a, 2
    			'fuConvertEvt2Evtx "\\" & lsServerName & "\c$\WINDOWS\system32\config\" & gsFilename, gsFilename
    			fuCopyEvtx "\\" & lsServerName & "\c$\Windows\System32\winevt\Logs\" & gsFilename, gsFilename
    			gbFoo = true
    		end if
    	Next
    
    	if gbFoo then
    		fuWritedown "* На компьютере '" & lsServerName & "' архивы журналов есть", 4
    	else
    		fuWritedown "* На компьютере '" & lsServerName & "' архивов журналов нет", 4
    	end if 
    	
    	fuServerHaveArchive = gbFoo
    end function
    
    function fuConvertEvt2Evtx(lsFilenamePath, lsFilename)
    	lbTmp = true
    	
    	if (fuIsFileExists("f:\Logi_ForADReports\" & lsFilename) and (fuIsFileExists("f:\Logi_ForADReports\" & lsFilename & "x"))) then
    		fuWritedown "* Конвертация файла " & lsFilename & " не нужна, уже есть сконвертированный", 4
    	else
    		fuWritedown "* Конвертируем файл " & lsFilename & "...", 4
    
    		Set WshShell = CreateObject("WScript.Shell")
    
    		gsRunCmd = "c:\script\convert_evt_to_evtx.bat " & lsFilenamePath & " " & lsFilename
    
    		fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 2
    		WshShell.Run gsRunCmd
    		
    		WScript.Sleep 300000
    	end if
    	
    	fuConvertEvt2Evtx = lbTmp
    end function 
    
    function fuCopyEvtx(lsFilenamePath, lsFilename)
    	lbTmp = true
    
    	if not fuIsFileExists("f:\Logi_ForADReports\" & lsFilename) then
    		Set WshShell = CreateObject("WScript.Shell")
    		
    		gsRunCmd = "c:\script\copy_evtx.bat " & lsFilenamePath & " " & lsFilename
    		fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 4
    		WshShell.Run gsRunCmd
    			
    		WScript.Sleep 25000
    	else
    		fuWritedown "* Архивный журнал " & lsFilename & " копировать не нужно, уже есть скопированный", 4
    	end if
    	
    	fuCopyEvtx = lbTmp
    end function 
    
    function fuDeleteEvtxFiles(lsFromList)
    	fuWritedown "* Удаление временных файлов: " & lsFromList, 4
    	lbTmp = true
    	
    	Set WshShell = CreateObject("WScript.Shell")
    	
    	if InStr(lsFromList, ",") then
    		lArrFrom = Split(lsFromList, ",")
    		
    		for lix = 0 to uBound(lArrFrom)
    			if InStr(lCase(lArrFrom(lix)), "archive-security-") then
    				gsRunCmd = "c:\script\del_evtx.bat " & lArrFrom(lix)
    
    				fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 4
    				WshShell.Run gsRunCmd
    			end if
    		next
    	else
    		gsRunCmd = "c:\script\del_evtx.bat " & lsFromList
    
    		fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 4
    		WshShell.Run gsRunCmd
    	end if
    	
    	WScript.Sleep 60000
    	
    	fuDeleteEvtxFiles = lbTmp
    end function 
    
    
    function fuIsFileExists(lsFilename)
    	lBoo = false
    	
    	Set FSO = CreateObject("Scripting.FileSystemObject")
    	if FSO.FileExists(lsFilename) then
    		' Файл существует
    		lBoo = true
    	else 
    		' Файла не существует
    	end if
    	Set FSO = nothing
    	
    	fuIsFileExists = lBoo
    end function
    
    function fuWritedown(lsToWrite, liCase)
    	Select Case liCase
    		Case 0: ' ничего не делать. Сообщение уходит в никуда.
    		Case 1: WScript.Echo lsToWrite ' сообщение только на экран
    		Case 2: objTextFileWriteLog.WriteLine lsToWrite ' сообщение только в журнал
    		Case 4: WScript.Echo lsToWrite ' сообщение и на экран, и в журнал
    				objTextFileWriteLog.WriteLine lsToWrite
    		Case else WScript.Echo lsToWrite
    	End Select
    end function 
    
    function fuNormalizeSystemDate(lsDate)
    	lsNormalizeDate = lsDate
    	
    	if InStr(lsDate, ".") then
    		lArrDate = Split(lsDate, ".")
    		lsNormalizeDate = lArrDate(2) & "." & lArrDate(1) & "." & lArrDate(0)
    	elseif InStr(lsDate, "/") then
    		lArrDate = Split(lsDate, "/")
    		lsNormalizeDate = fuCheckDatePart(lArrDate(2)) & "." & fuCheckDatePart(lArrDate(0)) & "." & fuCheckDatePart(lArrDate(1))
    	end if
    	
    	fuNormalizeSystemDate = lsNormalizeDate
    end function 
    
    function fuNormalizeDate(lsDate)
    	lsNormalizeDate = lsDate
    	
    	if InStr(lsDate, ".") then
    		lArrDate = Split(lsDate, ".")
    		lsNormalizeDate = lArrDate(2) & "." & lArrDate(1) & "." & lArrDate(0)
    	end if
    	
    	fuNormalizeDate = lsNormalizeDate
    end function 
    
    function fuCheckDatePart(lsDate)
    	lsNormalizeDate = lsDate
    	
    	if len(lsDate) <= 1 then
    		lsNormalizeDate = "0" & lsDate
    	end if
    	
    	fuCheckDatePart = lsNormalizeDate
    end function 
    
    function fuStartTimer(lsFunctionName)
    	fuStartTimer = Now()
    	if lsFunctionName <> "" then
    		fuWritedown VBNewLine & lsFunctionName & " запущена", 4
    	end if
    end function 
    
    function fuStopTimer(startTime)
    	EndTime = Now()
    	timeDiff = CDate(EndTime - startTime)
    	fuWritedown "* Поиск выполнялся: " & timeDiff & " (" & startTime & "/" & EndTime & ").", 4
    end function 
    
    function fuCheckResultFile(lsReportfile)
    	if objFSO.FileExists(lsReportfile) then
    		fuWritedown "* Результат сохранен в файл '" & lsReportfile & "'", 4
    	else 
    		fuWritedown "* Файл результатов '" & lsReportfile & "' не был создан, так как поиск не дал результатов", 4
    	end if
    end function 
    
    function fuTypeTextfile(lsTextfile)
    	'fuWritedown "Распечатать файл помощи '" & lsTextfile & "'", 1
    	Set objTextFileShowHelp = objFSO.OpenTextFile(lsTextfile, 1)
    	Do Until objTextFileShowHelp.AtEndOfStream
    		fuWritedown objTextFileShowHelp.Readline, 1
    	Loop
    	objTextFileShowHelp.Close
    end function 
    
    function fuNeedHelp(lsPar)
    	lbFoo = false
    	if  lsPar = "h" or lsPar = "help" or InStr(lsPar, "?") then
    		lbFoo = true
    	end if
    	fuNeedHelp = lbFoo
    end function 
    
    
    function fuGetFilename(lsDate)
    	lsTmp = "Archive-Security-2013-12-01-*.evtx"
    	
    	if InStr(lsDate, ".") then
    		lArrDate = Split(lsDate, ".")
    		lsTmp = "Archive-Security-" & fuCheckDatePart(lArrDate(2)) & "-" & fuCheckDatePart(lArrDate(1)) & "-" & fuCheckDatePart(lArrDate(0)) & "-*.evtx"
    	elseif InStr(lsDate, "/") then
    		lArrDate = Split(lsDate, "/")
    		lsTmp = "Archive-Security-" & fuCheckDatePart(lArrDate(2)) & "-" & fuCheckDatePart(lArrDate(1)) & "-" & fuCheckDatePart(lArrDate(0)) & "-*.evtx"
    	end if
    	
    	fuGetFilename = lsTmp
    end function 
    
    function fuGetLogFolder(lsServer)
    	lsTmp = ""
    	
    	Select Case lsServer
    		Case "DC1": lsTmp = "Q:\Logi_DC1\"
    		Case "DC2": lsTmp = "Q:\Logi_DC2\"
    		Case "FILE-SRV1": lsTmp = "Q:\Logi_FILE-SRV1\"
    		Case "FILE-SRV2": lsTmp = "Q:\Logi_FILE-SRV2\"
    		Case "EXCH1": lsTmp = "Q:\Logi_EXCH1\"
    		Case "EXCH2": lsTmp = "Q:\Logi_EXCH2\"
    		Case else 
    			fuWritedown "* В скрипте папка с архивами сервера " & lsServer & " не указана. Пытаюсь папку угадать 'Q:\Logi_" & lsServer & "\'", 4
    			lsTmp = "Q:\Logi_" & lsServer & "\"
    	End Select
    	
    	
    	lsPath = Left(lsTmp, Len(lsTmp)-1)
    	'lsPath = lsTmp
    	lsFile = gsLogFilename
    
    	lsBoo = fuNASHaveArchive(lsServer, lsPath, lsFile)
    
    	if lsBoo then
    		lsTmp = lsTmp & gsLogFilename
    	else
    		lsTmp = ""
    	end if
    	
    	fuGetLogFolder = lsTmp
    end function 
    
    
    function fuNASHaveArchive(Server, Path, File)
    	wscript.echo Server & ", " & Path & ", " & File
    
    	Const FILE_NAME = 0
    
    	dim gbFoo
    	dim gsFilename
    
    	gbFoo = false
    	
    	lsF = lCase(Left(File, Len(File)-6))
    
    	Set objShell = CreateObject("Shell.Application")
    	Set objFolder = objShell.Namespace(Path)
    
    	For Each strFileName in objFolder.Items
    		gsFilename = trim(lCase(objFolder.GetDetailsOf (strFileName, FILE_NAME)))
    		' wscript.echo  "* gsFilename: " & gsFilename
    	    if InStr(gsFilename, lsF) then
    			gbFoo = true
    		end if
    	Next
    
    	fuNASHaveArchive = gbFoo
    end function 
    
    function fuCheckfileSizeAndZIP(lsDate)
    	lsReportFolder = "F:\Reports\"
    	lArrReportfilesList = Array (_
    		lsReportFolder & "logged_Administrator_" & lsDate & ".html", _
    		lsReportFolder & "new_AD_" & lsDate & ".html", _
    		lsReportFolder & "logonFailuresStats_" & lsDate & ".html", _
    		lsReportFolder & "group_Manage_" & lsDate & ".html", _
    		lsReportFolder & "logonFailure_" & lsDate & ".html", _
    		lsReportFolder & "change_password_" & lsDate & ".html", _
    		lsReportFolder & "new_Comp_AD_" & lsDate & ".html", _
    		lsReportFolder & "audit_" & lsDate & ".html", _
    		lsReportFolder & "auditStat_" & lsDate & ".html", _
    		lsReportFolder & "logged_Rdp_" & lsDate & ".html", _
    		gsReportFolder & "AD_objects_" & gsNormalDate & ".html")
    
    	for lix = 0 to UBound(lArrReportfilesList)
    		lbTmp = false
    		
    		lsFilenamePath = lArrReportfilesList(lix)
    		ArcName = Left(lsFilenamePath, Len(lsFilenamePath)-5) & ".zip"
    		
    		if fuIsFileExists(lsFilenamePath) then
    			Set File = objFSO.GetFile(lsFilenamePath)
    
    			 lsFilenameSize =  File.Size
    			 if lsFilenameSize > 3000000 then
    				fuWritedown "* Размер файла '" & lsFilenamePath & "' больше 3 МБ (размер " & lsFilenameSize & " байт), необходимо его заархивировать", 4
    				fuWritedown "* Идет архивация...", 1
    				'--[ Архивирование отчета ]-------------------------------------------------------------------
    				Set Shell=CreateObject("WScript.Shell")
    				Set Zip=Shell.Exec("C:\Program Files\7-Zip\7z.exe a " & ArcName & " " & lsFilenamePath)
    
    				'Необходимо ожидание, пока архивирование не закончится
    				While (Zip.Status = 0)
    					WScript.Sleep 5000
    				Wend
    
    				Set Shell = Nothing
    
    				fuWritedown "* Архивирование завершено! Имя архива '" & ArcName & "'", 4
    				fuWritedown "* Удаляем файл отчета '" & lsFilenamePath & "'...", 4
    				objFSO.DeleteFile lsFilenamePath, true
    				fuWritedown "* Удаление завершено!", 1
    				lbTmp = true
    				'WScript.Sleep 2000
    				'---------------------------------------------------------------------------------------------
    			 end if
    		else 
    			' файла не существует, ничего не делаем.
    		end if
    	next
    	
    	fuCheckfileSizeAndZIP = lbTmp
    end function 
    


    Вспомагательные батники.
    convert_evt_to_evtx.bat
    copy %1 f:\Logi_ForADReports\%2
    wevtutil epl f:\Logi_ForADReports\%2 f:\Logi_ForADReports\%2x /lf:true
    

    copy_evtx.bat
    copy %1 f:\Logi_ForADReports\%2
    

    del_evtx.bat
    del %1
    


    Скрипт может запускаться без каких-либо ключей. В этом случае создаются все одиннадцать отчетов.
    Скрипт может иметь три ключа.
    Logparser_4.bat [список_отчетов] [адрес_электронной_почты] [дата]
    

    [список_отчетов] — необязательный ключ. Перечень отчетов, которые надо выполнить. Указывается в формате: «1,1,0,0,1,0,1,0,1,0,0», то есть единица указывает, что отчет надо выполнить.
    Можно сделать все отчеты, указав ключ all.
    Выключает создание всех отчетов ключ nothing.

    [адрес_электронной_почты] — необязательный ключ. Может принимать значения:
    y — отправить отчеты на адрес по умолчанию (admin1@domain.com)
    n — не отправлять отчеты на электронный ящик, а только сложить в папку отчетов f:\Reports.
    адрес_электронной_почты — Указывает адрес электронной почты, на который будут отосланы отчеты.

    [дата] — необязательный ключ. Указывает дату для работы с отчетами. Этот параметр указывается только тогда, когда надо отправить уже сделанные отчеты за дату в прошлом (Отчеты всегда выполняются за предыдущий день от даты запуска скрипта). Формат даты: YYYY.MM.DD

    Отчеты:
    1. Отчет поиска логинов администраторов
    2. Отчет AccauntManage
    3. Отчет создания статистики неудачных логинов
    4. Отчет управления группами
    5. Отчет поиска неудачных логинов
    6. Отчет управления паролями
    7. Отчет управления компьютерами
    8. Отчет аудита по папке Top-Secret-Documents
    9. Отчет статистики аудита по папке Top-Secret-Documents
    10. Отчет поиска логинов к RDP
    11. Отчет слежения за действиями над объектами в AD

    Примеры.

    Logparser_4.bat nothing "admin2@domain.com" 2013.01.01
    
    Все уже сделанные отчеты за 1 января 2013 г. отсылает на «admin2@domain.com» (заново отчеты не выполняются).

    Logparser_4.bat nothing y 2013.02.18
    
    Все уже сделанные отчеты за 18 февраля 2013 г. отсылает на адрес по умолчанию (заново отчеты не выполняются).

    Logparser_4.bat all "admin3@domain.com"
    
    Создает все отчеты и отсылает на «admin3@domain.com».

    Logparser_4.bat "1,0,0,0,0,0,0,0,0,1,0"
    
    Создает только первый и последний отчёты и отсылает на адрес по умолчанию.

    Logparser_4.bat "0,1,0,0,0,0,0,0,0,0,0" n
    
    Создает только второй отчет, но никуда не отсылает, а складывет в папку f:\Reports.

    Logparser_4.bat /?
    
    Показывает помощь.

    Примечание.
    Сейчас скрипт ищет события в архивных журналах безопасности (те, что Archive-Security-*.evt) в централизованном хранилище. В скрипте это диск Q, подключенный в начале в батнике
    net use Q: \\nas-srv\BACKUP
    Но может искать в оперативных и архивных журналах на серверах. Для этого нужно в каждой их 11 функций изменить
    lsFROM = fuCollectFileList(lArrServerList, false)
    на
    lsFROM = fuCollectFileList(lArrServerList, true)

    Тут можно скачать архив со скриптом, батниками и шаблонами.
    В принципе, не обязательно использовать скрипт дословно. Главное понять принцип, как логпарсер ищет события и выгружает их в html-файл, используя шаблон. И пользоваться.
    Метки:
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 15
    • 0
      Способ интересный.
      Но есть один минус, названия созданных/удаленных/измененных групп не отображаются. Только SID.
      Если группу удалить, то мы никогда названия не узнаем:)
      Такую же вещь можно сделать встроенными средствами: например powershell. Только плюсом к этому вместо SID в именах группы писать диспейл_нейм группы (отдельным запросом доставать имя группы по SID).
      1 скрипт, может запускаться раз в 1/2/3.../24 часа или как угодно, парсить лог, доставать инфу, писать в базу к примеру, а там уже как хотите работаете с этой базой.
      Вот пример. Правда не в базу. Но прикрутить запрос к базе несложно.
      • 0
        названия созданных/удаленных/измененных групп не отображаются
        Отображаются. Только в поле «Имя пользователя». Микрософт в разных событиях в одни и те же поля вносит разную информацию. Поэтому надо было бы назвать поля моего отчёта более абстрактно, конечно.
      • +1
        А за документик с событиями — отдельное спасибо!
        • 0
          А вы события входа в домен пользователей не собираете?
          К примеру чтобы знать где какой пользователь и в какое время залогинился?
          • 0
            Это мы у SCCM’а спрашиваем.
            • +1
              Это можно делать анализируя события керберос, точнее запрос тикетов (4769 событие).
              На каждом КД анализировать лог на предмет этого события, и он со 100% вероятностью скажет кто залогинился, на какой машине и в какое время.
              Тем самым кстати можно посмотреть как делятся кеорберос-запросы между КД в домене.
              • 0
                IMHO, SCCM-ом хорошо потом анализировать, кто где «пасся», а «где именно сейчас этот бездельник», как Вы справедливо написали, через запрос событий керберос, и то с некоторой вероятностью, и если аудит успеха для Audit account logon events включен.

                Почему не 100%? Потому, что в случае если пользователь залогинился в отключенной от сети станции, то авторизация его шла по тикету на локальном хранилище. А после восстановления сетевого соединения не факт, что ему сразу резко потребуются какие-то сетевые ресурсы. Он может и локально долго некоторое время работать, хотя машинка уже в сети авторизуется.
                • 0
                  Согласен, не 100%, не учел авторизацию без сетевого подключения к домену.
                • 0
                  А я на контроллере домена ищу событие «4624, An account was successfully logged on».
                  На каждом КД анализировать лог на предмет этого события
                  Просмотр логов 2-х КД для 3 тсч. пользователей гораздо дольше, чем глянуть в отчёте SCCM’а.
            • 0
              Спасибо, очень интересно
              • 0
                И вручную коррелировать тонны строчек?)

                Это очень круто, что можно аккуратно собрать такое на коленке, но ведь есть очень удобные готовые решения, вроде GFI EventsManager (обращайтесь за ключиками, если вдруг), например, которые и централизуют, и выполняют резервное копирование, и фильтрацию, и автоматическую отчетность с графиками по любым срезам, попутно проверяя параметры систем, что выйдет дешевле уже исходя из пересчета на человеко-часы, а также исключит человеческий фактор.

                + как Вы будете это в отчет для ИБ включать?
                • 0
                  За наводку на программу спасибо.
                  + как Вы будете это в отчет для ИБ включать?
                  Если я правильно понял вопрос, это уже и есть готовый отчёт для ИБ.
                  • +1
                    За «GFI EventsManager» редакция «Active Monitoring» вот такая сумма у меня вышла 3 312 000 р. :-) Тогда уж лучше какое-нибудь DLP-решение купить.
                  • 0
                    Не используйте Logparser на =<2008 серверах, используйте штатный wevtutil. Выгружает события намного быстрее.
                    • 0
                      Я запрос к Logparser’у делаю прямо в VBScript’е и результат выгружаю в html-файл.
                      Set oLogQuery = CreateObject("MSUtil.LogQuery")
                      
                      Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
                      oEVTInputFormat.direction = "BW"
                      
                      Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
                      oTPLOutputFormat.tpl = "c:\script\Tamplates\logonFailuresStats.tpl"
                      
                      strQuery = "SELECT TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS User, " & _
                      	"COUNT(*) AS Total " & _
                      "INTO " & lsReport & " " & _
                      "FROM " & lsFROM & " " & _
                      "WHERE EventID IN (4625) " & _
                      "AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
                      "GROUP BY User " & _
                      "ORDER BY Total DESC"
                          
                      oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
                      
                      И в скрипте же отсылаю результат.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.