Get-WmiObject not handling variable as it's typed

0

Im trying to get list of all computers and logged on user for each computer.

I get the list of computers, and when I query for each string returned, I can get the logged in user.

However, when I use the computer name variable to get all users - I get an error saying that my variable is unknown. I tried casting the computer name to string but that didn't help.

By the way it's the same error that comes up when I type in a wrong computer name, so it has something to do with the type of variable #item, When I print the variable out it's correct, but cannot be used inside the loop.

$obj = Get-ADComputer -Filter 'Name -like "L*"' -Properties * | Select -ExpandProperty Name
foreach ( $item in $obj ) { 
    $itemString = $item.ToString()
    $user = Get-WmiObject –ComputerName $itemString –Class Win32_ComputerSystem | Select-Object UserName | select -expandproperty UserName -first 1
    $user = $user.SubString(8)
    write-output "Computer: $itemString Username: $user" 

}
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At N:\Foreach.ps1:4 char:13
+     $user = Get-WmiObject –ComputerName $itemString –Class Win32_Comp ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], COMException
    + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
powershell
active-directory
asked on Stack Overflow Nov 26, 2018 by Viktor H • edited Nov 26, 2018 by Gabriel Luci

1 Answer

3

Your symptom suggests that the offending target computer is simply not available, e.g., due to being disconnected from the network or being powered off.

Your code doesn't deal with this scenario, but is otherwise correct in terms of data types (though given that the .Name property is [string]-typed and you've used Select-Object -ExpandProperty, $obj is an array of strings already, and therefore $item is a single string in the foreach loop, there is no need for a separate $itemString variable obtained with .ToString()).

Note: I'm assuming that the .Name property value is sufficient to identify a computer in the Get-WmiObject call; if it isn't, use Select-Object -ExpandProperty DNSHostName.

Generally, note that Get-WmiObject's -ComputerName parameter is [string[]]-typed, so you can directly pass an array of hostnames to it.

However, regrettably, the [System.Management.Automation.ErrorRecord] instances created by Get-WmiObject when a target computer is unavailable don't contain the offending computer name, so using a single command with later inspection of error output isn't an option (though see the Get-CimInstance alternative at the bottom).

With that in mind, here's a PSv3+ loop-based solution that handles unavailable computers gracefully:

foreach ($computer in (Get-ADComputer -Filter 'Name -like "L*"' -Properties *).Name) {
  $computerInfo = Get-WmiObject -ErrorAction SilentlyContinue -ComputerName $computer -Class Win32_ComputerSystem
  if (-not $?) { # Call failed, analyze the reason.
    if ($Error[0].Exception.HResult -eq 0x800706BA) { # unavailable
      # Merely issue a *warning*. 
      # Alternatively, collect the unavailable names in an array for later use.
      Write-Warning "Computer is unavailable: $computer"
    } else { # unexpected error, pass it through
      Write-Error $Error[0]
    }
  } else { # success
    "Computer: $($computerInfo.Name) Username: $(($computerInfo.UserName -split '\\')[-1])" 
  }
}

By contrast, Get-CimInstance - recommended instead of Get-WmiObject since its introduction in PSv3 - does add origin information to error records, so a single invocation of Get-CimInstance is sufficient - do note that the results are not guaranteed to arrive in the same order in which the computers were specified, but you do benefit from parallel execution.

# Use a single Get-CimInstance call to target all computers and
# quietly collect errors for later analysis.
$computerNames = (Get-ADComputer -Filter 'Name -like "L*"' -Properties *).Name
Get-CimInstance -ErrorAction SilentlyContinue -ErrorVariable errs `
                -ComputerName $computerNames -Class Win32_ComputerSystem |             #`
  ForEach-Object {
    "Computer: $($_.Name) Username: $(($_.UserName -split '\\')[-1])" 
  }

# Analyze the errors that occurred, if any.
$errs | ForEach-Object {
  if ($_.Exception -is [Microsoft.Management.Infrastructure.CimException] -and $_.CategoryInfo.Category -eq 'ConnectionError') {
    Write-Warning "Computer is unavailable: $($_.OriginInfo.PSComputerName)"
  } else { # unexpected error, pass it through
    Write-Error $_
  }
}
answered on Stack Overflow Nov 26, 2018 by mklement0 • edited Nov 26, 2018 by mklement0

User contributions licensed under CC BY-SA 3.0