Scripting Games 2010. Advanced Event 2 (Remote Event Log)

Еле-еле допилил скрипт для второго задания:

Event ScenarioYou need to retrieve the time that a workstation started. Because you still have a mixture of Windows 7, Windows Vista, and Windows XP computers, you cannot confidently use any of the newer event log entries. Instead, your research has revealed that you can get a pretty good estimate of the time a workstation started by querying the event log for the entry that states when the event log started. The event log entry you are looking for is seen in the following image.

 

So far, the Advanced scenario is the same as the Beginner scenario. Now for the Advanced criteria.

 

Design Criteria·         Your script should permit the use of alternative credentials when running against a remote computer. This means that if the user launching the script does not have sufficient rights to run against a remote computer, he should be able to provide them from the command line.

·         Your script should permit querying any of the traditional event logs.

·         Your script should permit querying for any event ID.

·         Your script should permit the user to specify a specific date or date range from which the events are to be returned.

·         Optionally, you should be able to query for events based upon a wildcard character pattern match in the event description.

·         Design points awarded for addition of command-line help and proper error handling. 

Задание, IMHO, носит несколько искусственный характер, т.к. определить uptime рабочей станции можно легко и просто при помощи  WMI  и двух строчек кода на PoSh’ике (и парсить логи для этого вовсе не требуется):

$OS=gwmi win32_operatingSystem -ComputerName .
$OS.ConvertToDateTime($OS.LastBootUpTime)

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

Сразу решил использовать WMI, т.к. все дополнительные требования, указанные в задании, можно реализовать при помощи запроса WQL. Вот тут я и наступил на грабли, о которых (до написания данного скрипта) и не подозревал. Оказалось, при попытке  выполнения следующей команды gwmi -ComputerName $ComputerName -Class Win32_NTlogEvent -filter $Filter

запущенной в контексте пользователя домена (не являющегося локальным администратором на компьютере, к которому производится обращение), PoSh будет возвращать сообщения об ошибках в отказе доступа вне зависимости от того, является ли компьютер, к которому производится обращение, членом домена или нет. Но, на что я сразу  не обратил внимание, когда писал этот скрипт, исключения, возникающие в случае обращения к доменному компьютеру отличаются от исключений, возникающих при обращении к НЕдоменному компьютеру, не смотря на то, что оба эти исключения сообщают об одном и том же, а именно: об отсутствии доступа. Более того, при обращении к доменному компьютеру возникает «неостанавливающая» ошибка (не приводящая к остановке работы скрипта), а при обращении к недоменному компьюьтеру выполнение работы скрипта прерывается, в случае возникновения отказа в доступе. В результате мой скрипт корректно работает только с компьютерами – членами домена.

И так, при обращении к доменному компьютеру мы имеем следующее:

PS > $error[0]| fl * -force
PSMessageDetails :
Exception : System.Management.ManagementException: Отказано в доступе
в System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
в System.Management.ManagementScope.InitializeGuts(Object o)
в System.Management.ManagementScope.Initialize()
в System.Management.ManagementObjectSearcher.Initialize()
в System.Management.ManagementObjectSearcher.Get()
в Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()
TargetObject :
CategoryInfo : InvalidOperation: (:) [Get-WmiObject], ManagementException
FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
PipelineIterationInfo : {0, 0}

PS > $error[0].exception| fl * -force
При обращении же к компьютеру не являющемуся членом домена результат другой:

ErrorInformation :
ErrorCode : AccessDenied
Message : Отказано в доступе
Data : {}
InnerException :
TargetSite : Void ThrowWithExtendedInfo(System.Management.ManagementStatus)
StackTrace : в System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
в System.Management.ManagementScope.InitializeGuts(Object o)
в System.Management.ManagementScope.Initialize()
в System.Management.ManagementObjectSearcher.Initialize()
в System.Management.ManagementObjectSearcher.Get()
в Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()
HelpLink :
Source : System.Management

$error[0]| fl * -force
PSMessageDetails :
Exception : System.UnauthorizedAccessException: Отказано в доступе. (Исключение из HRESULT: 0x80070005 (E_A
CCESSDENIED))
в System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr
errorInfo)
в System.Management.ManagementScope.InitializeGuts(Object o)
в System.Management.ManagementScope.Initialize()
в System.Management.ManagementObjectSearcher.Initialize()
в System.Management.ManagementObjectSearcher.Get()
в Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()
в System.Management.Automation.Cmdlet.DoBeginProcessing()
в System.Management.Automation.CommandProcessorBase.DoBegin()
TargetObject :
CategoryInfo : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException
FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
ErrorDetails :
InvocationInfo : System.Management.Automation.InvocationInfo
PipelineIterationInfo : {0, 0}

PS > $error[0].exception| fl * -force

Message : Отказано в доступе. (Исключение из HRESULT: 0x80070005 (E_ACCESSDENIED))
Data : {}
InnerException :
TargetSite : Void ThrowExceptionForHRInternal(Int32, IntPtr)
StackTrace : в System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorI
nfo)
в System.Management.ManagementScope.InitializeGuts(Object o)
в System.Management.ManagementScope.Initialize()
в System.Management.ManagementObjectSearcher.Initialize()
в System.Management.ManagementObjectSearcher.Get()
в Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()
в System.Management.Automation.Cmdlet.DoBeginProcessing()
в System.Management.Automation.CommandProcessorBase.DoBegin()
HelpLink :
Source : mscorlib

В общем-то, мой скрипт можно немного подправить для того, чтобы он стал более универсальным. Для этого можно сделать следующее: заменить в  строке gwmi -ComputerName $_ -Class Win32_NTlogEvent -filter $Filter -ErrorAction SilentlyContinue -ErrorVariable MyErr
-ErrorAction SilentlyContinue на -ErrorAction Stop и поместить эту строку  внутрь блока try…catch. Но сейчас я не буду этого делать, а размещу скрипт в том виде, в каком я его запостил на сайт Scripting Games:

<#
.Synopsis
	Get-RemEvents.ps1.ps1 PowerShell 20100428 shs
	Get events remotly from list of computers

	Get-RemEvents.ps1 <CompList> <LogName> [EventID] [DateBegin] [DateEnd] [$MesageWQLPatern]

.Parameter	CompList
	Full name of the file that contains list of computers
.Parameter LogName
	Name of the event log from which the script return events
.Parameter EventID
	ID of Event in event log to return by this script
.Parameter DateBegin
	Scritp looks for events that were generated from this date
.Parameter DateEnd
	Scritp looks for events that were generated till this date
.Parametr MesageWQLPatern
	Script looks fo events with messages matched this WQL-patern for Like operator
	see http://msdn.microsoft.com/en-us/library/aa392263(VS.85).aspx

.Example
	Get-RemEvents.ps1 C:\Scripts\PoSh.try\ScriptingGames\2010\2\complist.txt System 6005 04/26/2010 04/29/2010 %arted%
#>
param ($CompList,$LogName,[int]$EventID,[datetime]$DateBegin,[datetime]$DateEnd, [string]$MesageWQLPatern)
#
###############################################################################
#
# Filter. Return pingable computers only
#
Filter Where-Online
{
	$CompName=$_
    $ping = new-object System.Net.NetworkInformation.Ping
    #trap {Write-Verbose "$CompName `t-`t ping error"; Continue}
    try {if ($ping.send($_).Status -eq "Success" ) { $_ }}
	catch {Write-Verbose "$CompName `t-`t ping error"}
	#trap {Break}
}
###############################################################################
#
#==================================== Srcipt entry point ======================
#
cls
# Switch on|off verbose mode
$VerbosePreference = "Continue" #"SilentlyContinue" #Continue
#If reguiered parameters passed to the script...
if ($CompList -and $LogName) {
	#Parse input parameters
	#Test path to List of computers
	if (Test-Path $CompList) {
		#Make filter string to use in WMI query with Win32_NTlogEvent class
		$Filter="logfile='$LogName'"
		#if EventID defined...
		if ($EventID) {
			$Filter += " and eventcode='$EventID'"
		}
		#if $DateBegin defined...
		if ($DateBegin) {
			$Filter+= " and TimeGenerated >= '$([System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($DateBegin))'"
		}
		#if $DateEnd defined...
		if ($DateEnd) {
			$Filter+= " and TimeGenerated <= '$([System.Management.ManagementDateTimeConverter]::ToDMTFDateTime($DateEnd))'"
		}
		#if $MesageWQLPatern defined...
		if ($MesageWQLPatern) {
			$Filter+=" and Message LIKE '$MesageWQLPatern'"
		}
		#
		Write-Verbose "Result Filter String is $Filter"
		#get events for each computer in the list
		gc $CompList | Where-Online | foreach {
			Write-Host "`n`nProcessing $_... `n"
			#try to get events from computer
			gwmi -ComputerName $_  -Class Win32_NTlogEvent -filter $Filter -ErrorAction SilentlyContinue -ErrorVariable MyErr
			#parse errors with $? and -ErrorVariable
			#if we get an error...
			if (!$?) {
				# ... and  if it is  AccessDenyed error...
				if ($MyErr[0].Exception.errorcode -eq "AccessDenied") {
					#... then try with alternate credentials
					try {
						$cred=Get-Credential
						gwmi -ComputerName $_  -Class Win32_NTlogEvent -filter $Filter -Credential $cred -ErrorAction SilentlyContinue -ErrorVariable Err
					}
					catch
					{
						Write-Verbose "Error while trying to connect with alternate credential"
					}
				}
				#...else error will be reported to the user
				else {
					Write-Verbose "Display Error:"
					$MyErr
				}
			}
		}
	}
	else {
		Write-Host "File $CompList not found"
	}
}
else {
	Write-Host "Parameters required!`n See help in the head comment of this script!"
}

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.