4

I am trying to query multiple servers with WMI, but I don't always have access to the servers.

The code is below. Alas, it returns "access is denied" to the console, but I can't seem to get rid of it. Oh well.

However, I am trapping the servers that I can't connect to, so that I can tell someone else to look at them, or request access.

But when I run the code, it only returns the first list of servers; even if $failed_servers has values, nothing is returned. If I tell both to pipe to ogv, then two windows pop up.

Why won't both "$variable|select" work? If I remove the select on $failed_servers, then it shows up, albeit just sitting immediately underneath the successful ones. Which is okay-but-not-great.

$list = ("servera","serverb","serverc")
$failed_servers = @()
$final = foreach ($server_instance in $list)
{
$errors=@()
gwmi -query "select * from win32_service where name like '%SQLSERVER%'" -cn $server_instance -ErrorVariable +errors -ErrorAction SilentlyContinue
if ($errors.Count -gt 0) {$failed_servers += $server_instance
}
}

$final|select pscomputername, name, startmode, state |where {$_.pscomputername -ne $null}

$failed_servers |select @{N='Failed Servers'; E={$_}}

3 Answers 3

5

What you're experiencing is merely a display problem:

  • Both your Select-Object calls produce output objects with 4 or fewer properties whose types do not have explicit formatting data associated with them (as reported by Get-FormatData).

  • This causes PowerShell's for-display output formatting system to implicitly render them via the Format-Table cmdlet.

  • The display columns that Format-Table uses are locked in based on the properties of the very first object that Format-Table receives.

  • Therefore, your second Select-Object call, whose output objects share no properties with the objects output by the first one, effectively produces no visible output - however, the objects are sent to the success output stream and are available for programmatic processing.

A simple demonstration:

& {
  # This locks in Month and Year as the display columns of the output table.
  Get-Date   | Select-Object Month, Year
  # This command's output will effectively be invisible,
  # because the property set Name, Attributes does not overlap with
  # Month, Year
  Get-Item \ | Select-Object Name, Attributes
}

The output will look something like this - note how the second statement's output is effectively invisible (save for an extra blank line at the end):


Month Year
----- ----
    9 2021


Note the problem can even affect a single statement that outputs objects of disparate types (whose types don't have associated formatting data); e.g.:
(Get-Date | Select-Object Year), (Get-Item \ | Select-Object Name)


Workarounds:

  • Applying | Format-List to the command above makes all objects visible, though that obviously changes the display format.

  • Intra-script you could pipe each Select-Object pipeline to Out-Host to force instant, pipeline-specific formatting, but - given that the results are sent directly to the host rather than to the success output stream - this technique precludes further programmatic processing.


Potential future improvements:

GitHub issue #7871 proposes at least issuing a warning if output objects effectively become invisible.

Sign up to request clarification or add additional context in comments.

4 Comments

The unsuspecting programmer might consider this set of default behaviors...surprising.
why does an explicit call to format-table reset the display columns (as shown in stackoverflow.com/questions/79048866). Ie are you saying implicit calls to format-table retain display columns while explicit calls reset them?
@johnvkumpf: explicit Format-* calls result in output of formatting-instruction objects that are applied to their respective pipelines. (As an aside: this is why Format-* calls should only ever be used to produce for-display output.) As such, each such pipeline's for-display formatting is locked in, irrespective of subsequent Format-* calls or subsequent implicitly formatted output. In the absence of Format-* calls, a given pipeline / script as a whole is subject to locking in the formatting based on the first (non-primitive) output object.
@johnvkumpf, I've also added a link to additional background information to the answer (stackoverflow.com/a/55504393/45375).
1

I know this is old. However, since I stumbled upon this, I feel others may too.

Another solution to this display problem that may be more desirable, in some situations, would be to define a class ahead of time and then cast it.

class MyServiceClass {
    [string]$pscomputername
    [string]$name
    [string]$startmode
    [string]$state
}
# ... Code to Gather Results ...
[MyServiceClass[]]$final|select pscomputername, name, startmode, state |where {$_.pscomputername -ne $null}

I didn't test this with the OP's EXACT scenario but I did successfully use this for my own, similar situation. The beauty here is, you can enforce conformance to the data structure and also expand the class to include additional methods, etc.

Comments

1

Here is a quick and dirty (but ugly) fix to this formatting problem. Reset the format-table formatting using this line: @{reset='formatting'} | ft as shown below:

# mock up OP's structure
$final = for($x=0; $x -lt 4; $x++) {
    [PSCustomObject]@{
        "pscomputername" = "pscomputername_val_$x"
        "name"           =           "name_val_$x"
        "startmode"      =      "startmode_val_$x"
        "state"          =          "state_val_$x"
    }
}

$failed_servers = ("serverf1","serverf2")


'replicate the symptom'
$final|select pscomputername, name, startmode, state |where {$_.pscomputername -ne $null}

'----failed_servers-output-beg----'
$failed_servers |select @{N='Failed Servers'; E={$_}}
'----failed_servers-output-end----'

''
''
'fix'
$final|select pscomputername, name, startmode, state |where {$_.pscomputername -ne $null}

# this line fixes by resetting format-table
@{reset='formatting'} | ft

'----failed_servers-output-beg----'
$failed_servers |select @{N='Failed Servers'; E={$_}}
'----failed_servers-output-end----'
    

which produces the following output:

replicate the symptom

pscomputername       name       startmode       state      
--------------       ----       ---------       -----      
pscomputername_val_0 name_val_0 startmode_val_0 state_val_0
pscomputername_val_1 name_val_1 startmode_val_1 state_val_1
pscomputername_val_2 name_val_2 startmode_val_2 state_val_2
pscomputername_val_3 name_val_3 startmode_val_3 state_val_3
----failed_servers-output-beg----
                                                           
                                                           
----failed_servers-output-end----


fix
pscomputername_val_0 name_val_0 startmode_val_0 state_val_0
pscomputername_val_1 name_val_1 startmode_val_1 state_val_1
pscomputername_val_2 name_val_2 startmode_val_2 state_val_2
pscomputername_val_3 name_val_3 startmode_val_3 state_val_3



Name  Value     
----  -----     
reset formatting


----failed_servers-output-beg----

Failed Servers
--------------
serverf1      
serverf2      
----failed_servers-output-end----

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.