John Bauer
John Bauer

Reputation: 39

PowerShell Script getting JSON output to use in API

I am running a PowerShell script on a server to check on other machines on the network. I want the result of the check to be outputted in JSON format so I can send this JSON data via an api request to a web dashboard build with angular.

My set up:

Get-Request from Angular front end -> Express server -> run PowerShell script with Node-Powershell

Now I want to return the result in proper format to be used in the front end. What is the best way to accomplish this? I want to use the data to fill a material data table.

PowerShell Script status.ps1:

$Computers = @('ExampleComputer01', 'ExampleComputer02', 'ExampleComputer03', 'ExampleComputer04')
foreach ($computer in $Computers) {
gsv -cn $computer -Name ExampleProgram -ErrorAction 'SilentlyContinue'| select-object machinename, status | ConvertTo-Json 
}

Api from express server.js (using Node-Powershell):

app.get('/api/psjson', (request, response) => {
  ps.addCommand('./status.ps1');
  ps.invoke().then(output => {
    console.log(output);
    response.send(JSON.parse(output));
  }).catch(err => {
    console.log(err);
    response.send(err);
    ps.dispose();
  });
});

I tried using | ConvertTo-Json inside the loop but it is causing in error in node:

SyntaxError: Unexpected token { in JSON at position 55 at JSON.parse ()

Upvotes: 1

Views: 1464

Answers (2)

mklement0
mklement0

Reputation: 439842

CraftyB's answer diagnoses the problem correctly, but there's a simpler, more PowerShell-idiomatic solution that uses a single pipeline:

$computers = 'ExampleComputer01', 'ExampleComputer02', 'ExampleComputer03', 'ExampleComputer04'

gsv -cn $computers -Name Example -ErrorAction SilentlyContinue |
  | Select-Object machinename, status | 
      ConvertTo-Json
  • The crucial aspect is that all input objects are passed to a single ConvertTo-Json call - see the bottom section for an explanation.

  • In Windows PowerShell, Get-Service (gsv) the -ComputerName (-cn) parameter directly accepts an array of computer names.

    • Note: In PowerShell (Core) 7+, this form of remoting is no longer supported, so there is no -ComputerName parameter; there, assuming that the target computers are set up for PowerShell remoting, you could use:

      Invoke-Command -ComputerName $computers { Get-Service -Name Example -ErrorAction SilentlyContinue }
      

As for what you tried:

If you call ConvertTo-Json inside a loop, per input object, you will implicitly output multiple, independent JSON strings instead of a single JSON string representing the input objects as a JSON array:

Given the following sample input objects:

$objects = [pscustomobject] @{ foo=1 }, [pscustomobject] @{ foo=2 }

It is the difference between:

# !! BROKEN: *multiple* ConvertTo-Json calls.
# !! When the result is stringified, you get a space-separated list of the 
# !! individual JSON strings, which is NOT valid JSON.
PS> @"
$(
  foreach ($object in $objects) { $object | ConvertTo-Json -Compress }
)
"@

{"foo":1} {"foo":2}  # NOT valid JSON.

and:

# OK: *Single* ConvertTo-Json call, which outputs a valid JSON array.
PS> $objects | ConvertTo-Json -Compress

[{"foo":1},{"foo":2}]  # OK: valid JSON array.

Upvotes: 0

CraftyB
CraftyB

Reputation: 746

Please try the following:

$Computers = @('ExampleComputer01', 'ExampleComputer02', 'ExampleComputer03', 'ExampleComputer04')
$Results = @()
foreach ($computer in $Computers) {
    $Result = $null
    $Result = gsv -cn $computer -Name ExampleProgram -ErrorAction 'SilentlyContinue'| select-object machinename, status
    If ($Result -ne $null){
        $Results += $Result
    }
}

$Results | ConvertTo-Json

This builds an array of the results and then converts the array to JSON.

I think the issue you are experiencing is due to converting inside a loop and therefore the structure is incorrect.

Upvotes: 1

Related Questions