locked
Powershell - dealing with {host1, host2, host3, ....} RRS feed

  • Question

  • Hello,

    I'm working on a Clustered Windows 2012 R2 server, and I'd like to get the list the Preferred Owner list of some cluster Resources, but we have many nodes and the output is not readable from powershell because when use the standard command but it shows something like {server1, server2,.....} and the output on the screen is not complete.

    Then I've tried to get the output in table mode, but I don't know how to get it, I've used the following command using a "join" trick and I'm getting more or less what I want.

    get-clusterresource | Where{$_.resourcetype  -match "MyResourceType"} | get-clusterownernode | select clusterobject,,@{n='PrefOwners';e={$_.ownernodes -join","}} |ft-a

    I've got something like the following, all the Owners are included on a single column. I may survive with this, but I'll have to perform a second filter (excel) over  this output to split each PrefOwner in single columns.

    Resource Name                  PrefOwners

    "Cluster Resource name"    "PrefOwner1,PrefOwner2,PrefOwner3,prefOwner4,PrefOwner5"

    And I'd like to go an step further, what I want is have something like the following where each preferred owner has their own column.

    Resource Name                  owner1            owner2             Owner3           .......

    "Cluster Resource name"    "PrefOwner1"   "PrefOwner2"     "PrefOwner3"   .......

    How can I get this type of output on one single command?

    Best Regards

    Regards.

    Monday, April 27, 2015 9:11 AM

Answers

  • And what's wrong with multiple commands?

    Your best bet would be to create a custom object with the columns you're after, but that involves iterating through each owner and you won't get it on a single command.

    However, bearing in mind that you're you're using Windows 2012 R2 which comes with PowerShell 4 and that you're quite keen on concise statements, there are things you can do to shorten your version so far (and make it faster! MOAR SPEED!)

    A quick example: since PowerShell v4 you can now invoke a .Where() method on collections. It runs faster than piping it to a Where-Object, does exactly the same thing, and the syntax is pretty much the same too. Here's an example:

    # New PowerShell v4 way, fastest
    (Get-Service).Where({$_.Name -eq 'spooler'})
    
    # PowerShell v3 way, faster than PS <v3 ways
    Get-Service | Where-Object Name -eq spooler
    # Notice I don't even need to put quotes around the service name, whereas on all the other methods I do. Talk about being lazy!
    
    # Old way... Slowest one
    Get-Service | Where-Object {$_.Name -eq 'spooler'}

    For comparison, I ran some tests on all 3 ways by invoking each way 10 times and using Measure-Command to capture the times

    The v4 way took 160ms~ to run.

    The v3 way took 350ms~ to run.

    The <v3 way took 450ms~ to run.

    I ran all tests multiple times with consistent results, so there you go!

    Now... as for your custom object, as I said you can't do it in a single call (unless you want to have all your code cluttered in a line with over 200 characters and expect someone to be able to read it - this is not code art (http://www.dalatdesigns.com/blog/?p=11678).

    Anyway, here's a method. I don't have any clusters, so I settled for reading the displaying each of the properties of a WMI instance as one per column, but you get the idea:

    # I *should* have used the -Filter parameter of Get-WmiObject here but am trying to get something as similar to your code as possible
    $Processes = (Get-WmiObject -Class Win32_Process).Where({$_.Name -eq 'PowerShell.exe'})
    
    #It is better not to use a pipeline here else you have no way of telling when the subproperties you're dealing with now are from the old object or the new one
    foreach ($Process in $Processes)
    {
        $output = @{}
        $output.Name = $Process.Name
    
        
        # Normally it would work like this, but in WMI objects you can't retrieve them based on index, only name so... It should work for your objects though, give it a try!
        #for ($i = 0; $i -lt $Process.Properties.Count; $i++)
        #{
            #$output."Property$i" = $Process.Properties[$i].Name
        #}
    
        # As in this case we can't reference the property based on an index, we have to take another route...
        $i = 0
        foreach ($Property in $Process.Properties)
        {
            $output."Property$i" = $Property.Name
            $i++
        }
    
        $outputArray += New-Object -TypeName PSObject -Property $output
    }
    
    $outputArray | Format-Table *

    Obviously in my particular case the result will be almost unreadable as a table, since there's no more no less than 42 different properties on that object, but hey, this is what you asked for! You deal with it now :D

    PS: I'm just in a good mood today. No idea why, but I am. Please don't follow my 'You deal with it now' to the letter. If you have problems or questions come back!


    • Edited by FaustoNascimento Monday, April 27, 2015 10:22 AM
    • Marked as answer by Pep M Thursday, April 30, 2015 8:13 AM
    Monday, April 27, 2015 10:21 AM

All replies

  • And what's wrong with multiple commands?

    Your best bet would be to create a custom object with the columns you're after, but that involves iterating through each owner and you won't get it on a single command.

    However, bearing in mind that you're you're using Windows 2012 R2 which comes with PowerShell 4 and that you're quite keen on concise statements, there are things you can do to shorten your version so far (and make it faster! MOAR SPEED!)

    A quick example: since PowerShell v4 you can now invoke a .Where() method on collections. It runs faster than piping it to a Where-Object, does exactly the same thing, and the syntax is pretty much the same too. Here's an example:

    # New PowerShell v4 way, fastest
    (Get-Service).Where({$_.Name -eq 'spooler'})
    
    # PowerShell v3 way, faster than PS <v3 ways
    Get-Service | Where-Object Name -eq spooler
    # Notice I don't even need to put quotes around the service name, whereas on all the other methods I do. Talk about being lazy!
    
    # Old way... Slowest one
    Get-Service | Where-Object {$_.Name -eq 'spooler'}

    For comparison, I ran some tests on all 3 ways by invoking each way 10 times and using Measure-Command to capture the times

    The v4 way took 160ms~ to run.

    The v3 way took 350ms~ to run.

    The <v3 way took 450ms~ to run.

    I ran all tests multiple times with consistent results, so there you go!

    Now... as for your custom object, as I said you can't do it in a single call (unless you want to have all your code cluttered in a line with over 200 characters and expect someone to be able to read it - this is not code art (http://www.dalatdesigns.com/blog/?p=11678).

    Anyway, here's a method. I don't have any clusters, so I settled for reading the displaying each of the properties of a WMI instance as one per column, but you get the idea:

    # I *should* have used the -Filter parameter of Get-WmiObject here but am trying to get something as similar to your code as possible
    $Processes = (Get-WmiObject -Class Win32_Process).Where({$_.Name -eq 'PowerShell.exe'})
    
    #It is better not to use a pipeline here else you have no way of telling when the subproperties you're dealing with now are from the old object or the new one
    foreach ($Process in $Processes)
    {
        $output = @{}
        $output.Name = $Process.Name
    
        
        # Normally it would work like this, but in WMI objects you can't retrieve them based on index, only name so... It should work for your objects though, give it a try!
        #for ($i = 0; $i -lt $Process.Properties.Count; $i++)
        #{
            #$output."Property$i" = $Process.Properties[$i].Name
        #}
    
        # As in this case we can't reference the property based on an index, we have to take another route...
        $i = 0
        foreach ($Property in $Process.Properties)
        {
            $output."Property$i" = $Property.Name
            $i++
        }
    
        $outputArray += New-Object -TypeName PSObject -Property $output
    }
    
    $outputArray | Format-Table *

    Obviously in my particular case the result will be almost unreadable as a table, since there's no more no less than 42 different properties on that object, but hey, this is what you asked for! You deal with it now :D

    PS: I'm just in a good mood today. No idea why, but I am. Please don't follow my 'You deal with it now' to the letter. If you have problems or questions come back!


    • Edited by FaustoNascimento Monday, April 27, 2015 10:22 AM
    • Marked as answer by Pep M Thursday, April 30, 2015 8:13 AM
    Monday, April 27, 2015 10:21 AM
  • Thanks!!!

    Today I've learned something new with

    # New PowerShell v4 way, fastest
    (Get-Service).Where({$_.Name -eq 'spooler'})

    Thursday, April 30, 2015 8:13 AM
  • No problem, if you'd like to learn even more about these unfortunately mostly undocumented features read this amazing article by Kirk Munro.

    He'll even tell you how to get .where and .foreach onto PowerShell 3 if you're into that ;)

    http://www.powershellmagazine.com/2014/10/22/foreach-and-where-magic-methods/



    • Edited by FaustoNascimento Thursday, April 30, 2015 8:38 AM Gah, today is not my spelling dya. Wait, did I do it again? :(
    Thursday, April 30, 2015 8:37 AM
  • NOt a good way to get  a service:

    Get-Service spooler

    is the fastest way .  Using any form of where remotely will be much slower because all services will be returned then searched locally.


    \_(ツ)_/

    Thursday, April 30, 2015 8:47 AM
  • Jrv, I think you missed the very first line on my script:

    # I *should* have used the -Filter parameter of Get-WmiObject here but am trying to get something as similar to your code as possible

    I even used Get-Service on the same post just above it... The only reason why I user Get-WmiService was to have access to the .Properties property, as that's what was used to create my custom object.

    It was done this way because I was trying to recreate the OPs question, where the Get-ClusterResource cmdlet does NOT allow filtering on ResourceType (and he later gets passed multiple OwnerNodes, in the same way my script returned multiple Properties for the instance).


    • Edited by FaustoNascimento Thursday, April 30, 2015 9:24 AM Spelling is becoming a problem...
    Thursday, April 30, 2015 9:24 AM
  • Thanks!!!

    Today I've learned something new with

    # New PowerShell v4 way, fastest
    (Get-Service).Where({$_.Name -eq 'spooler'})

    I was responding too this statement which is misleading in this example.

    A better example sould be when filtering this:

    (dir c:\test).Where({$_.LastWriteTime -gt [datetime]::Today.AddDays(-1)})

    This will be faster than the pipeline equivalent since it directly access the collection rather than enumerating it in a pipeline.

    Get-Service works but is  bad example for this particular statement.


    \_(ツ)_/

    Thursday, April 30, 2015 9:41 AM
  • But I thought you were quite a fan of *not* filtering at source ;)
    Thursday, April 30, 2015 3:17 PM
  • But I thought you were quite a fan of *not* filtering at source ;)

    What gave you that idea?


    \_(ツ)_/

    Thursday, April 30, 2015 3:30 PM
  • Some discussions we got into in the past over whether to filter at source or not.
    Thursday, April 30, 2015 3:34 PM
  • Some discussions we got into in the past over whether to filter at source or not.

    Filtering at the source is almost always preferable.  I have hammered on that for years.  Most techs do not understand the difference so they skip trying to use it then have performance issues.


    \_(ツ)_/

    Thursday, April 30, 2015 3:36 PM
  • I know that, that's why we had a discussion about this a while ago where I said that even though for the OPs specific case filtering at source would have made no difference in terms of performance, it would have still been preferable to showcase it as at the very least it shows the community 'the right way' of doing things.

    However, and not saying it doesn't exist, but I've yet to come across a situation where filtering at source is not preferable/faster.

    Thursday, April 30, 2015 3:45 PM
  • However, and not saying it doesn't exist, but I've yet to come across a situation where filtering at source is not preferable/faster.

    Some filter conditions cannot be run at the source. In those cases we must filter at the client.

    This would filter at the source:

    Get-Process expl* -computer pc01

    This would have to run at the client:

    Get-Process  -computer pc01 | where{$_.Handles -gt 100}


    \_(ツ)_/

    Thursday, April 30, 2015 4:03 PM
  • Agree, but now you're not comparing apples with apples.

    You said it's almost always preferable to filter at source. That implies in some cases it's preferable not to filter at source, even though there is the option to do so.

    In the example you gave there is no option to filter on the 'source' cmdlet, since Get-Process does not allow to filter based on the property Handles.

    Still, you could do....

    Invoke-Command -ScriptBlock {(Get-Process).where{$_.Handles -gt 100}} -ComputerName pc01

    This will filter on the remote computer, rather than the client.

    Still, what I meant by 'filter at source' was filtering on the first instruction/function/cmdlet when that option is available, rather than using Where-Object to filter it after, regardless of dealing with local or remote computer(s)


    Thursday, April 30, 2015 4:18 PM
  • Which is what I would doo but most sites do not have remoting installed.

    \_(ツ)_/

    Thursday, April 30, 2015 4:21 PM
  • On a separate note, do you ever sleep? Seems like you're always here! :D
    Thursday, April 30, 2015 4:24 PM
  • Ha.  I work remote at night and stop here while waiting for things like builds and upgrades.

    I am on my way out now for other things so I will not be around for a number of hours then I will likely sleep for a few hours before starting the night.


    \_(ツ)_/

    Thursday, April 30, 2015 4:29 PM