Вычисленные свойства в выражениях конвейера возвращают ноль в заданиях при передаче с использованием PSObject - PullRequest
1 голос
/ 21 марта 2020

Я пытаюсь передать хеш-таблицу вычисленных свойств в запрос для использования с Select-Object. Работает при запуске в консоли. Я могу подтвердить, что задание считывает хеш-таблицу, поскольку в ней перечислены выбранные свойства в результате, но все их значения равны нулю.

Примечание: Я понимаю, что мне не нужно Тип приведите эти свойства. Я просто демонстрирую свою проблему.

Если я запускаю следующий код (это выглядит странно, но на самом деле есть вариант использования для этого), вывод содержит мои выбранные свойства (из $globalConfig.SystemState.Processors.SelectProperties), но вычисленные свойства имеют значение null, единственное свойство, которое возвращает правильное значение: name:

$globalConfig = @{
    PingAddress = '8.8.8.8';
    SystemState = @{
        Processors = @{
            Namespace = 'root\cimv2';
            ClassName = 'Win32_Processor';
            SelectProperties = 'name', @{ n = 'CpuStatus'; e = { [int]$_.CpuStatus }}, @{ n = 'CurrentVoltage'; e = { [int]$_.CurrentVoltage }};
        }
    }
}

$job = Start-Job -Name Processors -ArgumentList $globalConfig.SystemState.Processors -ScriptBlock {
    Try{
        $Response = @{
            State   = @();
            Error   = $Null
        }
        $Response.State = Get-CimInstance -ClassName $Args[0].ClassName | Select-Object $Args[0].SelectProperties -ErrorAction Stop
    }Catch{
        $Response.Error = @{Id = 2; Message = "$($Args[0].Target) query failed: $($_.Exception.Message)"}
    }

    Return $Response
}

$job | Wait-Job
$job | Receive-Job | ConvertTo-Json -Depth 3

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
119    Processors      BackgroundJob   Completed     True            localhost            ...

{
    "Error":  null,
    "State":  {
                  "name":  "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz",
                  "CpuStatus":  null,
                  "CurrentVoltage":  null
              }
}

Тем не менее, если я запускаю то же самое задание, но с теми же вычисленными свойствами, жестко запрограммированными (не переданными в Select-Object с помощью PSObject), оно работает как положено (значения 1 и 12 возвращаются в выводе):

$job = Start-Job -Name Processors -ArgumentList $globalConfig.SystemState.Processors -ScriptBlock {
    Try{
        $Response = @{
            State   = @();
            Error   = $Null
        }
        $Response.State = Get-CimInstance -ClassName $Args[0].ClassName | Select-Object Name, @{ n = 'CpuStatus'; e = { [int]$_.CpuStatus }},@{ n = 'CurrentVoltage'; e = { [int]$_.CurrentVoltage }}
    }Catch{
        $Response.Error = @{Id = 2; Message = "$($Args[0].Target) query failed: $($_.Exception.Message)"}
    }

    Return $Response
}

$job | Wait-Job
$job | Receive-Job | ConvertTo-Json -Depth 3

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
121    Processors      BackgroundJob   Completed     True            localhost            ...

{
    "Error":  null,
    "State":  {
                  "Name":  "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz",
                  "CpuStatus":  1,
                  "CurrentVoltage":  12
              }
}

Как передать объект с вычисленными свойствами в строку Select-Object, находясь внутри задания ?

Ответы [ 2 ]

1 голос
/ 21 марта 2020

Это хеш-таблица, а не псобъект. Похоже, вы не можете передавать скриптовые блоки на рабочие места. Они превращаются в строки.

$globalConfig = @{
  PingAddress = '8.8.8.8'
  SystemState = @{
    Processors = @{
      Namespace = 'root\cimv2'
      ClassName = 'Win32_Processor'
      SelectProperties = 'name', 
        @{ n = 'CpuStatus'; e = { [int]$_.CpuStatus }},
        @{ n = 'CurrentVoltage'; e = { [int]$_.CurrentVoltage }}
    }
  }
}

start-job -args $globalconfig.systemstate.processors {
  $list = $args[0].selectproperties
  $list[1].e.gettype()
  $list[2].e.gettype()
} | receive-job -wait -auto



IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object
True     True     String                                   System.Object

Работает с потоковыми заданиями. Threadjobs не сериализуют свои объекты. В PS 5 вы можете скачать его из галереи PowerShell. https://www.powershellgallery.com/packages/ThreadJob В PS 6 и 7, это идет с этим. Это не делает новый процесс, и все равно быстрее. (У него нет псевдонима "-args".)

start-threadjob -argumentlist $globalconfig.systemstate.processors {
  $list = $args[0].selectproperties
  $list[1].e.gettype()
  $list[2].e.gettype()
} | receive-job -wait -auto


IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ScriptBlock                              System.Object
True     True     ScriptBlock                              System.Object

Или используйте метод stati c [scriptblock] :: create (), как здесь: Как передать скрипт-блок как один из параметров в start-job

0 голосов
/ 21 марта 2020

Я бы не нашел свой ответ, если бы @ js2010 не указывал мне правильное направление, спасибо!

Он / она прав: по соображениям безопасности вы не можете передать блок скриптов на работу, но так как всегда с PowerShell есть обходной путь.

Я могу заставить это работать, переключившись с Start-Job на Invoke-Command -asjob, но для этого требуется, чтобы WinRM был запущен и сконфигурирован, что не вариант в рабочей среде.

$globalConfig = @{
    PingAddress = '8.8.8.8';
    SystemState = @{
        Processors = @{
            Namespace = 'root\cimv2';
            ClassName = 'Win32_Processor';
            SelectProperties = 'Name',  @{ n = 'CpuStatus'; e = { [int]$_.CpuStatus }};
        }
    }
}

$ScriptBlock = {

    param(
        [Parameter(Mandatory=$True, Position=1)]
        [hashtable]$hashtable
    )

    Try{
        $Response = @{
            State = @(); 
            Error = $Null
        }
        $Response.State = Get-CimInstance -ClassName $hashtable.ClassName | Select-Object -Property $hashtable.SelectProperties -ErrorAction Stop
    }Catch{
        $Response.Error = @{Id = 2; Message = "$($data.Target) query failed: $($_.Exception.Message)"}
    }

    Return $Response
}

Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $globalConfig.SystemState.Processors -ComputerName . -AsJob


Name                           Value
----                           -----
Error
State                          @{Name=Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz; CpuStatus=1}

Это должно быть возможно ... поэтому я продолжу тестировать решения и опубликую все, что найду здесь.

Обновление

У меня это работает, оно немного грязное, но оно соответствует потребности проекта. Я просто создаю команду, используя хеш-таблицу вне задания (в виде строки), передаю строку задания в качестве аргумента и затем запускаю команду внутри задания, используя Invoke-Expression:

$globalConfig = @{
    PingAddress = '8.8.8.8';
    SystemState = @{
        Processors = @{
            Target = 'Processors'
            Namespace = 'root\cimv2';
            ClassName = 'Win32_Processor';
            SelectProperties = [string]('"Name", @{ n = "CpuStatus"; e = { [int]$_.CpuStatus }}');
        }
    }
}

$Args = [PSCustomObject]@{
    Target = $globalConfig.SystemState.Processors.Target;
    Command = "Get-CimInstance -ClassName $($globalConfig.SystemState.Processors.ClassName) -ErrorAction Stop | Select-Object $($globalConfig.SystemState.Processors.SelectProperties) -ErrorAction Stop";
}

$job = Start-Job -Name Processors -ArgumentList $Args -ScriptBlock {

    Try{
        $Response = @{
            State = @(); 
            Error = $Null
        }
        $Response.State = Invoke-Expression -Command $args.Command
    }Catch{
        $Response.Error = @{Id = 2; Message = "$($Args.Target) query failed: $($_.Exception.Message)"}
    }

    Return $Response
}

$job | Wait-Job
$job | Receive-Job | ConvertTo-Json -Depth 3


{
    "Error":  null,
    "State":  {
                  "Name":  "Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz",
                  "CpuStatus":  1
              },
    "PSComputerName":  "localhost",
    "RunspaceId":  "89f17de1-98b6-4746-a0ba-3e7c47294c61",
    "PSShowComputerName":  false
}
...