1

I am working on dashboard that shows live details about a set of machines. I have the below for my Powershell

param([string] $server)


    $services = Get-WmiObject win32_service -ComputerName $server | Select -Property  Name, ExitCode, State, Status
    $disk = get-WmiObject win32_logicaldisk -Computername $server | Select-Object -Property DeviceID, @{n="Size";e={[math]::Round($_.Size/1GB,2)}},@{n="FreeSpace";e={[math]::Round($_.FreeSpace/1GB,2)}} 
    $cpu = Get-WmiObject win32_processor -ComputerName $server | Measure-Object -property LoadPercentage -Average | Select Average


$server_summary = @{
    services = $services
    disk = $disk
    cpu = $cpu
}

$json = $server_summary | ConvertTo-Json

return $json

I have the below for my c# function

public JsonResult test (string server)
    {
        string script = ControllerContext.HttpContext.Server.MapPath("~/Content/Powershell/Get-Server-Summary.ps1");
        string cmd = script + " -server '" + server + "'";

        using (var ps = PowerShell.Create())
        {
            ps.AddScript(cmd);
            var results = ps.Invoke();


            return Json(results);
        }

    }

In the script section of my HMTL I have

function test(server) {
        $.ajax({
            type: "POST",
            url: '@Url.Action("test")',
            dataType: "json",
            data: "{'server':" + "'" + server + "'}",
            contentType: "application/json; charset=utf-8",
            success: function (data) {


            },
            error: function (xhr, ajaxOptions, thrownError) {
                alert('Error getting stats for ' + server + '.');
            },

            complete: function () {

            }
        });
    }
$(document).ready(function(){
   test('server_name');
});

Even though I have converted the result to JSON before returning to the results to the application, I receive and empty string.

Am I missing something? I was able to get results with a slightly different function, but it returns incorrect results.

I will post that in the comments because I have too much code to post my question...

The results on the server side look like the below.enter image description here You can see that I am getting one result, which is a string containing all the json.

It was hard to get the screenshot in time. But you can see the value is there, It is just REALLY buried... Do I really need to parse through all of this? There isn't a function for this already?

enter image description here

gregnnylf94
  • 370
  • 3
  • 16
  • `foreach (var result in results) { output = result.ToString(); }` in my C# function returns incorrect results – gregnnylf94 Dec 19 '18 at 17:06
  • When you debug, what does `results` contain in the server-side code? Is the PowerShell operation asynchronous at all? Can you convert `results` to a simple custom type and return that object instead? In your browser's debugging tools, what is the raw content returned from the server? – David Dec 19 '18 at 17:10
  • @David I've added to the Q. But to clarify I do get "Correct" results, it is just being converted from JSON to a string somewhere along the lines. But I converted it twice, or could that be the problem? I converted to JSON once in Powershell, then once before passing it back through ajax. – gregnnylf94 Dec 19 '18 at 17:28
  • The question says you're getting an empty string in the response client-side, but that last comment suggests you're getting a string of JSON data. Which is it? As for "converting it twice", that doesn't make much sense. It really sounds like the object being returned by `ps.Invoke()` may not be exactly what you expect it to be, and a good approach to that (as well as to prevent unnecessary data from being exposed by your API) is to create a simple object (even an anonymous one) which returns only and exactly what you want to return from your API. That's probably worth a try. – David Dec 19 '18 at 17:34
  • What your PowerShell script returns already _is_ JSON text, so you shouldn't pass it to a `Json()` constructor - it seems that the JSON string is then considered a JSON string _value_, to which escaping is applied. – mklement0 Dec 19 '18 at 17:46
  • @mklement0 That makes sense, but when I pass the results from powershell without converting to JSON, I get an array with one element containing a hashtable reference. Which once passed back through ajax returns a `A circular reference was detected while serializing an object of type 'System.Management.Automation.PSP` error. I'm still working through it. – gregnnylf94 Dec 19 '18 at 17:54
  • @Daivd I am getting two variations. If I convert the results to JSON in Powershell, when passed back through ajax (from c#) `return Json(value)` is required. This converts my JSON to a single string. If I don't convert in Powershell I get the error mentioned in previous comment. I found `ConvertTo-Json` gives you a Json string which you then can pipe to `ConvterFrom-Json` which returns a JSON object. When this gets passed to C# I can see all my expected results in the Debugger, but I still get the `circular reference` error. I'm understanding it doesnt like the PSObjects but idk how to convert – gregnnylf94 Dec 19 '18 at 20:16
  • Solution is to convert results to JSON in Powershell. Pass the JSON string to C#. In C# convert the `PSObject` to a `string`. Then convert that string into an `object`. The exact code I used in C# `var results = ps.Invoke()[0].ToString(); JavaScriptSerializer j = new JavaScriptSerializer(); object json = j.Deserialize(results, typeof(object)); return Json(json);` Which I found https://stackoverflow.com/questions/22870624/convert-json-string-to-json-object-c-sharp" – gregnnylf94 Dec 19 '18 at 21:50

1 Answers1

2

Your PowerShell script already returns a JSON string, due to use of ConvertTo-Json.

As you state, the next step, in your C# code, is to extract that string from the System.Collections.ObjectModel.Collection<System.Management.Automation.PSObject instance that PowerShell.Invoke() returns:

 string json = ps.Invoke()[0].ToString();

The final step is to use that string as-is as your method's result data, via the Content() method - there is no need to deserialize the string first only to re-serialize it with the Json() method:

return Content(json, "application/json");

Note that you'll have to change your method's return type from JsonResult to ContentResult.

This approach comes courtesy of this answer.

mklement0
  • 382,024
  • 64
  • 607
  • 775