Search code examples
powershelljobs

Can't repeatedly receive output from job


Can I use Start-Job inside a ScheduledJob at all?

Here's the story: I'm using PowerShell Remoting from inside of my app using .Net classes from System.Management.Automation, Remoting, and Runspaces. I run a script that creates a ScheduledJob on the remote host that does some basic stuff with Hyper-V, let's say, Restart-Vm. The action itself performs as expected, just as though I had run it in the console on this remote host.

Although there is one difference - if I did put it to the console I would get the results of the operation immediately. For example, if the VM is Disabled I would receive 'The operation cannot be performed while the virtual machine is in its current state'. In case with SJs, when some SJ trigger gets activated and the SJ runs, I have no possibility to get the results of my Restart-Vm command immediately after it has been received. Instead, I have to check up on the corresponding BackgroundJob that is created when the SJ gets triggered and check its state and receive its results. It's not OK because my application must write events of this kind to EventLog on the remote machine. Even if the Restart-Vm operation does not succeed no exception gets thrown, and I can't obviously write to the EventLog in the body of my scheduled job.

I thought the solution could be something like this: create a new job inside the scheduled job, make this job perform the target action and bind a handler to the state change event of this job which would write to EventLog.

However, when I implement this

Start-Job -ScriptBlock { params($guid) Get-Vm $guid | Restart-Vm -Force; } -ArgumentList $id;

it fails. Here's how it happens: the SJ gets fired, the corresponding Job is created, I check its state - it is Completed and it HasMoreData. When I do Receive-Job I get this error

Receive-Job : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:8
+ $res | receive-job
+        ~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (System.Manageme...n.PSRemotingJob:PSObject) [Receive-Job], ParameterBindingEx 
   ception
    + FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.ReceiveJobCommand

I've tried running the Restart-Vm action with -AsJob switch, passing credentials, but nothing works.

UPDATE 5 June, 2015

In fact, this problem has arisen from my need to catch exceptions thrown by Restart-Vm. The fact I did not realize until recently was that I must specify either -ErrorAction Stop or -ErrorVariable parameters when calling Restart-Vm, so that I can get a control over its execution. So there's no need in any hacks like nesting jobs anymore.


Solution

  • After executing the statement $res = Get-Job | Receive-Job The value of $res is the output received from the job. That output is not a job that could be fed into Receive-Job again.

    Demonstration:

    PS C:\> Start-Job -ScriptBlock { 'foo' }
    
    Id  Name  PSJobTypeName  State    HasMoreData  Location   Command
    --  ----  -------------  -----    -----------  --------   -------
    6   Job6  BackgroundJob  Running  True         localhost  'foo'
    
    PS C:\> $res = Get-Job | Receive-Job
    PS C:\> $res
    foo
    PS C:\> $res | Receive-Job
    Receive-Job : The input object cannot be bound to any parameters for the command
    either because the command does not take pipeline input or the input and its
    properties do not match any of the parameters that take pipeline input.
    At line:1 char:8
    + $res | Receive-Job
    +        ~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (foo:PSObject) [Receive-Job], ...
        + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Comman...

    You need to keep the job object (or its ID) if you want to receive multiple times:

    PS C:\> $job = Start-Job -ScriptBlock { 'foo' }
    PS C:\> $job | Receive-Job
    foo
    PS C:\> $job | Receive-Job
    PS C:\> Get-Job -Id $job.Id | Receive-Job
    PS C:\> _