I have a powershell script as seen below.
$credential = Get-Credential
$server_list = @('server1','server2')
$script = "[System.Net.Dns]::GetHostName()"
foreach ($server_name in $server_list) {
$result = invoke-command -ComputerName $server_name -ScriptBlock { [System.Net.Dns]::GetHostName() }
write-output "Remote command execution result is $result"
}
What is the problem ?
In the example below, if the server is unreachable or access denied errors are encountered for whatever reason, the value of result is not populated. The value of $result is only populated for a successful execution.
Sometimes, I get the error below.
Connecting to remote server server1 failed with the following error message: WinRM cannot process the request. The following error occured whilst using kerberos authentication: cannot find the computer server1. Verify that the computer exists on the network and that the name provided is spelled correctly.
Now, what I want to achieve is as following, for every server, get the hostname using [System.Net.Dns]::GetHostName()
or any other means, the current time and the powershell version and any error message. Store this up in a custom object etc and return it as part of the script block. This way, when I receive the value of result per server, I can load the results in a database and analyse which servers encountered errors etc.
I looked at How to capture the Return Value of a ScriptBlock invoked with Powershell's Invoke-Command which was a good reference, but what they were doing there is a ping, in my case a ping doesnt really answer the questions of if the credential being used is valid etc.
CodePudding user response:
Invoke-Command has the "common parameters", like "ErrorVariable". So you can use "-ErrorVariable ErrorVariableName" to have the error object assigned to it. Then you can check if ErrorVariableName is null and do something with it. Like this:
$creds = Get-Credential
$server_list = @('server1','server2')
[System.Collections.Generic.List[pscustomobject]]$Output = @();
foreach ($server_name in $server_list) {
$result = invoke-command -ComputerName $server_name -ScriptBlock { [System.Net.Dns]::GetHostName() } -ErrorVariable ErrVar -Credential $creds
if ($ErrVar)
{
$Output.Add([PSCustomObject]@{
Server = $server_name
IsError = $true
Result = $ErrVar
})
}
else
{
$Output.Add([PSCustomObject]@{
Server = $server_name
IsError = $false
Result = $result
})
}
}
# do something with $Output
Also, look into the Error Variable itself, it has some properties like the ErrorCategory, StackTrace, etc... if you do not want the complete StackTrace, just the CategoryInfo.
Alternatively, you can use check try{} catch{}. Since Invoke-Command is not a terminating on an error, you need to have the ErrorActionPreference variable defined to Stop, or use -ErrorAction Stop to make the statement terminating.
Microsoft references:
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_commonparameters?view=powershell-7.2 https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.2