4

The problem I'm having stems from passing a PSCustomObject as an argument to the Start-Process cmdlet (I'm essentially starting a new PowerShell process to run a script asynchronously from the calling script). Although the parameter is defined as type PSCustomObject, it is accepted as a string for some reason, and therefore it looks like I am required to convert it back to a PSCustomObject to access any attributes.

Here is the necessary part of my calling script:

# Convert JSON object to PowerShell object
$payload = ConvertFrom-Json $body

Write-Host $payload
## Returns exactly the following PsCustomObject:

## @{os=Windows Server Datacenter 2016; vmName=sbtestvm; diskType=Managed; username=testuser;
## password=Pa55w.rd1234; location=West Europe; size=Standard_D1;
## requestType=0; sender=test}

Write-Host $payload.os
## Returns: Windows Server Datacenter 2016

# Fire up new worker shell asynchronously
Start-Process powershell.exe -ArgumentList '-NoExit', "$PSScriptRoot\ServiceBus-AsyncWorker.ps1", "'$payload'"  # -Windowstyle Hidden

And my executed script:

Param(
    [Parameter(Mandatory=$True)]
    [PSCustomObject]$Request
)

# Import RequestHandler module to deal with processing service bus requests
Import-Module $PSScriptRoot\RequestHandler\RequestHandler.psm1

Write-Host $Request
## Returns exactly the same as 'Write-Host $payload' in the calling script

Write-Host $Request.os
## Returns empty string

Write-Host $Request.GetType()
## Returns System.String <--- This is the issue

Long story short: is there a way to prevent this object being automatically parsed as a string in the first place? If not - how can this string be cast back to the relevant object type?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Harry
  • 197
  • 3
  • 16
  • 1
    Have you tried removing the quotes in your `Start-Process` line? `"'$payload'" ` -> `$payload` – G42 Jun 28 '17 at 12:19
  • Yep, I've fiddled around with that aspect quite a bit - I've also tried to pass raw JSON and parse that in the other method instead, but all of the quotation marks in the JSON string vanish so it becomes practically useless. Your suggestion fails due to the spaces in the object format, therefore the command believes that the single argument is in fact multiple. – Harry Jun 28 '17 at 12:36

1 Answers1

3

Start-Process powershell.exe starts a new PowerShell process. You cannot pass PowerShell objects across process boundaries.

You could change

Start-Process powershell.exe -ArgumentList '-NoExit', "$PSScriptRoot\ServiceBus-AsyncWorker.ps1", "'$payload'"

to

$PSScriptRoot\ServiceBus-AsyncWorker.ps1 $payload

to avoid creating a new process, but that will run the script in the same window.

If you need to run the script detached from the console you could run it as a background job:

$job = Start-Job -ScriptBlock {
    Param($Path, $Data)
    & "$Path\ServiceBus-AsyncWorker.ps1" $Data
} -ArgumentList $PSScriptRoot, $payload

Otherwise you need to pass the argument in serialized form (e.g. the original JSON) when spawning the new process:

Start-Process powershell.exe -ArgumentList '-NoExit', "$PSScriptRoot\ServiceBus-AsyncWorker.ps1", "`"$body`""

and then (re-)create the object from the string:

Param(
    [Parameter(Mandatory=$true)]
    [string]$Json
)

# Import RequestHandler module to deal with processing service bus requests
Import-Module $PSScriptRoot\RequestHandler\RequestHandler.psm1

$Request = ConvertFrom-Json $Json
...
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Thanks @ansgar - Background jobs are something I haven't seen before, but definitely seem like a cleaner way to get around this. I attempted to use your code exactly - however from a bit of research it looks like the `$using` command works for PowerShell V3 and earlier. For newer versions, an `ArgumentList` is required (see my edit). Also, I tried to use your final solution (after attempting this earlier myself), however, as mentioned to @gms0ulman, the string escape characters ruin the JSON formatting, rendering this method useless. PS. Thanks for fixing my question formatting! – Harry Jun 28 '17 at 13:41
  • The `using` [scope modifier](https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_scopes) was introduced with PowerShell v3. It's working just fine in PowerShell v4 and above. It does NOT work prior to PowerShell v3. – Ansgar Wiechers Jun 28 '17 at 13:50
  • Ah okay, I must have read this incorrectly. Although, I'm using v5 and the script you suggested produced a couple of syntax errors, even when preceded by the call operator. The syntax of the file path after `$using:` prevented the script from executing, and if I was to change this to a string variable, use of `$using:payload` threw an unexpected token error. – Harry Jun 28 '17 at 14:04
  • `using` might not work with [automatic variables](https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_automatic_variables) like `$PSScriptRoot` (haven't tested). `$using:payload` should work, though. – Ansgar Wiechers Jun 28 '17 at 14:07