Задание Powershell неожиданно возвращает объект System.Management.Automation.PSObject вместо System.Object

Я запускаю обслуживающий сценарий Powershell, который удаленно проверяет журналы событий сервера Windows на наличие различных записей, а затем предпринимает соответствующие корректирующие действия / действия по предупреждению.

Сценарий запускается каждые 5 минут, но иногда будет работать слишком долго из-за тайм-аута вызовов Get-WinEvent с ошибкой недоступности RPC при попытке запросить недоступные / не отвечающие серверы.

Чтобы избежать этой проблемы, я работаю над упаковкой вызовов Get-WinEvent в Jobs, чтобы я мог применить к ним настраиваемый тайм-аут.

Для заданий Get-WinEvent, обнаруживающих несколько событий, Receive-Job правильно возвращает массив System.Object [], содержащий объекты System.Diagnostics.Eventing.Reader.EventLogRecord. Если обнаружено только одно событие, Receive-Job вместо этого возвращает объект System.Management.Automation.PSObject.

Без кода, связанного с заданием, вызов Get-WinEvent, обнаруживающий одно событие, возвращает объект System.Diagnostics.Eventing.Reader.EventLogRecord, не являющийся массивом, который можно легко обернуть массивом для последующего использования.

У кого-нибудь есть лучший способ добавить тайм-аут к удаленному вызову Get-WinEvent или объяснение / исправление для возвращаемого System.Management.Automation.PSObject вместо не-массива System.Diagnostics.Eventing.Reader.EventLogRecord ' объект?

Функция и некоторые примеры вызовов показаны ниже:

Function CollectRemoteEvents($the_server,$event_log,$events_to_find,$event_label,$search_start,$search_timeout,$max_event_count){
    Try{
        $job_info = Start-Job -name GetEvents -scriptblock {param($server,$logname,$eventID,$StartTime,$MaxEvents) Get-WinEvent -ComputerName $server -FilterHashtable @{"logname"=$logname;"id"=$eventID;StartTime=$StartTime} -MaxEvents $MaxEvents} -Arg $the_server,$event_log,$events_to_find,$search_start,$max_event_count

        #if the provided timeout value is greater than 0, use it
        if($search_timeout -gt 0){
            #if the job takes a while, tell it to timeout after ## seconds
            $wait_result = Wait-Job -id $job_info.id -timeout $search_timeout
        }Else{
            #if the timeout was specified as 0, let the job run to completion
            $wait_result = Wait-Job -id $job_info.id
        }
        
        $current_job_state = Get-Job -id ($job_info.id)
        
        #check if the job has completed before time runs out
        if($current_job_state.State -eq "Completed"){
            #capture the job object
            $job = Get-Job -id ($job_info.id)

            #retrieve the output of the job; if the job raises errors, exceptions will be populated into the $joberror variable
            #NOTE: the $ is *intentionally* left out of the 'joberror' variable name in the command below
            $job_result = $job | Receive-Job -ErrorVariable joberror -ErrorAction Stop
            If($joberror -ne "" -And $joberror -ne $null){
                #if joberror is not empty, the job failed; log it
#               write-host "JobError: '$joberror'"  #used for debugging, this would log to file in a production capacity
            }Else{
#               write-host $job_result.gettype()    #used for debugging
                return ,$job_result
            }
        }else{
            #the search timed out
#           write-host "The event log search timed out." #used for debugging, this would log to file in a production capacity
            return $null
        }
    }Catch [Exception]{
        If($_.FullyQualifiedErrorID -eq "NoMatchingEventsFound,Microsoft.PowerShell.Commands.GetWinEventCommand"){
            #No logon timeout events were registered since $search_start
            write-host "$the_server : No $event_label events were found."
            return @()
        }Elseif($_.FullyQualifiedErrorID -eq "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetWinEventCommand"){
            #"argument validation error", exit the function with a return value indicating failure
            write-host "$the_server : Event log retrieval failed, can't check for $event_label events (Argument validation error);"
            return $null
        }Elseif($_.FullyQualifiedErrorID -eq "System.Diagnostics.Eventing.Reader.EventLogException,Microsoft.PowerShell.Commands.GetWinEventCommand"){
            #"the RPC server is unavailable", exit the function with a return value indicating failure
            write-host "$the_server : Event log retrieval failed, can't check for $event_label events (RPC server unavailable);"
            return $null
        }Else{
            #if the server logs cannot be retrieved, exit the function with a return value indicating failure
            write-host "$the_server : Event log retrieval failed, can't check for $event_label events (Check access/permissions)($($_.FullyQualifiedErrorID));"
            return $null
        }
    }
}

$server_name = "localhost"
$system_event_ID = @(6013)
$app_event_ID = @(1033)
$timeout_check_timespan = (Get-Date).AddMonths(-2)
$WinEvent_timeout = 10  #how long to let the Job run before timing out

$returns_array = CollectRemoteEvents $server_name 'System' $system_event_ID "Label One" $timeout_check_timespan $WinEvent_timeout 5
$returns_non_array = CollectRemoteEvents $server_name 'Application' $app_event_ID "Label Two" $timeout_check_timespan $WinEvent_timeout 1

write-host ""
write-host $returns_array
write-host $returns_array.count
write-host ""
write-host $returns_non_array
write-host $returns_non_array.count

Запятая в основной строке возврата - это попытка принудительного возврата массива (см .: Свойство Count массива в PowerShell с pscustomobjects)

Я также попытался создать экземпляр массива, а затем добавить к нему набор результатов:

$var = @()
$var += $results
return $var

приведение набора результатов в виде массива:

return [Array]($results)

и возвращая набор результатов как часть массива:

return @($results)

Я считаю, что это другая проблема, чем та, которая описана в предлагаемом решении «Возвращаемое значение функции в Powershell» - в моей проблеме проблема типов объектов присутствует до того, как функция вернется.

Раскомментирование следующей строки в целях отладки

#               write-host $job_result.gettype()    #used for debugging

В результате будет напечатан следующий результат:

System.Object []

System.Management.Automation.PSObject

Строка System.Object [] возвращается заданием, выполняющим запрос Get-WinEvent, который находит несколько событий.

Строка System.Management.Automation.PSObject возвращается заданием, выполняющим запрос Get-WinEvent, который находит одно событие.


person Graham    schedule 10.02.2016    source источник
comment
Попробуйте удалить запятую из return ,$job_result   -  person Jan Chrbolka    schedule 10.02.2016
comment
Запятая - это попытка принудительного возврата массива (см .: stackoverflow.com/questions/31514102/)   -  person Graham    schedule 10.02.2016
comment
Возможный дубликат возвращаемого значения функции в PowerShell   -  person Eris    schedule 10.02.2016
comment
Обнаруженный проблемный / неожиданный тип данных находится в переменной, заполненной Receive-Job, поэтому это происходит внутри функции до того, как какой-либо внешний код получит возвращенные данные.   -  person Graham    schedule 10.02.2016
comment
Попробуйте использовать $var.psbase, а не $var. PSobject, в моем понимании, должен быть оболочкой для обычного .NET-объекта. Таким образом, под обложкой почти все в сценарии будет PSObject, но PS обычно это скрывает. Однако в некоторых случаях кажется, что PS вводит в заблуждение (смущает?) Обертку объекта PSObject. Я думаю, это может быть связано с объектом, пересекающим пространства выполнения, из-за вашего случая здесь и аналогичного поведения при получении объектов, созданных с помощью сценария, запущенного в Powershell, созданном [system.management.automation.powershell]::Create().   -  person Χpẘ    schedule 11.02.2016


Ответы (1)


После большого количества поисков в Google, основанного на предложении пользователя Reddit, выяснилось, что вам фактически нужно дважды обернуть возвращаемый контент с одним объектом, чтобы он превратился в массив:

#this *does not* work
return @(@($job_result))

#This works
return , @($job_result)
person Graham    schedule 15.02.2016