Powershell verbose output for chained exceptions - powershell

I'm new to powershell and troubleshooting an issue with one of our custom cmdlets. By default, all exceptions thrown within the cmdlet have minimum information, no stack trace and no info on chained exceptions. Is there a way to enable verbose output of exceptions?

the $error collection contains a live list of all unhandled exceptions thrown in the current session. The last exception is at $error[0]. A good technique to do something like this to capture the error as soon as possible:
ps> invoke-something
error: ...
ps> $e = $error[0]
Explore $e with get-member.

I've used this technique to get nested error objects:
$error[0]|format-list -force

Here's a neat function that I've stolen from someone on the 'net :). I have it in my profile and will happily spread it further:
#Get detailed information on an error
function Resolve-Error ($ErrorRecord=$Error[0])
{
$ErrorRecord | Format-List * -Force
$ErrorRecord.InvocationInfo |Format-List *
$Exception = $ErrorRecord.Exception
for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException))
{ "$i" * 80
$Exception |Format-List * -Force
}
}

Related

Foreach write output when one or more fail

Currently I have this script:
$AdminSiteURL="https://contoso-admin.sharepoint.com"
$SiteURL=""
$UserID="klaas.hansen#contoso.nl"
$sitecollectios = #("https://contoso.sharepoint.com/sites/Extranet","https://contoso.sharepoint.com/sites/contoso","https://contoso.sharepoint.com/sites/Projecten","https://contoso.sharepoint.com/sites/PFO","https://contoso.sharepoint.com/sites/beheer","https://contoso.sharepoint.com/sites/Intranet")
#Get Credentials to connect
$Cred = Get-Credential
#Connect to SharePoint Online Admin Site
Connect-SPOService -Url $AdminSiteURL -Credential $cred
foreach ($collectie in $sitecollectios)
{
Get-SPOUser -Site $collectie -LoginName $UserID
}
When it can't find the user however the foreach shows an error. which is obvious. Is it possible to when it can't find the user in one or more of the site collections it shows me an error in write output, but not every time it can't find it. so for example it can't find the user in 3 site collections it only has to show me once that it can't find it.
Mathias R. Jessen's solution is effective, but there's a simpler and faster alternative:
The -ErrorVariable common parameter has a rarely seen feature that allows you to append the errors collected during command execution to an existing variable, simply by prepending + to the target variable name, which enables the following solution:
foreach ($collectie in $sitecollectios)
{
# Using built-in alias parameter names, you could shorten to:
# Get-SPOUser -ea SilentlyContinue -ev +errs ...
Get-SPOUser -ErrorAction SilentlyContinue -ErrorVariable +errs -Site $collectie -LoginName $UserID
}
# Print the errors that occurred.
$errs
-ErrorAction SilentlyContinue silences the errors (do not use Ignore, as that would suppress the errors altogether).
-ErrorAction +errs collects any error(s) in variable $errs, by either appending to the existing collection in $errs or by creating one on demand.
Note how the variable name, errs must not be prefixed with $ when passed to -ErrorAction.
Afterwards, you can examine the $errs collection to see for which users the call failed.
$errs (like the automatic $Error variable that collects errors session-wide) will be an array-like object (of type System.Collections.ArrayList) containing System.Management.Automation.ErrorRecord objects.
The simplest way to get the error message (short of simply printing $errs as a whole to the screen) is to call .ToString() on an error record; e.g., $errs[0].ToString(); to get all error messages in the collection, use $errs.ForEach('ToString'). There is the .Exception.Message property, but that can situationally be overruled by .ErrorDetails.Message when the error prints to the display; .ToString() applies this logic automatically.
The .TargetObject property tells you the target object or input that triggered the error; I can't personally verify what Get-SPOUser does, but it would make sense for the -LoginName argument of a non-existing users to be reflected there; this is how it works analogously with Get-Item -Path NoSuch, for instance: in the resulting error record, .TargetObject contains 'NoSuch' resolved to a full path.
Catch the errors inline and then report on number of errors caught at the end:
$FailedCollections = #()
Connect-SPOService -Url $AdminSiteURL -Credential $cred
foreach ($collectie in $sitecollectios)
{
try{
Get-SPOUser -Site $collectie -LoginName $UserID -ErrorAction Stop
}
catch{
$FailedCollections += $collectie
}
}
if($FailedCollections.Count -ge 1){
Write-Error "Errors encounted in $($FailedCollections.Count) collections: [$($FailedCollections -join ', ')]"
}

Can I use -ErrorVariable while using Out-String in Powershell

I'm trying to capture the output of a command into output variable in Powershell. The output of the command is in table format which is why I'm using Out-String. Now I need to suppress the error messages which is why I use an error variable to store the error messages. I tried the following things but none of them suppress my error and error gets displayed on screen.
$output = $esxcli.network.firewall.ruleset.allowedip.list() | Out-String -ErrorVariable myErr
$output = $esxcli.network.firewall.ruleset.allowedip.list() -ErrorVariable myErr | Out-String
$output = $esxcli.network.firewall.ruleset.allowedip.list() | Out-String 2>&1
$output = $esxcli.network.firewall.ruleset.allowedip.list() 2>&1 | Out-String
Is there a way where I can suppress the errors while using Out-String in a simple way (nothing like try-catch)?
If your .list() method is throwing an exception you have two options:
Set your $ErrorActionPreference variable to 'SilentlyContinue' or 'Ignore'
$ErrorActionPreference = 'SilentlyContinue'
Wrap your method call in a try/catch block
try
{
$output = $esxcli.network.firewall.ruleset.allowedip.list() | Out-String
}
catch
{
"Error thrown! $PSItem"
}
Do note if your method call is throwing a terminating error, you do not have a workaround.
To add a bit more detail to your original question about -ErrrorVariable. -ErrorVariable is a common parameter which you get by default when adding the CmdletBinding attribute to the beginning of a function.
The .list() is a .Net method not a PowerShell function and thus -ErrorVariable does not work with it. You can however write a short function to wrap the .Net method in a small PowerShell function if you would like to use it a lot and would like to leverage some of PowerShell's awesomeness with that method.
Example:
function Get-ESXAllowedIPList {
[CmdletBinding()]
param($esxcli)
return $esxcli.network.firewall.ruleset.allowedip.list()
}
You can then use it like this:
Get-ESXAllowedIPList -ErrorAction SilentlyContinue -ErrorVariable ESXAllowedIPListErrors | Out-String

Non-terminating Error Handling - Difference between if $Error.count and $ErrorActionPreference = 'stop'

I need to handle a non-terminating error in a powershell script. What is the most efficient way to this?
To set $ErrorActionPreference variable to stop and use try/catch
$ErrorActionPreference = 'stop'
try{
functionThatCanFail
}catch{
#Do Stuff
}
Or to clear $Error variable and then evaluate if it is populated
$Error.Clear()
functionThatCanFail
if( $Error.Count -ne 0){
#Do Stuff
}
I would add [CmdletBinding()]to your function and put it inside a try/catch.
Because of the CmdletBinding you are now able to call your function with the parameter -ErrorAction Stop.
The other suggestion with the $ErrorActionor $Error.Clear()would also work but is not a 'clean' way.
function functionThatCanFail
{
[CmdletBinding()]
param
(
$Param1
)
#Do stuff
}
try
{
functionThatCanFail -ErrorAction Stop
}
catch
{
#Error case
}
The simplest approach is to use the -ErrorVariable / -ev common parameter, which records all non-terminating errors reported by a given cmdlet in a designated variable.
Note, however, that this only works with cmdlets and advanced functions / scripts, because only they support common parameters - see Patrick's helpful answer for how to define your own function as an advanced function.
# Provoke a non-terminating error and silence it,
# but store it in custom variable $err via common parameter -ErrorVariable
Get-Item /NoSuchFile -ErrorAction SilentlyContinue -ErrorVariable err
if ($err) { # if a / a least one non-terminating error was reported, handle it.
"The following non-terminating error(s) occurred:`n$err"
}
That way, the error analysis is command-scoped, without having to record the state of or alter the state of the session-level $Error collection.
Note, however, that you cannot handle terminating errors this way.
For a comprehensive overview of PowerShell error handling, see here.

The way to differ thrown and swallowed exception and an error written to the pipeline

Given the function that receives Powershell code as a text and execute it:
function Exec
{
param(
[string]$expression
)
$Error.Clear()
$Result = $Null
try
{
$Result = Invoke-Expression $expression
}
catch {
$Result = $_
$Error.Clear()
}
finally
{
if($Error.Count -gt 0)
{
$Result = $Error[0]
}
}
Write-Host $Result
}
Three types of expressions might be passed to this function:
$expression1 = "try {throw 'some exception'}catch {'some result'}"
$expression2 = "throw 'another exception'"
$expression3 = "Write-Error 'something went wrong...'"
When I pass the first one I would like "some result" to be printed, however I'll see "some exception" instead of this, because $result was rewritten in Finally block.
Is there any way to print actual result, but not the last error here and not break down working of this code with other two types of expressions?
It would make things more clear if OP specified desired behavior in the various cases you are trying to handle, rather than giving a set of 3 (what are essentially) unit tests. Having 3 unit tests is problematic because: 1) the person answering the question has to reverse engineer the desired behavior by first guessing at what the 3 unit tests are supposed to yield and second by guessing what behavior that result implies and 2) 3 unit tests aren't enough to cover the gamut of behaviors (if I'm guessing the intended behaviors correctly).
The gamut of behaviors I'm guessing are: 1) expression executes without errors, 2) expression executes with one non-terminating error, 3) expression executes with multiple non-terminating errors, 4) a terminating error, 5) a single non-terminating error and a terminating error, 6) multiple non-terminating errors and a terminating error
With that said how about this:
function Exec
{
param(
[string]$expression
)
# Not usually necessary to clear $error but could be done here if
# really necessary, by uncommenting next line
# $error.clear()
$preErrorCount = $postErrorCount = $Error.count
$Result = $Null
# If $expression executes without errors, then $result is value of
# $expression. If a terminating error occurs, then $result is the
# ErrorRecord that caused the termination. If only non-terminating error(s)
# occurred then $result is $error[0] (most recent non-terminating error)
try
{
$Result = Invoke-Expression $expression
}
catch {
$Result = $_
$postErrorCount = $Error.count
}
if($Error.Count -ne $preErrorCount) {
# Either (or both) terminating and/or non-terminating error occurred
if ($postErrorCount -ne $preErrorCount) {
# Terminating error occurred. $Result is already set correctly, nothing to do
} else {
# Only non-terminating error(s) occurred, copy most recent
$result = $error[0]
}
}
# In the OP $Result is not returned by function, only written to host
# Preserving that behavior
Write-Host $Result
# NOTE: in OP script does not clear $error if non-terminating error occurs
# but clears it if terminating error occurs. Probably a bug.
# Uncomment following if $error should be cleared on both terminating and
# non-terminating errors before returning to caller. (In general callers
# of PS cmds shouldn't assume $error is cleared or preserved across the call)
# $error.clear()
}
This function works as I want:
function Exec
{
param(
[string]$expression
)
$Error.Clear()
$Result = $Null
try
{
$Result = Invoke-Expression $expression -ErrorAction Stop
}
catch {
$Result = $_
}
Write-Host $Result
}

Try method in powershell

So I want to build a try method into my powershell script below. If I am denied access to a server, I want it to skip that server. Please help..
[code]$Computers = "server1", "server2"
Get-WmiObject Win32_LogicalMemoryConfiguration -Computer $Computers | Select-Object `
#{n='Server';e={ $_.__SERVER }}, `
#{n='Physical Memory';e={ "$('{0:N2}' -f ($_.TotalPhysicalMemory / 1024))mb" }}, `
#{n='Virtual Memory';e={ "$('{0:N2}' -f ($_.TotalPageFileSpace / 1024))mb" }} | `
Export-CSV "output.csv"[/code]
Try/catch functionality is built-into PowerShell 2.0 e.g.:
PS> try {$i = 0; 1/$i } catch { Write-Debug $_.Exception.Message }; 'moving on'
Attempted to divide by zero.
moving on
Just wrap you script in a similar try/catch. Note you could totally ignore the error by leaving the catch block empty catch { } but I would recommend at least spitting out the error info if your $DebugPreference is set to 'Continue'.
You can simply suppress errors with the ErrorAction parameter:
Get-WmiObject Win32_LogicalMemoryConfiguration -Computer $Computers -ErrorAction SilentlyContinue | ...
You can use trap to replicate Try/Catch, see http://huddledmasses.org/trap-exception-in-powershell/ or http://weblogs.asp.net/adweigert/archive/2007/10/10/powershell-try-catch-finally-comes-to-life.aspx for examples.
Use a filter function? Like this tutorial explains.
He passes a list of computers to his pipeline - first it tries to ping each one, and then only passes the ones that respond to the next command (reboot). You could customize this for whatever actual functionality you wanted.