Error Handling in PowerShell Advanced Function - powershell

I am trying to add error handling to my script so whoever runs it can see errors in a txt file after.
I know i can use try, catch to capture errors, but the problem i have is, i have hundreds of lines of script, some Cmdlets which don't have variables to error capture on, how can i capture the errors for the whole function and export to a txt file to save having to go through each line adding -erroraction stop and put it in a try block with the catch block having an out-file command in it to send to file.
Thanks

When I need logging, I always just throw something like this in the catch block:
$ErrorName = $Error[0].exception.GetType().fullname
$ErrorDescription = $Error[0].exception.Message
"Something went wrong gathering the account names. Cannot continue processing, a fatal error has occurred... `r`n $ErrorName `r`n $ErrorDescription `r`n SCRIPT TERMINATED `r`n " | Out-file -filepath $ErrorLog -append

Related

How to save powershell hyper-v errors to file

I am making a script to turn virtual machines on and off in hyper-v.
Sometimes the Stop-VM command fails and I need to save the bug or reflect it in some way in a log file
I tried putting the command in a trycath but it didn't work.
Command:
Stop-VM $VMapagar
Sometimes the command gives me this error and does not turn off the machine
Stop-VM: Could not stop.
I would like to be able to reflect the failure in some way in a log.txt
Thanks!
Use Try..Catch to trap the error by telling PS to treat it as a terminating error, then process it as you require:
# Rest of your script
Try {
# Run your command, but tell PS to stop if it find an error
# You can explore the effects of the other possible values for -ErrorAction in PS documentation.
Stop-VM $VMpagar -ErrorAction Stop
# If it's got this far, then there can't have been an error so write a success message to console
Write-Host "OK"
}
Catch {
# This code will process if there was an error in the "Try" block
# By default, within the "Catch" block, the "$_" variable contains the error message
Write-Host "Error: $_"
# Write the error to a log file - "`n" tells PS to write a newline before the subsequent text
Add-Content -Path 'c:\temp\log.txt' -Value "`n$_"
# You could stop the script here using "Throw" or "Exit" commands if you want the whole script to stop on ANY error
}
# Your script will continue from this point if you haven't stopped it
Scepticalist's helpful answer shows how to capture a terminating error, by using the common -ErrorAction (-ea) parameter with value 'Stop' in order to promote non-terminating errors (the most common kind) to terminating ones, which allows them to be trapped with a try/ catch / finally statement.
Note that this approach limits you to capturing the first non-terminating error (whereas a single cmdlet call may emit multiple ones), because it - thanks to -ErrorAction Stop - then instantly terminates the statement and transfers control the catch block (where the automatic $_ variable reflects the triggering error in the form of an [ErrorRecord] instance).
Also note that execution continues after a catch block by default - unless you explicitly use throw to re-throw the terminating error (or use a statement such as exit to exit the script).
To capture - potentially multiple - non-terminating errors you have two options:
Redirect them directly to a file, using the redirection operator > with the number of the error stream, 2:
Stop-Vm $vms 2>errs.txt
This sends any errors quietly to file errs.txt; that is, you won't see them in the console. If no errors occur, an empty file is created.
Note: This technique is the only option for directly redirecting an external program's errors (stderr output); however, using redirection 2>&1 you can capture success output (stdout) and errors (stderr) combined, and split them by their source stream later - see the bottom section of this answer.
Use the common -ErrorVariable (-ev) parameter to collect any non-terminating errors in a variable - note that the target variable must be specified without the $:
Stop-Vm $vms -ErrorVariable errs
By default, the errors are still output as well and therefore print to the console (host) by default, but you can add -ErrorAction SilentlyContinue to prevent that. Caveat: Do not use -ErrorAction Ignore, as that will categorically suppress errors and prevent their collection.
You can then inspect the $errs array (list), which is empty if no errors occurred and otherwise contains one or more [ErrorRecord] instances, and send the collected errors to a file on demand; e.g.:
if ($errs) { $errs > errs.txt }
See also:
This answer for information about PowerShell's two fundamental error types.
GitHub docs issue #1583 for a comprehensive overview of PowerShell's surprisingly complex error handling.

Powershell code not exiting after try catch block

I have a code that downloads a file from SharePoint, edits it, uploads it back to SharePoint and finally sends a confirmation email. I have functions for each of the tasks. The code works fine.
However, I want to add error exception for a condition when if the file is open in SharePoint by some user, show error message and exit code. The issue I am experiencing is the code continues to run even if there is an exception. In the below code, the sendMail function is called even when the getSharePointFile function fails.
I have tried $ErrorActionPreference = "Stop" but with that, the catch block is not executed and my custom error MessageBox is not displayed. Thanks in advance.
Here is the relevant code:
function getSharePointFile {
Connect-PnPOnline $SharepointURL -UseWebLogin
Get-PnPFile -Url $fileRelativeURL -Path $localFilePath -FileName $fileName -AsFile -Force
}
function runCatch {
$showMessage = [System.Windows.Forms.MessageBox]::Show($_)
exit
}
try {getSharePointFile}
catch{runCatch}
try {updateAuditResults}
catch{runCatch}
try {uploadToSharePoint}
catch{runCatch}
try {sendMail}
catch{runCatch}
Two issues:
First, you have four independent try catch blocks, and an error handled in one has no impact on the others.
Try something like this:
try {
getSharePointFile
updateAuditResults
uploadToSharePoint
sendMail
}
catch{runCatch}
The first line to generate an error will end the batch of commands and jump to the catch block. The rest of the batch will be skipped.
The second issue you might run into is not all PowerShell cmdlets return errors when they fail. You will need to test yours to verify. Some will just display error text and continue.
More info here: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally
As #Mike Smith said, with the code you provided, it seems better to merge your function calls inside one try catch block.
Depending on your PowerShell version, you also need to add -ErrorAction Stop parameter to trap errors into the catch block (add it for the cmdlets, not the function call)
function getSharePointFile {
Connect-PnPOnline $SharepointURL -UseWebLogin -ErrorAction Stop
Get-PnPFile -Url $fileRelativeURL -Path $localFilePath -FileName $fileName -AsFile -Force -ErrorAction Stop
}

How to make verification process in PowerShell?

I have powershell script. It has a lot of process such are get content, copy file, rename extension file, remove file, append text.
I want to make verification for each process, so I will continue to the next process once I got correct result. For the example, for checking file, I use Test-Path. Is anyone give me idea, which verification is better to use in PowerShell?
Thank you for your advice.
You can add -WhatIf parameter to debug any cmdlet. Also you can handle any errors and exceptions using Try..Catch block.
E.G
Try {
Get-Content "C:\foo.txt" -ErrorAction Stop
}
Catch {
# The statements put here will be executed if any error was caught in try block
}
You can catch specific error using:
Try {
Get-Content "C:\foo.txt" -ErrorAction Stop
}
Catch [System.Management.Automation.ItemNotFoundException] {
# The statements put here will be executed if file not found
# Any exception can be placed in the [] braces
# ErrorAction Common parameter is used to pass the errors to catch block and halt script on error
# You can remove that if you don't want to halt script on error
}

Script stops execution when faces an error

I have a master script master.ps1 which calls two scripts One.ps1 and Two.ps1 like:
&".\One.ps1"
&".\Two.ps1"
When the One.ps1 script has an error, the execution gets stopped without continuing the execution of Two.ps1
How to continue execution of Two.ps1 even if there is an error in One.ps1?
You have to set the $ErrorActionPreference to continue:
Determines how Windows PowerShell responds to a non-terminating
error (an error that does not stop the cmdlet processing) at the
command line or in a script, cmdlet, or provider, such as the
generated by the Write-Error cmdlet.
You can also use the ErrorAction common parameter of a cmdlet to
override the preference for a specific command.
Source.
$ErrorActionPreference = 'continue'
Note: As a best practice I would recommend to first determine the current error action preference, store it in a variable and reset it after your script:
$currentEAP = $ErrorActionPreference
$ErrorActionPreference = 'continue'
&".\One.ps1"
&".\Two.ps1"
$ErrorActionPreference = $currentEAP
#Martin is correct assuming that the success or failure of .\One.ps1 does not impact .\Two.ps1 and if you don't care about logging or otherwise dealing with the error. but if you would prefer to handle the error rather than just continue past it you could also use a Try{}Catch{} block as below to log the error (or take any other action you would like in the Catch{})
Try{
&".\One.ps1"
} Catch {
$error | Out-File "OneError.txt"
}
Try{
&".\Two.ps1"
} Catch {
$error | Out-File "TwoError.txt"
}
Other ways to format this but you get the idea.

How to get the error code when there is error in powershell?

My snippet is something like this:
$msg=Remove-Item -Recurse -Force C:\users\bkp 2>&1
if ($LASTEXITCODE -eq 1)
{
"Encountered error during Deleting the Folder. Error Message is $msg. Please check." >> $LogFile
exit
}
The folder C:\users\bkp does not exist. Even though $msg gives me the error message $LASTEXITCODE is still 0. How do I capture as a flag?
You can use the $? automatic variable to determine the result of the last command. If you need access to the actual error, you can use the $Error automatic variable. The first item in the array is the last error thrown:
Remove-Item -Recurse -Force C:\users\bkp 2>&1
if( -not $? )
{
$msg = $Error[0].Exception.Message
"Encountered error during Deleting the Folder. Error Message is $msg. Please check." >> $LogFile
exit
}
$LASTEXITCODE is strictly for command line programs to return their status. Cmdlets that are built into PS, such as Remove-item return their errors in up to 3 ways. For warnings, they write messages (or other .NET objects) to the "warning stream". In PSv3 there is a straightforward way to redirect that stream to a file: cmdlet blah blah blah 3>warning.out. The second is via the error stream. That stream can be redirected as well ... 2>error.out, or more typically errors are caught with try/catch or trap, or written to a variable with the -ErrorVariable parameter (see help about_commonparameters). The third way is for errors to be "thrown". Unless caught (try/catch or trap), a thrown error will cause the script to terminate. Thrown errors generally are subclasses of the .NET class system.Management.Automation.ErrorRecord. An ErrorRecord provides a lot more information about an error than a return code.
If remove-item fails due to a file not found error, it writes a System.Management.Automation.ItemNotFoundException to the error stream. Using a try/catch you can filter for that specific error or other specific errors from remove-item. If you are just typing in PS commands from the command line you can enter $error[0]|select-object * to get a lot of info on the last error.
You could do this:
try {
Remove-Item -Recurse -Force C:\users\bkp 2>&1
} catch {
# oops remove-item failed. Write warning then quit
# replace the following with what you want to do
write-warning "Remove-item encounter error: $_"
return # script failed
}