3

Here is a quick little PowerShell blurp that is supposed to get the device's IP address, Service Tag, and MAC address of any active MAC addresses on the device (active meaning there is a connection).

Anyway, if I don't use an if statement, this works perfectly, but if I do, it only executes the first line:

Test-Connection $computername -count 1 | select @{Name="Computername";Expression={$_.Address}},Ipv4Address

If the device is on... and the last line of the device is off.

Write-Host "Device is offline"

Here is my little PowerShell 'script':

$computername = Read-Host 'Enter Computer Name'
$online = Test-Connection -Computername $computername -BufferSize 16 -Count 1 -Quiet
IF ($online -eq "True") {
    Test-Connection $computername -count 1 | select @{Name="Computername";Expression={$_.Address}},Ipv4Address
    Get-WmiObject win32_SystemEnclosure -computername $computername | select serialnumber
    Get-wmiobject -class "Win32_NetworkAdapterConfiguration" -computername $computername |Where{$_.IpEnabled -Match "True"}
} Else {
    Write-Host "Device is offline"
}

Why does this happen? What might I be doing wrong?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Luke Zeimet
  • 31
  • 1
  • 4
  • 2
    NB: Use `$true` instead of `"True"`; the first is a boolean value; the second a string. – JohnLBevan Aug 11 '17 at 14:44
  • If I've understood your question, you're saying that when the computer is online, you're not seeing the serial number or network adapter information; is that correct? – JohnLBevan Aug 11 '17 at 14:45
  • 2
    If so, maybe there's a permissions issue running those WMI queries against the remote machine? What happens if you run those statements on their own? Also, what's your `$ErrorActionPreference` set to (i.e. maybe it's set to `SilentlyContinue`, such that exceptions won't be displayed)? – JohnLBevan Aug 11 '17 at 14:47
  • @JasonSnell no i dont think so... @{Name="Computername"... just changes the word 'Address' to 'Computername' as described in the rest of the line – Luke Zeimet Aug 11 '17 at 14:52
  • @JohnLBevan yes, kinda .... if the script looks like this(with no if condition) $computername = Read-Host 'Enter Computer Name' Test-Connection $computername -count 2 Test-Connection $computername -count 1 | select @{Name="Computer Name";Expression={$_.Address}},Ipv4Address Get-WmiObject win32_SystemEnclosure -computername $computername | select serialnumber Get-wmiobject -class "Win32_NetworkAdapterConfiguration" -computername $computername |Where{$_.IpEnabled -Match "True"} – Luke Zeimet Aug 11 '17 at 14:54
  • btw if you want to format code on Stack Overflow, instead of using `
    ` tags, use the backtick (````) character; or for multi-line code blocks (outside of comments) place 4 spaces before each line.  See https://meta.stackexchange.com/a/22189/199916 for more.
    – JohnLBevan Aug 11 '17 at 14:59
  • @JohnLBevan t^^^ That looks like garbage...sorry It works perfectly.. but my whole point of trying out a IF statement was to have it test the connection to see if its online, then tell me the information about it But for whatever reason it only executes the first line if its online and last line if its offline(if its offline thats all i want it to do) – Luke Zeimet Aug 11 '17 at 14:59
  • 1
    The `If` statement makes sense; I'm asking how it works without that statement to understand the problem; i.e. does the `if` statement cause the problem, or is it unrelated (which seems more likely) / is it just that the code calling `get-wmiobject` isn't producing any output; most likely because of some error that we're not seeing. – JohnLBevan Aug 11 '17 at 15:02
  • the IF statement, is directly related to it not executing the
    get-wmiobject
    lines not working. or so it seems, because if I take out the IF statement and just have 4 lines, it executes them all as it should
    – Luke Zeimet Aug 11 '17 at 15:13

3 Answers3

2

Boolean variables in PowerShell are $true and $false. If should be:

if ($online -eq $true) {

Or

if($online)
G42
  • 9,791
  • 2
  • 19
  • 34
  • In Powershell `$true -eq "true"` is also valid. For whatever reason, a string true or false can be compared against a bool. – Jason Snell Aug 11 '17 at 14:49
  • 3
    `"True"` is a `truthy` value (https://stackoverflow.com/a/38831795/361842), but `"false"` is also truthy (e.g. run: `$true -eq "false" `), so it's best to always use the defined values / be careful when running such checks. – JohnLBevan Aug 11 '17 at 14:50
  • True. It's weird how "false" is also truthy. – Jason Snell Aug 11 '17 at 14:52
  • 3
    @JasonSnell; it's because the word "false" only has meaning to us as people; from a PowerShell comparison perspective, it casts this a a boolean to perform a comparison, and a non-zero-length string casts to true by definition. `"false" -eq $true` gives false because it converts `$true` to a string, `"true"`, and so `"false" -eq "true"` evaluates to false. To avoid such confusion, always use the correct types when making comparisons. – JohnLBevan Aug 11 '17 at 14:55
2

Try this:

$computername = Read-Host 'Enter Computer Name'
$online = Test-Connection -Computername $computername -BufferSize 16 -Count 1 -Quiet
IF ($online -eq $true) {

    Write-Host "Device is online"
    Test-Connection $computername -count 1 | select @{Name="Computername";Expression={$_.Address}},Ipv4Address

    try {
        [PSObject[]]$systemEnclosures = Get-WmiObject win32_SystemEnclosure -computername $computername -ErrorAction Stop
        Write-Host "Found $($systemEnclosures.Count) System Enclosures"
        $systemEnclosures | select serialnumber
        [PSObject[]]$NetworkAdapterConfiguration = Get-wmiobject -class "Win32_NetworkAdapterConfiguration" -computername $computername -ErrorAction Stop
        Write-Host "Found $($NetworkAdapterConfiguration.Count) Network Adapter Configurations"
        $NetworkAdapterConfiguration = $NetworkAdapterConfiguration | Where{$_.IpEnabled}
        Write-Host "Found $($NetworkAdapterConfiguration.Count) IP Enabled Network Adapter Configurations"
        $NetworkAdapterConfiguration
    } catch {
        Write-Host "An Error Occurred"
        Write-Host $_.ToString() #show exception info
    }
} Else {
    Write-Host "Device is offline"
}

NB: I'm not suggesting you keep this code in your final script; just use this to understand what's happening behind the scenes.

Per comments; use $true instead of "true", as though both are truthy, using the wrong type will lead to a false understanding of the language / some really odd bugs down the line where you find that lines like if($true -eq "false") {write-output "Well, this is unusual"} will cause some odd behaviour.

Also you may wish to look into replacing Write-Host with Write-Output for any logical return values, or Write-Verbose/Write-Debug for any informative/investigation outputs; then call the code with the appropriate switches / preferences... but that's unrelated to your issue. More on that is in Write-Host Considered Harmful.


Update

Per comments, the issue you're seeing is a bug gotcha: https://github.com/PowerShell/PowerShell/issues/4552

If the data coming out of this code is just to be displayed in the console, you can avoid this issue by explicitly calling the Format-Table command:

$computername = Read-Host 'Enter Computer Name'
IF (Test-Connection -Computername $computername -BufferSize 16 -Count 1 -Quiet) {
    Test-Connection $computername -count 1 | select @{Name="Computername";Expression={$_.Address}}, 'Ipv4Address' | Format-Table
    Get-WmiObject win32_SystemEnclosure -computername $computername | select serialnumber | Format-Table
    Get-wmiobject -class "Win32_NetworkAdapterConfiguration" -computername $computername | Where{$_.IpEnabled} | Format-Table
} Else {
    Write-Host "Device is offline"
}

If you need the output to go to the pipeline for consumption elsewhere, all's good as it is (i.e. without the format-table piece); the objects are being correctly written to the pipeline; the issue is simply that when it comes to displaying all of the results together, the first object causes PowerShell to create columns ComputerName and Ipv4Address, and PowerShell subsequently attempts to display those properties of the following objects, despite those not having such properties. That said, this could be improved by putting the different object types into different properties of a custom object for easy reference. For example,

$computername = Read-Host 'Enter Computer Name'
If (Test-Connection -Computername $computername -BufferSize 16 -Count 1 -Quiet) {
    (new-object -TypeName PSObject -Property @{
        ConnectionTest = Test-Connection $computername -count 1 | select @{Name="Computername";Expression={$_.Address}}, 'Ipv4Address'
        SystemEnclosures = Get-WmiObject win32_SystemEnclosure -computername $computername | select serialnumber
        NetworkAdapterConfigs = Get-wmiobject -class "Win32_NetworkAdapterConfiguration" -computername $computername | Where{$_.IpEnabled}
    })
} Else {
    Write-Host "Device is offline"
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • Did this work for you? you got the Sevice Tag and Mac addresses? – Luke Zeimet Aug 11 '17 at 15:29
  • @LukeZeimet; Originally I'd not tried it. I've now added type definitions to ensure that when 1 object is returned, the objects are still arrays, so still have a `Count` property: e.g. `[PSObject[]]$systemEnclosures`. – JohnLBevan Aug 11 '17 at 15:47
  • That resolves the count issue. Regarding returned values, yes I get results; but like you say, they don't display (regardless of the `if` statement though; it seems the issue's caused by running after the `Test-Connection` line which follows the `if` statement. My guess is that the PS is trying to put the output into the same columns it created for `Test-Connection` and thus failing... – JohnLBevan Aug 11 '17 at 15:50
  • FYI: If you add `Format-List` after the `Test-Connection` statement (i.e. so it doesn't create a table) the later commands work, which supports this theory. Likewise if you explicitly use a `format-table` after `select serialnumber`. – JohnLBevan Aug 11 '17 at 15:53
  • For even more fun; try `$systemEnclosures | select @{Name="Computername";Expression={$_.SerialNumber}}` in place of `$systemEnclosures | select SerialNumber`; this will show you that it's using the previous output's table. – JohnLBevan Aug 11 '17 at 15:55
  • Thanks to mklement0 on GitHub for pointing to the underlying cause, as explained by @PetSerAI here: https://stackoverflow.com/a/34858911/361842 – JohnLBevan Aug 14 '17 at 07:58
  • 1
    John: My apologies for leading us down the wrong path initially: the behavior you're describing is actually _as designed_, I think, as I've since explained [here](https://stackoverflow.com/a/45705068/45375). The asynchronous behavior of implicit `Format-Table` explained in @PetSerAl's answer can also be a problem, but it is unrelated to this issue. – mklement0 Aug 16 '17 at 14:05
  • 1
    No worries, thanks @mklement0. Will continue the discussion on GitHub as though I agree with your comments, I think the behaviour does have some issues (e.g. it's a gotcha for the unwary). – JohnLBevan Aug 16 '17 at 14:38
  • Good idea: It's definitely a gotcha, and definitely worth documenting, although I suggest you no longer call it a _bug_ in your answer. – mklement0 Aug 16 '17 at 14:45
  • 1
    @mklement0 good point; amended (left `bug` with a strikethrough to keep a bit of historical context) – JohnLBevan Aug 16 '17 at 14:54
1

Test-Connection -Quiet returns a Boolean, not a string. Try if ($online) or if ($online -eq $true) if you want to be explicit.

https://learn.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Management/Test-Connection?view=powershell-5.1

Michael Flanakin
  • 472
  • 1
  • 5
  • 18