Keith Jones
Keith Jones

Reputation: 49

Need help capturing error messages in PowerShell

I am trying to capture the users in the local Administrators group from multiple servers on the network. I originally started with a script I found that does an WMI call, but all of those are blocked on the network.

I finally got the following to work, but When I get an error, it gives the Powershell error, and I would like to simplify it by just giving the short answer such as "Access is Denied" or "WinRM cannot process the request.", etc... I have to turn the report in to users who are non-technical.

I would like to capture the sentence following the following error message : to the period.

If there is a better way of doing this, or the output, I am open to suggestions.

Thanks for your help

$computerlist = get-content "C:\ServerList.txt"


ForEach ($computer in $computerList)
    {
    &{$computer 
    Invoke-Command -ComputerName $computer -ScriptBlock {Get-LocalGroupMember -Group administrators} |
        Select-Object Name,PrincipalSource|Format-table -AutoSize 
    Write-Output " "} 2>&1 >>.\output.txt
    
    }

[XXXXXXX] Connecting to remote server XXXXXXX failed with the following error message : WinRM cannot process the request. The following error occurred while using Kerberos authentication: Cannot find the computer XXXXXXX. Verify that the computer exists on the network and that the name provided is spelled correctly. For more information, see the about_Remote_Troubleshooting Help topic. + CategoryInfo : OpenError: (EPICSOAPT:String) [], PSRemotingTransportException + FullyQualifiedErrorId : NetworkPathNotFound,PSSessionStateBroken

Upvotes: 2

Views: 1198

Answers (2)

mklement0
mklement0

Reputation: 440297

Try the following:

Invoke-Command -ComputerName $computerList -ScriptBlock { 
  Get-LocalGroupMember -Group administrators | Select-Object Name, PrincipalSource 
} 2>&1 | ForEach-Object {
  if ($_ -is [System.Management.Automation.ErrorRecord]) { # an error
    # Extract the substring of interest from the error message and prepend 
    # the computer name.
    # Write the result as a *warning*
    # Note: Warnings are sent to a different output stream,
    #       which in this case means that all warnings print *before* 
    #       Format-Table's output.
    Write-Warning ("$($_.TargetObject ): " + (($_ -split ' : ')[1] -split '\.')[0] + '.')
  }
  else { # an output object, pass it through
    $_ 
  }
} | Format-Table -GroupBy PSComputerName -AutoSize > .\output.txt
  • It is better to pass the list of computers to Invoke-Command in a single call, which causes these computers to be targeted in parallel.

    • Note: The output from the target computers is not guaranteed to arrive in input order, but you could insert a Sort-Object PSComputerName pipeline segment in order to sort the results alphabetically by computer name.
  • As in your approach 2>&1 is used to merge the error stream (2) into the success (1) stream.

  • In the ForEach-Object script block, -is, the type(-inheritance) / interface test operator is used to detect errors (System.Management.Automation.ErrorRecord) instances among the input objects.

    • If such an instance is found, the -split operator is used to extract the part of the error message of interest, and the computer name is prepended.

    • Write-Warning is used to emit the error message in a visually conspicuous manner; Note that warnings are sent to a different output stream (#3), which in this case means that all warnings print before the success output formatted by Format-Table.

    • If you wanted to emit the warnings after, you'd have to collect the strings in an array (or list) and output them in a separate statement afterwards.

  • Format-Table's -GroupBy parameter is used to group the formatted success output objects by the .PSComputerName property, which is implicitly added by Invoke-Command and contains the name of the source computer.

    • Note that both the .PSComputerName property and the other added property, .RunspaceId are included in the table output by default. You can avoid inclusion of PSComputerName by adding the -HideComputerName switch to the Invoke-Command call; however, the RunspaceId property is still included. To avoid that, use Format-Table's -Property parameter to pass an explicit list of property names to display. (Another, more convenient but more expensive option is to use -HideComputerName, and insert a Select-Object * -Exclude RunspaceId command into the pipeline, before the Format-Table call).

Sample output (assumes that successfully retrieved objects have properties one, two, and three; note how the warning about a failed connection prints first):

WARNING: server2: WinRM cannot complete the operation.

   PSComputerName: server1

one two three PSComputerName RunspaceId
--- --- ----- -------------- ----------
  1   2     3 server1        29b0bc1e-cf7f-4d9b-b267-86deff2b7ed0
  4   5     6 server1        ab2ed6a9-3fba-4083-b13e-b43617048ecf

   PSComputerName: server3

one two three PSComputerName RunspaceId
--- --- ----- -------------- ----------
  7   8     9 server3        39b0bc1e-cf7f-4d9b-b267-86deff2b7ed1

Upvotes: 1

Jawad
Jawad

Reputation: 11364

I would suggest using try/catch block with -ErrorAction Stop with the Invoke-Command cmdlet.

ErrorAction Stop forces the error to be thrown as an Exception which can be caught with the try / catch block and process the error message however you want.

try { 
  Invoke-Command -ComputerName test -ScriptBlock { 
    Write-output "hello" 
  } -ErrorAction Stop 
} 
catch { 
  Write-Output "Error Occured: $($_)" 
}

// prints normal output: 
Error Occured: [test] Connecting to remote server test failed with the following error message : WinRM cannot process the request. The following error occurred while using Kerberos authentication: Cannot find the computer test. Verify that the co
mputer exists on the network and that the name provided is spelled correctly. For more information, see the about_Remote_Troubleshooting Help topic.

Upvotes: 1

Related Questions