PowerShell ErrorRecord - powershell

when I run a script and it throws an exception, another script is run (log script).
I have to give the current error object to the log script.
try
{
New-Item -Path 'E:\test.txt' -ItemType 'File' -ErrorAction Stop
}
catch
{
Write-Log -ErrorObject $Error
}
How can I access the error object of the first script directly from the second script?
The best way would be to use the .Net framework (System.Management.Automation.ErrorRecord).
I want to output a complete error message.

Related

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
}

Powershell: cannot hide 'Access is denied' error on 'Remove-Item'

I would like to keep a Remove-Item instruction quiet, exception or not. I'm running below command in a script to delete a certificate:
Remove-Item $store\$thumbprint
If I run the script as local Admin, fine... it keeps quiet and the file is deleted.
If however I run it as unpriviledged user, I get an 'Access in denied' error as expected, but I would like to keep this quiet in any case.
I've tried the following:
$output = (Remove-Item $store\$thumbprint)
# or...
try{Remove-Item $store\$thumbprint} catch{}
# or...
Remove-Item $store\$thumbprint -ErrorAction SilentlyContinue
But I always get the error/exception
displayed on the console.
By default, a non-terminating error is generated by Remove-Item and it adds an error to the $Error variable without throwing an exception. To see what Windows PowerShell will do when a non-terminating error arises, look at the value of the $ErrorActionPreference variable (its default value is Continue).
The Access to the path '…' is denied is an example of such a non-terminating error so you can use ErrorAction parameter which overrides the value of the $ErrorActionPreference variable for the current command:
Remove-Item $store\$thumbprint -ErrorAction SilentlyContinue
On the other side, $ErrorActionPreference and the ErrorAction parameter don't affect how PowerShell responds to terminating errors that stop cmdlet processing. So if we are not sure whether an error is terminating or not then it's safe to handle any error the Try-Catch-Finally blocks using -ErrorAction Stop as follows:
try {
Remove-Item $store\$thumbprint -ErrorAction Stop
} catch {
### A Catch block can include commands for tracking the error
### or for recovering the expected flow of the script
}

How to catch error from Remove-Item and emit a warning instead?

In a post-deployment script used in a continuous integration pipeline (Azure DevOps), I'm removing old files.
Basically, it's a PowerShell script that removes every release folder but the current one in the deployment directory.
Sometimes, the Remove-Item fails for some reason (old file still opened by someone one the deplyoment machine, for instance)
It's not a big deal. I don't want an error saying my whole deployment failed because of this. However, I want a warning, so I'm aware that it happened.
For instance (MCVE):
Remove-Item INEXISTENT_FILE
Problem : it causes an error.
Attempt 1 :
Remove-Item INEXISTENT_FILE -ErrorAction SilentlyContinue
Problem : It removes the Error completely, that's not what I want (I want a warning)
Attempt 2 : I tried to use ErrorVariable as recommended here : https://devblogs.microsoft.com/powershell/erroraction-and-errorvariable/
Remove-Item INEXISTENT_FILE -ErrorAction SilentlyContinue -ErrorVariable $removeItemError
if ($removeItemError) {
Write-Warning "Warning, something failed!"
}
Problem : it doesn't work, it doesn't show the if part. If I remove "SilentlyContinue" error action, it just emits an error, and in any case never goes into the if part.
Attempt 3 : I tried to use also Try Catch block as proposed here : PowerShell -ErrorAction SilentlyContinue Does not work with Get-ADUser
Try {
Remove-Item INEXISTENT_FILE
}
Catch {
Write-Warning "Warning, something failed!"
}
Problem : it never goes into the catch block either (!?)
Anyone has another option to show a warning instead of an error if Remove-Item fails ?
The error produced by Remove-Item is considered 'non-terminating', which means that it is ignored by 'try/catch'. To force it to become 'visible' to 'try/catch' use the ErrorAction parameter:
Remove-Item INEXISTENT_FILE -ErrorAction Stop
Alternatively, you can change this at the script level (i.e. for all subsequent commands) like this:
$ErrorActionPreference = 'Stop'
The error message can be retrieved using $_.Exception.Message or $error[0]

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
}

How to confirm completion of previous command in powershell

I have a simple powershell script that gets ran daily to compress and move some log files. How can i test that the command completes successfully before deleting the original log file.
set-location $logpath1
& $arcprg $pram $dest_file $source_file
Move-Item $dest_file $arcdir
If the Move-Item completes ok i want to remove-item $source_file
The completion status of the previous command can be accessed via the special variable $?.
Note that this works best with non-terminating errors (like you would get from Move-Item). Terminating errors are the result of a direct throw or an exception getting thrown in .NET and they alter the flow of your code. Best to use a trap or try/catch statement to observe those type of errors.
One other thing to watch out for WRT $? and console exes is that PowerShell assumes an exit code of 0 means success (i.e. $? is set to $true) and anything else means failure ($? set to $false). Unfortunately not all console exe's observe that exit code convention e.g. there may be multiple success codes and a single failure code (0). For those exes that don't follow the exit code rules, use $LastExitCode as pointed out in the comments to determine success or failure.
Depending on how parnoid you are and what component you are using for archiving, you can check the archive to confirm the file exixts. We use DotNetZip component to zip our archive log files (http://dotnetzip.codeplex.com/).
$zipFileObj = new-object Ionic.Zip.ZipFile($zipName);
[void] $zipFileObj.UpdateFile( "$fileName", "" ) # adds file if doesn't already exist
trap #catch an zip errors and Stop processing
{
write-error "Caught a system exception. Execution stopped"
write-error $("TRAPPED: " + $_.Exception.Message);
exit
}
if ( $zipFileObj.ContainsEntry( $fileName) )
{
remove-item $pathFile # delete file from file-system
}
else
{
# throw error
}