Отчёт о запуске программ на компьютерах пользователей

в 7:21, , рубрики: windows, запуск приложений, информационная безопасность, отчёт, системное администрирование, метки: ,

Всем привет!
Отчёт о том, что запускает пользователь на своём компьютере, крайне важен. С многих точек зрения. Особенно с точки зрения информационной безопасности.
Информация о запуске программ на компьютерах пользователей храниться в журнале безопасности. Конечно, рассматривается среда Windows. В Инете готового решения не нашёл, поэтому сделал свою реализацию.
Скрипт запускается на сервере. На выходе имеем набор файлов с отчётами о запуске программ.
Картинка для привлечения внимания:
Отчёт о запуске программ на компьютерах пользователей

Основная идея такая. Текущие события журнала безопасности сохраняются в evt-файле на клиентском компьютере. Файл копируется на сервер, где информация из него загружается на SQL Server. Затем SQL-запросом формируется отчёт и сохраняется в файл.
Теперь, как это реализовано.
Необходимо создать папки Log, Logs, CheckComps, Logi_ForReports и Computer. У меня папки на диске F. В папке Log создать файл list.txt со списком компьютеров, которые необходимо проверить. Каждое имя компьютера с новой строки. Я создал 2 файла list.txt и list7.txt для XP и семёрок соответственно. В папке Computer создать файл is_computer_online_listComps.vbs

Содержимое файла is_computer_online_listComps.vbs:

on error resume next

dim gsFileName
dim gsRunCmd
dim gix
dim giy
dim giz

if Wscript.Arguments.Count = 1 then
	gsFileName = Wscript.Arguments(0)
	gsOS = "XP"
elseif Wscript.Arguments.Count = 2 then
	gsFileName = Wscript.Arguments(0)
	gsOS = Wscript.Arguments(1)
else
	gsFileName = InputBox("Файл со списком компьютеров", "Ввод", "F:Loglist.txt")
	gsOS = InputBox("Тип операционной системы:" & VBNewLine & "'XP' - для Windows 2000/XP" & VBNewLine & "'7' - для Windows 7", "Ввод", "XP")
end if

gsOS = uCase(gsOS)
wscript.echo "gsOS: " & gsOS
if inStr(gsOS, "XP") = 0 and inStr(gsOS, "7") = 0 then
	MsgBox "Некорректно указан тип операционной системы!", vbInformation, "Внимание"
	Wscript.Quit
end if

WScript.Echo "Файл со списком компьютеров: " & gsFileName

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)

gix = 0
giy = 0

Set WshShell = CreateObject("WScript.Shell")

do until objTextFileOpen.AtEndOfStream
	gsComputerName = objTextFileOpen.Readline
	giy = giy + 1
loop 

objTextFileOpen.Close

wscript.echo "Найдено компьютеров: " & giy

Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)

do until objTextFileOpen.AtEndOfStream
	gsComputerName = objTextFileOpen.Readline
	gix = gix + 1
	giz = gix * 100
	giz = giz / giy
	giz = Round(giz, 1)
	giOst = giy - gix
	
	if fuPing(gsComputerName) then
		wscript.echo gsComputerName & VBTab & " осталось: " & giOst & ", готово: " & giz & "%"
		
		if inStr(gsOS, "XP") then
			gsRunCmd = "f:Computeris_computer_online.bat " & gsComputerName & " y"
		elseif inStr(gsOS, "7") then
			gsRunCmd = "f:Computeris_computer_online7.bat " & gsComputerName & " y"
		end if
		WshShell.Run gsRunCmd
		if giOst <> 0 then
			WScript.Sleep 180000 ' Внимание! Вот это задержка в 180 секунд между компьютерами.
		end if
	else
		wscript.echo gsComputerName & VBTab & " осталось: " & giOst & ", готово: " & giz & "%. Выключен."
	end if
loop 

objTextFileOpen.Close

WScript.Echo "Операция завершена!"

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
			'WScript.Echo "* Компьютер " & NetworkDevice & " в сети!" 
			lBoo = true
		else
			'WScript.Echo "* Компьютера нет в сети."
		end if
	next
	
	fuPing = lBoo
end function 

Запускается процедура проверки bat-файлом. Ссылку на него можно сделать, например, на рабочем столе.

bat-файл

cscript //nologo "f:Computeris_computer_online_listComps.vbs" %1 %2

Основной скрипт is_computer_online_listComps.vbs читает список компьютеров из текстового файла и для каждого запускает bat-файл формирования отчёта. Для XP — это файл is_computer_online.bat, для 7 — is_computer_online7.bat.
Примечание.
На сервере нужно установить logparser.
Всё описанное должно заработать и на компьютере администратора. Только надо установить Microsoft SQL SERVER 2008 NATIVE CLIENT и Microsoft SQL Server 2008 Command Line Utilities. Но я не проверял.

Блок работы с компьютерами XP

Bat-файл:

is_computer_online.bat

cscript //nologo "f:Computeris_computer_online.vbs" %1 %2

Bat-файл запускает скрипт. Скрипт выполняет сохранение событий журнала безопасности в evt-файл и запускает основной батник mo2csv.bat.

is_computer_online.vbs
on error resume next

dim gsComputerName
dim gsUseLogFile
dim gsLogFilename
dim gbFlag

dim gsTableName
dim gsCompName
dim gsRunCmd

if Wscript.Arguments.Count = 1 then
	gsComputerName = Wscript.Arguments(0)
	gsUseLogFile = "n"
elseif Wscript.Arguments.Count = 2 then
	gsComputerName = Wscript.Arguments(0)
	gsUseLogFile = Wscript.Arguments(1)
else
	gsComputerName = InputBox("Имя компьютера", "Введите", "")
	gsUseLogFile = InputBox("Использовать log-файл для проверки?" & VBNewline & "[y/n]", "Введите", "y")
end if

WScript.Echo "* Имя компьютера " & gsComputerName
gsLogFilename = "f:Log" & gsComputerName & ".log"

if lCase(gsUseLogFile) = "y" then
	gbFlag = false
	WScript.Echo "* Файл журнала " & gsLogFilename
	set objFSO = CreateObject("Scripting.FileSystemObject")

	if not objFSO.FileExists(gsLogFilename) then
		WScript.Echo "* Файла журнала нет. Создается..."
		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 8, True)
		objTextFileWriteLog.writeLine "n"
		objTextFileWriteLog.close
		WScript.Echo "* Создан успешно."
	end if

	set objTextFileOpen = objFSO.OpenTextFile(gsLogFilename, 1)
	do until objTextFileOpen.AtEndOfStream
		record = trim(objTextFileOpen.Readline)
		if record = "n" then
			WScript.Echo "* Компьютер не проверялся ранее."
			if fuPing(gsComputerName) then
				gbFlag = true
				
				if fuBackup(gsComputerName) then
					WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
					fuUploadEvents gsComputerName
					wscript.sleep 10000
				end if
			end if
		elseif record = "y" then
			WScript.Echo "* Информация с компьютера " & gsComputerName & " уже закачана на сервер."
		else 
			WScript.Echo "* Некорректная информация о компьютере " & gsComputerName & " в log-файле."
		end if
	loop

	objTextFileOpen.close

	if gbFlag then
		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 2, True)
		objTextFileWriteLog.writeLine "y"
		objTextFileWriteLog.close
		WScript.Echo "* Информация записана в журнал."
	end if
else
	'if fuPing(gsComputerName) then
		if fuBackup(gsComputerName) then
			WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
			fuUploadEvents gsComputerName
			wscript.sleep 10000
		end if
	'end if
end if

wscript.sleep 1000

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
			WScript.Echo "* Компьютер " & NetworkDevice & " в сети!" 
			lBoo = true
		else
			WScript.Echo "* Компьютера нет в сети."
		end if
	next
	
	fuPing = lBoo
end function 

function fuBackup(lsComputername)
	lsEvtBackupFilename = "c:" & lsComputername & ".evt"
	lsEvtBackupFilenameRemote = "\" & lsComputername & "c$" & lsComputername & ".evt"
	lbFlag = false
	
	set lObjFSO = CreateObject("Scripting.FileSystemObject")
	
	if lObjFSO.FileExists(lsEvtBackupFilenameRemote) then
		WScript.Echo "* Файл журнала уже есть. Используем существующий..."
		lbFlag = true
	else
		Wscript.Echo "* Выполняется резервное копирование..."
		Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate,(Backup)}!\" & lsComputername & "rootcimv2")
		Set colLogFiles = objWMIService.ExecQuery ("Select * from Win32_NTEventLogFile where LogFileName='Security'")

		For Each objLogfile in colLogFiles
			errBackupLog = objLogFile.BackupEventLog(lsEvtBackupFilename)
			If errBackupLog = 0 Then        
				Wscript.Echo "* Резервное копирование выполнено успешно."
				lbFlag = true
			Else
				Wscript.Echo "* Резервное копирование не выполнено."
			End If
		Next
	end if
	
	fuBackup = lbFlag
end function 

function fuUploadEvents(lsComputername)
	WScript.Echo "* Запущена закачка на сервер..."
	gsCompName = lCase(lsComputername)

	gsTableName = fuGetTableName(gsCompName)
	gsTableName = uCase(gsTableName)
	gsOutputFilename = "f:Computer" & gsCompName & ".csv"
	gsOutputFilenameSQL = "f:Computer" & gsCompName & "_sql.csv"

	Set WshShell = CreateObject("WScript.Shell")
	gsRunCmd = "f:Computermo2csv.bat " & gsCompName & " " & gsOutputFilename & " " & gsOutputFilenameSQL & " " & gsTableName

	WScript.Echo "* Выполняется команда: '" & gsRunCmd & "'"
	WshShell.Run gsRunCmd
end function 

function fuGetTableName(lsCompName)
	lsTmp = lsCompName
	
	if InStr(lsTmp, "-") then
		lsTmp = Replace(lsTmp, "-", "_")
	end if
	
	fuGetTableName = lsTmp
end function 

mo2csv.bat делает следующее:

  • Забирает evt-файл с удалённого компьютера на сервер.
  • Преобразовывает evt-файл в evtx.
  • Выгружает только события запуска/остановки программ из evtx-файла в текстовый файл csv.
  • Информацию из текстового файла закачивает на SQL Server.
  • Бекапит оригинальный evt-файл в папку Logi_ForReports (вдруг пользователь сотрёт свой журнал, а у нас копия есть).
  • Удаляет временный evtx-файл.
  • Формирует и выполняет sql-запрос к SQL Server’у.
  • Удаляет временные файлы (в случае отладки или для изучения работы скрипта, этот раздел можно закомментировать).
  • Перемещает отчёты в папку CheckComps.

mo2csv.bat

@echo off

@set WDate=%date:~-10%

@echo * Журнал безопасности перемещается с удаленного компьютера %1 (Windows XP)...
move \%1c$%1.evt f:Logs
@echo * Перемещение завершено.

@echo * Выполняется конвертация evt журнала в evtx...
wevtutil epl f:Logs%1.evt f:Logs%1.evtx /lf:true
@echo * Конвертация завершена.

@echo * Информация из журнала безопасности выгружается в текстовый файл. Источник: f:Logs%1.evtx, назначение: %2
LogParser.exe file:"f:Computerget_info_from_log.sql"?source=f:Logs%1.evtx+output_file=%2 -i:EVT -o:TSV -headers:ON -oSeparator:tab -oTsFormat:"dd.MM.yyyy hh:mm:ss" -fileMode:1
@echo * Выгрузка завершена.

@echo * Исправляю текстовый файл %2. Получаю %3...
cscript F:Computerupdate_csvFile_forSQLCheck.vbs %2 %3 //NoLogo
@echo * Исправление завершено.


@echo * Информация из текстового файла закачивается на SQL Server. Источник: %3, таблица %4...
LogParser.exe file:"f:Computerget_info_from_log_2SQL.sql"?source=%3+output_file=%4 -i:TSV -headerRow:ON -iSeparator:tab -iTsFormat:"dd.MM.yyyy hh:mm:ss" -o:SQL -server:"SQL-SRVSEC" -database:quickly -driver:"SQL Server" -createTable:ON
@echo * Процесс завершен.


@echo * Журнал безопасности перемещается в архив...
move f:Logs%1.evt f:Logi_ForReports%1_%WDate%_sec.evt
@echo * Перемещение завершено. Имя архивного файла 'f:Logi_ForReports%1_%WDate%_sec.evt'

@echo * Удаление временного evtx журнала...
del f:Logs%1.evtx
@echo * Удаление завершено.


@echo * Создание sql-запроса...
cscript "F:Computercreate_SQL_full.vbs" %1 1 //nologo
@echo * Создание завершено.

@echo * Выполнение sql-запроса...
SQLCMD.EXE -S SQL-SRVSEC -d quickly -E -i f:Computer%1-1.sql -o "f:Computer%1. Запуск программ.csv" -W -R -s ";" -w 4000
@echo * Выполнение завершено.

@echo * Исправляю результирующие файлы отчетов...
cscript F:Computerupdate_result_file.vbs "f:Computer%1. Запуск программ.csv" //nologo
@echo * Исправление завершено.

@echo * Удаление временных файлов...
del f:Computer%1-1.sql
del %2
del %3
del f:Computer%1_dbg.txt
del "f:Computer%1. Запуск программ.csv"
@echo * Удаление завершено.

@echo * Перемещение файлов-отчетов...
move "f:Computer%1. Запуск программ.xls" "f:CheckComps%1. Запуск программ.xls"
@echo * Перемещение завершено.

@echo on

Примечание
Возможно, в батнике нужно будет SQLCMD.EXE заменить на «c:Program FilesMicrosoft SQL Server100ToolsBinnSQLCMD.EXE», а LogParser.exe на «c:Program Files (x86)Log Parser 2.2LogParser.exe» (или «c:Program FilesLog Parser 2.2LogParser.exe»).
Имя сервера с SQL Server’ом SQL-SRV, имя экземпляра SEC и имя базы quickly. Заменить на свои.

get_info_from_log.sql

SELECT RecordNumber as id, 
	eventid as eId, 
	TimeGenerated as Tg, 
	resolve_sid(sid) as UserName, 
	computername as Computer, 
	EXTRACT_TOKEN(Strings, 0, '|') as image_unique_id,
	EXTRACT_TOKEN(Strings, 1, '|') as image
into %output_file% 
FROM %source%
where ((EventID in (592; 593))
and (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITYNETWORK SERVICE')
and (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITYSYSTEM'))
and TimeGenerated >= TO_TIMESTAMP('01.03.2011 00:00:00','dd.MM.yyyy hh:mm:ss')
order by recordnumber asc

get_info_from_log_2SQL.sql

SELECT *
into %output_file% 
FROM %source%

update_csvFile_forSQLCheck.vbs

On Error Resume Next

dim gsSimbolSplitFields
dim sgSimbolSplitAdmin
dim gbInsideBlock
dim gIx
dim gbDebug
dim gbWriteString

Dim gArrBlock_admin

gsSimbolSplitFields = vbTab
sgSimbolSplitAdmin = ";"
gbInsideBlock = false
gbIERuning = false
gbIE = false
giBlockPlus = 0
giIEPlus = 0
gsDateBlock = "01.01.2011 00:00:00"
TgBlockStop = "01.01.2011 00:00:00"
idBlockStop = ""
gIx = 0
gArrBlock_admin = Array (sgSimbolSplitAdmin & sgSimbolSplitAdmin, _
	sgSimbolSplitAdmin & sgSimbolSplitAdmin, _
	sgSimbolSplitAdmin & sgSimbolSplitAdmin, _
	sgSimbolSplitAdmin & sgSimbolSplitAdmin)
gbDebug = true
'gbDebug = false

if Wscript.Arguments.Count = 1 then
	sgFilename = Wscript.Arguments(0)
	sgFilenameOut = fuRemoveExtention(sgFilename) & "_sql.csv"
	gsLogFilename = fuRemoveExtention(sgFilename) & "_dbg.txt"
elseif Wscript.Arguments.Count = 2 then
	sgFilename = Wscript.Arguments(0)
	sgFilenameOut = Wscript.Arguments(1)
	gsLogFilename = fuRemoveExtention(sgFilename) & "_dbg.txt"
elseif Wscript.Arguments.Count = 3 then
	sgFilename = Wscript.Arguments(0)
	sgFilenameOut = Wscript.Arguments(1)
	gsLogFilename = Wscript.Arguments(2)
else
	sgFilename = InputBox("Имя исходного файла", "Введите", "f:comp-6475.csv")
	sgFilenameOut = InputBox("Имя результирующего файла", "Введите", fuRemoveExtention(sgFilename) & "_sql.csv")
	gsLogFilename = InputBox("Имя файла журнала", "Введите", fuRemoveExtention(sgFilename) & "_dbg.txt")
end if

Set objFSO = CreateObject("Scripting.FileSystemObject")

if not objFSO.FileExists(sgFilename) then
	wscript.echo "Исходного файла для обновления нет, выхожу!"
	Wscript.Quit
end if

Set objTextFileOpen = objFSO.OpenTextFile(sgFilename, 1)
Set objTextFileWrite = objFSO.CreateTextFile(sgFilenameOut, True)

if gbDebug then
	if not objFSO.FileExists(gsLogFilename) then
		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 8, True)
	else
		set objTextFileWriteLog = objFSO.CreateTextFile(gsLogFilename, True)
	end if
end if
	
Do Until objTextFileOpen.AtEndOfStream
	record = trim(objTextFileOpen.Readline)
	gIx = gIx + 1
	gbWriteString = true
	fuPrint gIx & ". '" & record & "'"
	
	if InStr(record, gsSimbolSplitFields) then
		arr = Split(record, gsSimbolSplitFields)
		id = arr(0)
		eId = arr(1)
		Tg = arr(2)
		UserName = arr(3)
		Computer = arr(4)
		image_unique_id = arr(5)
		image = arr(6)
		
		if InStr(lCase(image), "explorer.exe") then
			if eId = "592" then
				gbBlockBegin = true
				gbBlockEnd = false
				giBlockPlus = giBlockPlus + 1
				fuPrint "explorer.exe старт"
			else 
				gbBlockBegin = false
				gbBlockEnd = true
				giBlockPlus = giBlockPlus - 1
				if giBlockPlus < 0 then
					giBlockPlus = 0
				end if
				fuPrint	"explorer.exe стоп"
			end if
		else
			gbBlockBegin = false
			gbBlockEnd = false
		end if
		
		if InStr(lCase(image), "iexplore.exe") then
			gbIE = true
			fuPrint	"Строка с iexplore.exe"
			
			if eId = "592" then
				fuPrint	"iexplore.exe старт"
				giIEPlus = giIEPlus + 1
				gbIERuning = true
				
				if giIEPlus = 1 then
					image_unique_idIEStart = image_unique_id
				end if
			else 
				fuPrint	"iexplore.exe стоп"
				giIEPlus = giIEPlus - 1
				gbIERuning = false
			end if
		else 
			gbIE = false
			fuPrint	"Строка без iexplore.exe"
		end if
		
		if gIx = 1 then
			objTextFileWrite.WriteLine record & gsSimbolSplitFields & "CompStart"
			fuPrint "Первая строка, записываем"
		elseif gIx = 2 then
			fuPrint "вторая строка"
			if gbBlockBegin then
				fuPrint "Начало блока, записываем"
				gbInsideBlock = true
				gsDateBlock = Tg
				gsUserNameBlockStart = UserName
				image_unique_idBlockStart = image_unique_id
				objTextFileWrite.WriteLine record & gsSimbolSplitFields & gsDateBlock
			end if
			
			idPrev = id
			eIdPrev = eId
			TgPrev = Tg
			UserNamePrev = UserName
			ComputerPrev = Computer
			image_unique_idPrev = image_unique_id
			imagePrev = image	
		else
			'fuPrint "остальные строки"
			
			'-- Запуск explorer.exe
			if gbBlockBegin then
				fuPrint "Запуск explorer.exe (экземпляр № " & giBlockPlus & ")"
				
				if giBlockPlus = 1 then
					giDiff = DateDiff("s", CDate(TgBlockStop), CDate(Tg))
					if giDiff > 9 then
						if Len(idBlockStop) > 0 then
							fuPrint "Это не перезапуск explorer.exe. Записываем остановку предыдущего блока"
							record_convert_prev = idBlockStop & gsSimbolSplitFields & _
									eIdBlockStop & gsSimbolSplitFields & _
									TgBlockStop & gsSimbolSplitFields & _
									UserNameBlockStop & gsSimbolSplitFields & _
									ComputerBlockStop & gsSimbolSplitFields & _
									image_unique_idBlockStart & gsSimbolSplitFields & _
									imageBlockStop & gsSimbolSplitFields & _
									gsDateBlock
							fuPrint record_convert_prev
							objTextFileWrite.WriteLine record_convert_prev
						end if
						
						gsDateBlock = Tg
						fuPrint "Новая дата блока: '" & gsDateBlock & "'"
						image_unique_idBlockStart = image_unique_id
						fuPrint "Новый код блока: '" & image_unique_idBlockStart & "'"
						gsUserNameBlockStart = UserName
						fuPrint "Новый пользователь блока: '" & gsUserNameBlockStart & "'"
					else
						fuPrint "Это перезапуск explorer.exe! Не записываем остановку предыдущего блока и не записываем запуск этого."
						gbWriteString = false
					end if
					
					gbInsideBlock = true
				else
					if lCase(gsUserNameBlockStart) = lCase(UserName) then
						fuPrint "gsUserNameBlockStart: '" & gsUserNameBlockStart & "', UserName: '" & UserName & "'"
						fuPrint "Начало блока. Возможно, компьютер был аварийно выключен. Необходимо записать окончание предыдущего блока и сохранить новые параметры блока"
						record_convert_prev = "999" & gsSimbolSplitFields & _
								"593" & gsSimbolSplitFields & _
								Tg & gsSimbolSplitFields & _
								UserName & gsSimbolSplitFields & _
								Computer & gsSimbolSplitFields & _
								image_unique_idBlockStart & gsSimbolSplitFields & _
								"C:WINDOWSexplorer.exe" & gsSimbolSplitFields & _
								gsDateBlock
						fuPrint record_convert_prev
						objTextFileWrite.WriteLine record_convert_prev
						
						giBlockPlus = 1
						
						gsDateBlock = Tg
						fuPrint "Новая дата блока: '" & gsDateBlock & "'"
						image_unique_idBlockStart = image_unique_id
						fuPrint "Новый код блока: '" & image_unique_idBlockStart & "'"
					else
						'fuPrint "gsUserNameBlockStart: '" & gsUserNameBlockStart & "', UserName: '" & UserName & "'"
						fuPrint "Не начало блока. Возможно, администратор запустил explorer. Записать текущую строку и сохранить параметры"
						gArrBlock_admin(giBlockPlus-2) = image_unique_id & sgSimbolSplitAdmin & UserName & sgSimbolSplitAdmin & Tg
						fuPrint gArrBlock_admin(giBlockPlus-2)
						'gsDateBlock_admin = Tg
						objTextFileWrite.WriteLine record & gsSimbolSplitFields & Tg
						gbWriteString = false
					end if
				end if
			end if
			
			'-- Остановка explorer.exe
			if gbBlockEnd then
				fuPrint "Остановлен explorer.exe (осталось экземпляров " & giBlockPlus & ")"
				
				if giBlockPlus = 0 then
					fuPrint "Остановлен последний экземпляр, сохраняем его значения"
					
					idBlockStop = id
					eIdBlockStop = eId
					TgBlockStop = Tg
					UserNameBlockStop = UserName
					ComputerBlockStop = Computer
					image_unique_idBlockStop = image_unique_id
					imageBlockStop = image
					
					gbInsideBlock = false
					giIEPlus = 0 ' <-- Добавил для обнуления количества копий IE
				else
					fuPrint "Остановлен не последний экземпляр, его значения не сохраняем, только записываем текущую строку"		
					for giY = 0 to UBound(gArrBlock_admin)
						arrA = Split(gArrBlock_admin(giY), sgSimbolSplitAdmin)
						gsImage_unique_id_A = arrA(0)
						gsUserName_A = arrA(1)
						gsTg_A = arrA(2)
						
						if gsImage_unique_id_A = image_unique_id then
							gsDateBlock_admin = gsTg_A
						end if
					next
					objTextFileWrite.WriteLine record & gsSimbolSplitFields & gsDateBlock_admin
					gbWriteString = false
				end if
			end if
			
			'-- Записать текущую строку 
			if gbInsideBlock then
				if gbIE then
					if (((gbIERuning) and (giIEPlus = 1)) or ((not gbIERuning) and (giIEPlus = 0))) then
						fuPrint "Записать IE строку"
						record_convert_prev = id & gsSimbolSplitFields & _
							eId & gsSimbolSplitFields & _
							Tg & gsSimbolSplitFields & _
							UserName & gsSimbolSplitFields & _
							Computer & gsSimbolSplitFields & _
							image_unique_idIEStart & gsSimbolSplitFields & _
							image & gsSimbolSplitFields & _
							gsDateBlock
						fuPrint record_convert_prev
						objTextFileWrite.WriteLine record_convert_prev
					else
						fuPrint "Вот хрень с IE! gbIERuning: " & gbIERuning & ", giIEPlus: " & giIEPlus
						record_convert_prev = id & gsSimbolSplitFields & _
							eId & gsSimbolSplitFields & _
							Tg & gsSimbolSplitFields & _
							UserName & gsSimbolSplitFields & _
							Computer & gsSimbolSplitFields & _
							image_unique_idIEStart & gsSimbolSplitFields & _
							image & gsSimbolSplitFields & _
							gsDateBlock
						fuPrint record_convert_prev
					end if
				else
					if gbWriteString then
						fuPrint "Текущая строка в блоке и ее нужно записать"
						fuPrint record & gsSimbolSplitFields & gsDateBlock
						objTextFileWrite.WriteLine record & gsSimbolSplitFields & gsDateBlock
					else
						fuPrint "Текущая строка в блоке, но ее записывать не нужно"
					end if
				end if
			else
				fuPrint "Текущая строка не в блоке. Не записываем"
			end if

			'-- Сохранить текущие значения параметров для следующего прохода 
			idPrev = id
			eIdPrev = eId
			TgPrev = Tg
			UserNamePrev = UserName
			ComputerPrev = Computer
			image_unique_idPrev = image_unique_id
			imagePrev = image
		end if
	end if
	fuPrint "----------------------------------------------"
Loop

objTextFileWrite.Close
objTextFileOpen.Close

if gbDebug then
	objTextFileWriteLog.close
end if

WScript.Echo ""
WScript.Echo "* Операция успешно завершена." 


function fuRemoveExtention(lsFilename)
	lRes = lsFilename
	if InStr(lsFilename, ".") then
		lRes = Left(lsFilename, Len(lsFilename)-4)
	end if
	fuRemoveExtention = lRes
end function 

function fuGetDateFromFullDate(lsFullDate)
	lRes = lsFullDate
	if InStr(lsFullDate, " ") then
		lArr = Split(lsFullDate, " ")
		lsDate = lArr(0)
		lsTime = lArr(1)
		lRes = lsDate
	end if
	fuGetDateFromFullDate = lRes
end function 

function fuGetTimeFromFullDate(lsFullDate)
	lRes = lsFullDate
	if InStr(lsFullDate, " ") then
		lArr = Split(lsFullDate, " ")
		lsDate = lArr(0)
		lsTime = lArr(1)
		lRes = lsTime
	end if
	fuGetDateFromFullDate = lRes
end function 

function fuPrint(lsStr)
	'if gbDebug then
	'	wscript.echo lsStr
	'end if
	
	if gbDebug then
		objTextFileWriteLog.writeLine lsStr
	end if
	
	fuPrint = true
end function 

create_SQL_full.vbs

if Wscript.Arguments.Count = 1 then
	gsComputerName = Wscript.Arguments(0)
	gsSQLtype = "1"
elseif Wscript.Arguments.Count = 2 then
	gsComputerName = Wscript.Arguments(0)
	gsSQLtype = Wscript.Arguments(1)
else
	gsComputerName = InputBox("Имя компьютера", "Введите", "")
	gsSQLtype = InputBox("Тип sql-запроса?" & VBNewline & "[1 - короткий, 2 - полный, 3 - оба]", "Введите", "1")
end if

set objFSO = CreateObject("Scripting.FileSystemObject")

if gsSQLtype = "1" then
	fuCreateSQLFile gsComputerName, "1"
elseif gsSQLtype = "2" then
	fuCreateSQLFile gsComputerName, "2"
elseif gsSQLtype = "3" then
	fuCreateSQLFile gsComputerName, "1"
	fuCreateSQLFile gsComputerName, "2"
end if

function fuGetTableName(lsCompName)
	lsTmp = lsCompName
	
	if InStr(lsTmp, "-") then
		lsTmp = Replace(lsTmp, "-", "_")
	end if
	
	fuGetTableName = lsTmp
end function 

sub fuCreateSQLFile(lsComputerName, lsSQLtype)
	if lsSQLtype = "1" then
		lsTemplateFilename = "f:Computertemplate-short.sql"
	elseif gsSQLtype = "2" then
		lsTemplateFilename = "f:Computertemplate-full.sql"
	end if
	
	lsLogFilename = "f:Computer" & lsComputerName & "-" & lsSQLtype & ".sql"
	lsTableName = fuGetTableName(lsComputerName)

	if not objFSO.FileExists(lsLogFilename) then
		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 8, True)
	else
		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 2, True)
	end if
	
	Set objTextFileOpen = objFSO.OpenTextFile(lsTemplateFilename, 1)
	
	do until objTextFileOpen.AtEndOfStream
		record = objTextFileOpen.Readline
		if InStr(record, "WARNING__TABLE_NAME_FOR_CHANGE") then
			record = Replace(record, "WARNING__TABLE_NAME_FOR_CHANGE", lsTablename)
		end if
		objTextFileWriteLog.writeLine record
	loop 

	objTextFileOpen.Close
	objTextFileWriteLog.Close
end sub 

template-short.sql

SELECT     TOP (100) PERCENT Computer AS [Имя компьютера], UserName AS [Учетная запись], image AS Программа, start_time AS [Время запуска], 
                      stop_time AS [Время завершения], dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) / 3600)) 
                      + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 / 60)) 
                      + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 % 60)) AS Длительность
FROM         (SELECT     r.Computer, r.UserName, r.image_unique_id, r.image, r.Tg AS start_time, MIN(s.Tg) AS stop_time
                       FROM          (SELECT     id, eId, Tg, UserName, Computer, image_unique_id, image
                                               FROM          dbo.WARNING__TABLE_NAME_FOR_CHANGE
                                               WHERE      (eId = 592) AND (Tg > CONVERT(DATETIME, '2013-01-01 00:00:00.000', 102))
                                               ) AS r INNER JOIN
                                                  (SELECT     id, eId, Tg, UserName, Computer, image_unique_id, image
                                                    FROM          dbo.WARNING__TABLE_NAME_FOR_CHANGE
                                                    WHERE      (eId = 593)) AS s ON r.image_unique_id = s.image_unique_id AND r.image = s.image AND r.id < s.id AND r.Tg <= s.Tg
                       GROUP BY r.UserName, r.Computer, r.image_unique_id, r.image, r.Tg) AS DERIVEDTBL
ORDER BY 'Время запуска' DESC

update_result_file.vbs

if Wscript.Arguments.Count = 1 then
	gsFileName = Wscript.Arguments(0)
	gsFileNameRes = fuRemoveExtention(gsFileName) & ".xls"
elseif Wscript.Arguments.Count = 2 then
	gsFileName = Wscript.Arguments(0)
	gsFileNameRes = Wscript.Arguments(1)
else
	gsFileName = InputBox("Файл для обновления", "Ввод", "")
	gsFileNameRes = InputBox("Файл результата", "Ввод", fuRemoveExtention(gsFileName) & ".xls")
end if

sgSimbolSplit = ";"
gsSimbolSplitFields = vbTab

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)

if not objFSO.FileExists(gsFileName) then
	wscript.echo "Исходного файла для обновления нет, выхожу!"
	objTextFileOpen.Close
	Wscript.Quit
end if

if not objFSO.FileExists(gsFileNameRes) then
	set objTextFileWriteRes = objFSO.OpenTextFile(gsFileNameRes, 8, True)
else
	set objTextFileWriteRes = objFSO.CreateTextFile(gsFileNameRes, True)
end if

do until objTextFileOpen.AtEndOfStream
	record = objTextFileOpen.Readline
	
	if ((InStr(record, "--------")) or (Len(record) = 0) or (InStr(record, "обработано строк")) or (InStr(record, "rows affected"))) then
		'wscript.echo "пропускаю строку: '" & record & "'"
	else
		if InStr(record, sgSimbolSplit) then
			recordRes = Replace(record, sgSimbolSplit, gsSimbolSplitFields)
		else
			recordRes = record
		end if
		
		objTextFileWriteRes.writeLine recordRes
	end if
loop 

objTextFileWriteRes.Close
objTextFileOpen.Close

WScript.Echo "Обновление завершено! Результирующий файл " & gsFileNameRes

function fuRemoveExtention(lsFilename)
	lRes = lsFilename
	if InStr(lsFilename, ".") then
		lRes = Left(lsFilename, Len(lsFilename)-4)
	end if
	fuRemoveExtention = lRes
end function 

Блок работы с компьютерами с семёркой

Bat-файл:

is_computer_online7.bat

cscript //nologo "f:Computeris_computer_online7.vbs" %1 %2

Bat-файл запускает скрипт. Скрипт выполняет сохранение событий журнала безопасности в evt-файл и запускает основной батник mo7.bat.

is_computer_online7.vbs

on error resume next

dim gsComputerName
dim gsUseLogFile
dim gsLogFilename
dim gbFlag

dim gsTableName
dim gsCompName
dim gsRunCmd


if Wscript.Arguments.Count = 1 then
	gsComputerName = Wscript.Arguments(0)
	gsUseLogFile = "n"
elseif Wscript.Arguments.Count = 2 then
	gsComputerName = Wscript.Arguments(0)
	gsUseLogFile = Wscript.Arguments(1)
else
	gsComputerName = InputBox("Имя компьютера", "Введите", "")
	gsUseLogFile = InputBox("Использовать log-файл для проверки?" & VBNewline & "[y/n]", "Введите", "y")
end if

WScript.Echo "* Имя компьютера " & gsComputerName

if lCase(gsUseLogFile) = "y" then
	gbFlag = false
	
	gsLogFilename = "f:Log" & gsComputerName & ".log"

	WScript.Echo "* Файл журнала " & gsLogFilename

	set objFSO = CreateObject("Scripting.FileSystemObject")

	if not objFSO.FileExists(gsLogFilename) then
		WScript.Echo "* Файла журнала нет. Создается..."
		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 8, True)
		objTextFileWriteLog.writeLine "n"
		objTextFileWriteLog.close
		WScript.Echo "* Создан успешно."
	end if

	set objTextFileOpen = objFSO.OpenTextFile(gsLogFilename, 1)

	do until objTextFileOpen.AtEndOfStream
		record = trim(objTextFileOpen.Readline)
		if record = "n" then
			WScript.Echo "* Компьютер не проверялся ранее."
			if fuPing(gsComputerName) then
				'fuListInstalledSoftware gsComputerName
				gbFlag = true
				
				if fuBackup(gsComputerName) then
					WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
					fuUploadEvents gsComputerName
					wscript.sleep 10000
				end if
			end if
		elseif record = "y" then
			WScript.Echo "* Информация с компьютера " & gsComputerName & " уже закачана на сервер."
		else 
			WScript.Echo "* Некорректная информация о компьютере " & gsComputerName & " в log-файле."
		end if
	loop

	objTextFileOpen.close

	if gbFlag then
		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 2, True)
		objTextFileWriteLog.writeLine "y"
		objTextFileWriteLog.close
		WScript.Echo "* Информация записана в журнал."
		
		'MsgBox "Компьютер " & gsComputerName & " в сети!", vbInformation, "Внимание"
	end if
else
	'if fuPing(gsComputerName) then
		if fuBackup(gsComputerName) then
			WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
			fuUploadEvents gsComputerName
			wscript.sleep 60000
		end if
	'end if
end if

wscript.sleep 1000

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
			WScript.Echo "* Компьютер " & NetworkDevice & " в сети!" 
			lBoo = true
		else
			WScript.Echo "* Компьютера нет в сети."
		end if
	next
	
	fuPing = lBoo
end function 

function fuBackup(lsComputername)
	lsEvtBackupFilename = "c:" & lsComputername & ".evt"
	lsEvtBackupFilenameRemote = "\" & lsComputername & "c$" & lsComputername & ".evt"
	lbFlag = false
	
	'WScript.Echo "* lsEvtBackupFilename: " & lsEvtBackupFilename
	'WScript.Echo "* lsEvtBackupFilenameRemote: " & lsEvtBackupFilenameRemote
	
	set lObjFSO = CreateObject("Scripting.FileSystemObject")
	
	if lObjFSO.FileExists(lsEvtBackupFilenameRemote) then
		WScript.Echo "* Файл журнала уже есть. Используем существующий..."
		lbFlag = true
	else
		Wscript.Echo "* Выполняется резервное копирование..."
		
		Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate,(Backup)}!\" & lsComputername & "rootcimv2")

		Set colLogFiles = objWMIService.ExecQuery ("Select * from Win32_NTEventLogFile where LogFileName='Security'")

		For Each objLogfile in colLogFiles
			errBackupLog = objLogFile.BackupEventLog(lsEvtBackupFilename)
			If errBackupLog = 0 Then        
				Wscript.Echo "* Резервное копирование выполнено успешно."
				lbFlag = true
			Else
				Wscript.Echo "* Резервное копирование не выполнено."
			End If
		Next
	end if
	
	fuBackup = lbFlag
end function 

function fuUploadEvents(lsComputername)
	WScript.Echo "* Запущена закачка на сервер..."
	gsCompName = lCase(lsComputername)

	gsTableName = fuGetTableName(gsCompName)
	gsTableName = uCase(gsTableName)


	Set WshShell = CreateObject("WScript.Shell")

	gsRunCmd = "f:Computermo7.bat " & gsCompName & " " & gsTableName

	WScript.Echo "* Выполняется команда: '" & gsRunCmd & "'"
	WshShell.Run gsRunCmd
end function 

function fuGetTableName(lsCompName)
	lsTmp = lsCompName
	
	if InStr(lsTmp, "-") then
		lsTmp = Replace(lsTmp, "-", "_")
	end if
	
	fuGetTableName = lsTmp
end function  

mo7.bat делает следующее:

  • Забирает evt-файл с удалённого компьютера на сервер.
  • Преобразовывает evt-файл в evtx.
  • Информацию из evtx-файла закачивает на SQL Server.
  • Бекапит оригинальный evt-файл в папку Logi_ForReports (вдруг пользователь сотрёт свой журнал, а у нас копия есть).
  • Удаляет временный evtx-файл.
  • Формирует и выполняет sql-запрос к SQL Server’у.
  • Удаляет временные файлы (в случае отладки или для изучения работы скрипта, этот раздел можно закомментировать).
  • Перемещает отчёты в папку CheckComps.

mo7.bat

@echo off

@set WDate=%date:~-10%

@echo * Журнал безопасности перемещается с удаленного компьютера %1 (Windows 7)...
move \%1c$%1.evt f:Logs
@echo * Перемещение завершено.

@echo * Выполняется конвертация evt журнала в evtx...
wevtutil epl f:Logs%1.evt f:Logs%1.evtx /lf:true
@echo * Конвертация завершена.

@echo * Информация из журнала безопасности закачивается на сервер. Источник: f:Logs%1.evtx
LogParser.exe file:"f:Computerget_info_from_log7.sql"?source=f:Logs%1.evtx+output_file=%2 -i:EVT -o:SQL -server:"SQL-SRVSEC" -database:quickly -driver:"SQL Server" -createTable:ON
@echo * Процесс завершен.

@echo * Журнал безопасности перемещается в архив...
move f:Logs%1.evt f:Logi_ForReports%1_%WDate%_sec.evt
@echo * Перемещение завершено. Имя архивного файла 'f:Logi_ForReports%1_%WDate%_sec.evtx'


@echo * Создание sql-запроса...
cscript "F:Computercreate_SQL_full7.vbs" %1 1 //nologo
@echo * Создание завершено.

@echo * Выполнение sql-запроса...
SQLCMD.EXE -S SQL-SRVSEC -d quickly -E -i f:Computer%1-1.sql -o "f:Computer%1. Запуск программ.csv" -W -R -s ";" -w 4000
@echo * Выполнение завершено.

@echo * Исправляю результирующие файлы отчетов...
cscript F:Computerupdate_result_file7.vbs "f:Computer%1. Запуск программ.csv" //nologo
@echo * Исправление завершено.

@echo * Удаление временных файлов...
del f:Logs%1.evtx
del f:Computer%1-1.sql
del "f:Computer%1. Запуск программ.csv"
@echo * Удаление временных файлов завершено.

@echo * Перемещение файлов-отчетов...
move "f:Computer%1. Запуск программ.xls" "f:CheckComps%1. Запуск программ.xls"
@echo * Перемещение завершено.

@echo on

Примечание
Возможно, в батнике нужно будет SQLCMD.EXE заменить на «c:Program FilesMicrosoft SQL Server100ToolsBinnSQLCMD.EXE», а LogParser.exe на «c:Program Files (x86)Log Parser 2.2LogParser.exe» (или «c:Program FilesLog Parser 2.2LogParser.exe»).
Имя сервера с SQL Server’ом SQL-SRV, имя экземпляра SEC и имя базы quickly. Заменить на свои.

get_info_from_log7.sql

SELECT RecordNumber as id, 
	eventid as eId, 
	TimeGenerated as Tg, 
	--resolve_sid(sid) as UserName, 
	EXTRACT_TOKEN(Strings, 1, '|') as UserName,
	computername as Computer, 
	EXTRACT_TOKEN(Strings, 4, '|') as image_id,
	EXTRACT_TOKEN(Strings, 5, '|') as image,
	EXTRACT_TOKEN(Strings, 6, '|') as name
into %output_file% 
FROM %source%
where (EventID in (4688;4689)) and (
(TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITYNETWORK SERVICE')
and (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITYSYSTEM'))
and TimeGenerated >= TO_TIMESTAMP('01.01.2013 00:00:00','dd.MM.yyyy hh:mm:ss')
order by recordnumber desc

create_SQL_full7.vbs

'on error resume next

if Wscript.Arguments.Count = 1 then
	gsComputerName = Wscript.Arguments(0)
	gsSQLtype = "1"
elseif Wscript.Arguments.Count = 2 then
	gsComputerName = Wscript.Arguments(0)
	gsSQLtype = Wscript.Arguments(1)
else
	gsComputerName = InputBox("Имя компьютера", "Введите", "")
	gsSQLtype = InputBox("Тип sql-запроса?" & VBNewline & "[1 - короткий, 2 - полный, 3 - оба]", "Введите", "1")
end if

set objFSO = CreateObject("Scripting.FileSystemObject")

if gsSQLtype = "1" then
	fuCreateSQLFile gsComputerName, "1"
elseif gsSQLtype = "2" then
	fuCreateSQLFile gsComputerName, "2"
elseif gsSQLtype = "3" then
	fuCreateSQLFile gsComputerName, "1"
	fuCreateSQLFile gsComputerName, "2"
end if

function fuGetTableName(lsCompName)
	lsTmp = lsCompName
	
	if InStr(lsTmp, "-") then
		lsTmp = Replace(lsTmp, "-", "_")
	end if
	
	fuGetTableName = lsTmp
end function 

sub fuCreateSQLFile(lsComputerName, lsSQLtype)
	if lsSQLtype = "1" then
		lsTemplateFilename = "f:Computertemplate-short7.sql"
	elseif gsSQLtype = "2" then
		lsTemplateFilename = "f:Computertemplate-full7.sql"
	end if
	
	lsLogFilename = "f:Computer" & lsComputerName & "-" & lsSQLtype & ".sql"
	lsTableName = fuGetTableName(lsComputerName)

	'WScript.Echo "* Имя компьютера " & lsComputerName
	'WScript.Echo "* Имя таблицы " & lsTableName
	'WScript.Echo "* Имя файла sql-запроса " & lsLogFilename

	if not objFSO.FileExists(lsLogFilename) then
		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 8, True)
	else
		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 2, True)
	end if
	
	Set objTextFileOpen = objFSO.OpenTextFile(lsTemplateFilename, 1)
	
	do until objTextFileOpen.AtEndOfStream
		record = objTextFileOpen.Readline
		if InStr(record, "WARNING__TABLE_NAME_FOR_CHANGE") then
			record = Replace(record, "WARNING__TABLE_NAME_FOR_CHANGE", lsTablename)
		end if
		objTextFileWriteLog.writeLine record
	loop 

	objTextFileOpen.Close
	objTextFileWriteLog.Close
end sub 

template-short7.sql

SELECT TOP (100) PERCENT Computer AS [Имя компьютера], UserName AS [Учетная запись], program AS Программа, start_time AS [Время запуска], 
       stop_time AS [Время завершения], dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) / 3600)) 
       + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 / 60)) 
       + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 % 60)) AS Длительность
FROM   (SELECT r.Computer, s.UserName, r.programID, r.id AS R_ID, MIN(s.id) AS S_ID, r.program, r.Tg AS start_time, MIN(s.Tg) AS stop_time
        FROM   (SELECT  id, eId, Tg, UserName, Computer, image_id AS programID, image AS program
				FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
				WHERE   (eId = 4688)
				AND     (Tg > CONVERT(DATETIME, '2013-01-01 00:00:00.000', 102))
				AND     image not like '%.scr') AS r 
				INNER JOIN
				(SELECT  id, eId, Tg, UserName, Computer, image AS programID, name AS program
				FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
				WHERE   (eId = 4689)
				AND     name not like '%.scr') AS s 
				ON r.programID = s.programID AND r.program = s.program AND r.UserName = s.UserName AND r.id <= s.id
         GROUP BY r.Computer, s.UserName, r.programID, r.id, r.program, r.Tg) AS DERIVEDTBL
UNION ALL
SELECT  TOP (100) PERCENT Computer AS [Имя компьютера], UserName AS [Учетная запись], program AS Программа, start_time AS [Время запуска], 
        stop_time AS [Время завершения], dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) / 3600)) 
        + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 / 60)) 
        + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 % 60)) AS Длительность
FROM    (SELECT r.Computer, s.UserName, r.programID, r.id AS R_ID, MIN(s.id) AS S_ID, r.program, r.Tg AS start_time, MIN(s.Tg) AS stop_time
         FROM   (SELECT  id, eId, Tg, UserName, Computer, image_id AS programID, image AS program
				 FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
                 WHERE   (eId = 4688)
                 AND     (Tg > CONVERT(DATETIME, '2013-01-01 00:00:00.000', 102))
				 AND     image like '%.scr') AS r 
				 INNER JOIN
                (SELECT  id, eId, Tg, UserName, Computer, image AS programID, name AS program
                 FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
                 WHERE   (eId = 4689)
				 AND     name like '%.scr') AS s 
				 ON r.programID = s.programID AND r.program = s.program AND r.id <= s.id
         GROUP BY r.Computer, s.UserName, r.programID, r.id, r.program, r.Tg) AS DERIVEDTBL2
ORDER BY 'Время запуска' DESC 

update_result_file7.vbs

'on error resume next

if Wscript.Arguments.Count = 1 then
	gsFileName = Wscript.Arguments(0)
	gsFileNameRes = fuRemoveExtention(gsFileName) & ".xls"
elseif Wscript.Arguments.Count = 2 then
	gsFileName = Wscript.Arguments(0)
	gsFileNameRes = Wscript.Arguments(1)
else
	gsFileName = InputBox("Файл для обновления", "Ввод", "")
	gsFileNameRes = InputBox("Файл результата", "Ввод", fuRemoveExtention(gsFileName) & ".xls")
end if

sgSimbolSplit = ";"
gsSimbolSplitFields = vbTab

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)

if not objFSO.FileExists(gsFileName) then
	wscript.echo "Исходного файла для обновления нет, выхожу!"
	objTextFileOpen.Close
	Wscript.Quit
end if

if not objFSO.FileExists(gsFileNameRes) then
	set objTextFileWriteRes = objFSO.OpenTextFile(gsFileNameRes, 8, True)
else
	set objTextFileWriteRes = objFSO.CreateTextFile(gsFileNameRes, True)
end if

do until objTextFileOpen.AtEndOfStream
	record = objTextFileOpen.Readline
	
	if ((InStr(record, "--------")) or (Len(record) = 0) or (InStr(record, "обработано строк")) or (InStr(record, "rows affected"))) then
		'wscript.echo "пропускаю строку: '" & record & "'"
	else
		if InStr(record, sgSimbolSplit) then
			recordRes = Replace(record, sgSimbolSplit, gsSimbolSplitFields)
		else
			recordRes = record
		end if
		
		objTextFileWriteRes.writeLine recordRes
	end if
loop 

objTextFileWriteRes.Close
objTextFileOpen.Close

WScript.Echo "Обновление завершено! Результирующий файл " & gsFileNameRes

function fuRemoveExtention(lsFilename)
	lRes = lsFilename
	if InStr(lsFilename, ".") then
		lRes = Left(lsFilename, Len(lsFilename)-4)
	end if
	fuRemoveExtention = lRes
end function 

На SQL Server’е надо создать функцию FU_GET_FULL_QTY_TEST:

FU_GET_FULL_QTY_TEST

USE [quickly]
GO
/****** Object:  UserDefinedFunction [dbo].[FU_GET_FULL_QTY_TEST]    Script Date: 12/03/2013 13:03:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[FU_GET_FULL_QTY_TEST] (@short_qty varchar(255))
RETURNS varchar(255)
AS
BEGIN
DECLARE @retMsg varchar(255)
set @retMsg = @short_qty
if len(@short_qty) <= 1
set @retMsg = '0' + @retMsg
RETURN (@retMsg)
END

Архив со скриптами можно скачать тут.
Знаю, кажется много батников и скриптов. Но достаточно один раз настроить и пользоваться потом.

И кто как делает отчёт по запуску программ на компьютерах пользователей? Поделитесь.

Автор: luzhin_kirill

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js