Reputation: 49
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
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.
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.
.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
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