Get-Service via Invoke-Command and the "Status" property

This topic contains 12 replies, has 4 voices, and was last updated by Profile photo of Fredrik Kacsmarck Fredrik Kacsmarck 3 weeks, 3 days ago.

Viewing 13 posts - 1 through 13 (of 13 total)
  • Author
    Posts
  • #53061
    Profile photo of Fredrik Kacsmarck
    Fredrik Kacsmarck
    Participant

    Hi all,

    First time poster but have been lurking for a while.
    Anyway I stumbled upon this question and thought I should ask if there is quick way of solving this.

    If you run the following line:

    $data = Invoke-Command -ComputerName server1 {Get-Service | select name,displayname,status}

    If you then do a

    $data | get-member

    You would see for the status property:
    Name: Status
    MemberType: NoteProperty
    Definition: Deserialized.System.ServiceProcess.ServiceControllerStatus Status=Stopped

    Is there an easy/quick way of just getting either the deserialized or the string version of the definition?

    I can always recreate new objects and just picking one of them in a foreach loop.
    But is there a quicker way to "get rid" of one of the definition values for Status?

    Thanks in advance,
    Fredrik

    #53065
    Profile photo of Don Jones
    Don Jones
    Keymaster

    So... you're running Get-Member, which is supposed to show you the definition.

    What is it you're after? Just "Stopped?"

    #53071
    Profile photo of Dan Potter
    Dan Potter
    Participant

    This?

    $data | select name,displayname,@{n='def';e={($_ |gm | ?{$_.name -eq 'status'}).definition}}

    #53080
    Profile photo of Jonathan Warnken
    Jonathan Warnken
    Participant

    Look at the answer by Boe Prox from https://social.technet.microsoft.com/Forums/scriptcenter/en-US/6ae98ec3-4f87-4704-9e0d-b9e61502a377/invokecommand-return-object-best-way-to-format-or-access-the-data?forum=ITCG

    I think it has to do with the object being returned as a deserialized hash table. I had some issues access data that that object type in the past. I did some up with something similiar to what you are looking for, but it does have an extra step to make it into a hashtable that you can access.

    $ret = Invoke-Command -computername comp1,comp2,comp3 -ScriptBlock {
    New-Object PSObject -Property @{
    Computer = hostname
    IP = ipconfig
    Pass_policy = net accounts
    }
    }
    $hash = @{}
    $ret | % {
    $hash[$_.computer]= $_
    }
    You can then do something like $hash.'computername'.ip and get the data.

    #53088
    Profile photo of Fredrik Kacsmarck
    Fredrik Kacsmarck
    Participant

    The issue is this (in a condensed form):

    $data | convertto-json | convertfrom-json

    Will give you an error because status gets two values named 'value' which don't work.

    I have solved the problem by creating new objects but I wonder if its possible to just get one of those values from the first invoke statement. Rather than go through an extra loop.

    #53094
    Profile photo of Jonathan Warnken
    Jonathan Warnken
    Participant

    the issue on my test lab was the returns from get-service had to be converted prior to sending the data back to the initiating system

    this was on windows 10 1607 and powershell 5.1.14393.103

    $data =Invoke-Command -ComputerName . {Get-Service | select name,displayname,status|convertto-json}|ConvertFrom-Json
    
    #53096
    Profile photo of Don Jones
    Don Jones
    Keymaster

    Can you share a bit about the scenario, here?

    I ask because the native XML serialization/deserialization should work – the back-and-forth in JSON shouldn't be necessary. In general, it's considered a good practice to get the entire XML object and select locally...

    Invoke-Command { Get-Service } | Select name,status

    So that the object being serialized isn't a Selected subset. That just helps the ETS and formatting system make the right decisions. When you select remotely, you can indeed get some odd decisions being made, since the receiving computer doesn't have a proper TypeName to match against. That's when you'll tend to get goofy hashtables.

    #53099
    Profile photo of Fredrik Kacsmarck
    Fredrik Kacsmarck
    Participant

    Don,

    Didn't think of using the select after the return of the data, if it works then it would be a better solution.

    Jonathan,

    Sorry I tried to keep the "scenario" as close to the core problem as possible but I guess it became too short 🙂
    I don't want to convert the data straight away to json it was just a way to illustrate the problem.
    Basically I've written a module that will inventory local and remote servers, according to our needs and then output the results to json.
    Later on if we would like to use that information we could import the json file and convert it back to powershell objects or any other tool that can use json as a starting point.

    #53101
    Profile photo of Fredrik Kacsmarck
    Fredrik Kacsmarck
    Participant

    Yes, doing the select after the invoked result, rather than in the scriptblock works.
    Didn't think it would be a difference but I guess I learn something every day 🙂

    #53103
    Profile photo of Don Jones
    Don Jones
    Keymaster

    And consider persisting the results in CliXML. It's a better-fidelity format; you can always use PowerShell to import that and convert to JSON if some other tool needs that.

    #53105
    Profile photo of Jonathan Warnken
    Jonathan Warnken
    Participant

    What I saw was the Status object had two values a numerical value and a Friendly string value when it is deserialized

    Compare these two tests

    Get-service Bits|Select name,displayname,status|ConvertTo-Json
    {
        "Name":  "Bits",
        "DisplayName":  "Background Intelligent Transfer Service",
        "Status":  4
    }

    But the result is different when deserialized

    Get-Service Bits |select name,displayname,status|Export-Clixml -path D:\test\Test_data.xml
    $data = Import-Clixml -path D:\test\Test_data.xml
    $data|ConvertTo-Json
    {
        "Name":  "Bits",
        "DisplayName":  "Background Intelligent Transfer Service",
        "Status":  {
                       "value":  4,
                       "Value":  "Running"
                   }
    }
    #53107
    Profile photo of Don Jones
    Don Jones
    Keymaster

    Yeah, right. There's only a few instances where you'd see that, but Services is one of those instances. The actual value is 4, but there's a translation mechanism in play that also gives you "Running." There's some PowerShell magic getting involved in the middle. I can see where that would be troubling for strict JSON.

    And for the record, that's not happening because you're selecting a subset of properties. It'd do it with the entire object, too. There are some other translated properties that'll do the same thing, in fact, like StartType. But technically, Status becomes a collection, having two child objects. In really truly strict JSON, "value" and "Value" are different (case sensitivity). I think .NET's JSON implementation is a little fuzzier, though.

    The difference is that with CliXML, PowerShell will behave "more normally" after an import; if you use JSON as your persistence format, you'll lose some of PowerShell's automagic when you re-import. Hopefully not a deal-breaker for you.

    #53159
    Profile photo of Fredrik Kacsmarck
    Fredrik Kacsmarck
    Participant

    Will have a look at the difference between CliXML and JSON.

    One thing I noticed while doing the workaround by looping through the returned results was the selection of either the deserialized or the string value.
    The code I used was:

    $serviceData = @()
    
    foreach($s in $service)
         {
         $status = $s.Status.Value
         $properties = @{ "Name" = $s.Name;
                          "DisplayName" = $s.DisplayName;
                          "Status" = $status;
                        }
         $serviceData += New-Object -TypeName PSObject -Property $properties
     
         }
    

    While trying to select one of the values I experimented and I could get the string value with the above $status = $s.Status.Value example.
    To get just the numerical value I found I could do $s.Status[0]
    But $s.Status[1] didn't result in the string value, maybe I'm missing something but it seemed a bit strange.

    Anyway doing the select localy fixed the issue I was having.

Viewing 13 posts - 1 through 13 (of 13 total)

You must be logged in to reply to this topic.