Aug 14 2023 08:33 PM
I have a Try..Catch in my code where the script logs into the CIMC of our Cisco UCS servers to get hardware info.
I found something which I cannot explain and need your guidance please. Here's the Catch code:
Catch [System.Exception]
{
$_.Exception.pstypenames;
# Did not connect due to connection issue.
Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red
Switch ($PSItem.Exception.ToString())
{
{$_.contains("Unable to connect")}
{
Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Connection_Not_Available"
}
{$_.contains("Authentication failed")}
{
Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Authentication_Failed"
}
Default
{
Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Unknown_Error"
}
}
The line "$_.Exception.pstypenames;" shows the following values:
System.Exception
System.Object
The line "Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red" shows the following:
[MyServerName/172.72.24.82] Error: Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.
This is OK so far- the CIMC portal on this server is not accessible and not pingable and this is the expected result.
The Switch statement evaluates this statement to be true:
{$_.contains("Unable to connect")}
Again, as expected.
Now this is where my problem starts. This line "Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red" shows a blank value.
Does $PSItem have a "shelf-life" where it's contents will fade away if not consumed?
Here's some testing I did at the "Catch [System.Exception]" line:
[DBG]: PS C:\Users\jmilano>> $PSItem
Connect-IMC : Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.
At C:\Users\jmilano\Documents\PowerShell_Scripts\UCS\Get-RackServerInventory.ps1:223 char:30
+ ... IMCHandle = Connect-IMC -Name $RackServerIMCIP -Credential $CurrentCr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Connect-Imc], Exception
+ FullyQualifiedErrorId : Cisco.Imc.Cmdlets.ConnectImc
[DBG]: PS C:\Users\jmilano>> $PSItem.exception
Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.
[DBG]: PS C:\Users\jmilano>> $PSItem.FullyQualifiedErrorId
Cisco.Imc.Cmdlets.ConnectImc
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.GetBaseException()
Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.GetHashCode()
47918467
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.connectionid
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.errorcategory
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.errorid
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.HResult
-2146233088
[DBG]: PS C:\Users\jmilano>> $PSItem.exception.InnerException
[DBG]: PS C:\Users\jmilano>>
Here's some testing I did at the last Write-Host command:
[DBG]: PS C:\Users\jmilano>> $PSItem
System.Exception: Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.
[DBG]: PS C:\Users\jmilano>> $PSitem.exception
[DBG]: PS C:\Users\jmilano>> $PSItem.FullyQualifiedErrorId
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.GetBaseException()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $PSItem.Exception.GetBaseException()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.GetHashCode()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $PSItem.Exception.GetHashCode()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.ConnectionId
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.ErrorCategory
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.ErrorId
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.HResult
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.InnerException
[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.Message
[DBG]: PS C:\Users\jmilano>>
It seems that the$PSItem loses some or all of its properties as soon as the Switch command is executed. Is this correct? Do I have to save the $PSItem properties as soon as the Catch is executed?
Aug 15 2023 04:52 PM
Solution
Hi, Julian.
I've used the following code to reproduce your symptoms, after which I get into the issues.
#region Test session-specific stuff. Remove for production use.
Clear-Host;
Remove-Variable -Name @("StringLabel", "Connected", "ErrorPropertyValue") -ErrorAction:SilentlyContinue;
$StringLabel = "Foo";
#endregion
try
{
dir B:\ -ErrorAction:Stop;
}
Catch [System.Exception]
{
$_.Exception.pstypenames;
# Did not connect due to connection issue.
Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red
Switch ($PSItem.Exception.ToString())
{
{$_.contains("Unable to connect")}
{
Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Connection_Not_Available"
}
{$_.contains("Authentication failed")}
{
Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Authentication_Failed"
}
Default
{
Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Unknown_Error"
}
}
}
There's two errors in this output:
Firstly, $_ and $PSItem are precisely the same thing (the former being an alias of the latter), so you can get away with using just one or the other in code.
Next, there is indeed a "shelf life" - for multiple reasons:
What you're seeing is the effect of the latter, where the scope changes from the catch block to the switch statement/block (which itself is a subset of the broader catch block).
Outside of the switch block, $_ still equals your exception, but inside of the switch block, it equals the string emitted by the switch expression of:
$PSItem.Exception.ToString()
This explains the second error being thrown from the catch block, as a System.String object clearly doesn't have a property named "Exception" (and therefore no ToString() method below that, either), which is reflected in that second error message.
The best thing to do when working with the pipeline automatic variable in a complex block (irrespective of whether it's a catch block or any other) is to assign it to a variable upon entry to the block - unless you're not using nested references (you don't want to make things messy if you don't have to.)
The variable is usable from anywhere inside of that specific block, independent of the pipeline variable changing for whatever reason.
Taking this simple step of assigning the pipeline variable upon entry gives you this instead:
#region Test session-specific stuff. Remove for production use.
Clear-Host;
Remove-Variable -Name @("StringLabel", "Connected", "ErrorPropertyValue") -ErrorAction:SilentlyContinue;
$StringLabel = "Foo";
#endregion
try
{
dir B:\ -ErrorAction:Stop;
}
Catch [System.Exception]
{
$e = $_;
# $e.Exception.pstypenames;
# Did not connect due to connection issue.
Switch ($e.Exception.ToString())
{
{$e.Exception.Message.Contains("Unable to connect")}
{
Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Connection_Not_Available"
}
{$e.Exception.Message.Contains("Authentication failed")}
{
Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Authentication_Failed"
}
Default
{
Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
$Connected = $False
$ErrorPropertyValue = "Unknown_Error"
}
}
}
$Connected;
$ErrorPropertyValue;
Which is probably what you wanted.
If you want to get into the nuts and bolts of the topics this post touches on, here you go:
Cheers,
Lain
Aug 15 2023 04:59 PM