foreach continue is not working when inside RunWithElevatedPrivileges - powershell

Below code keep executing when error arises,
foreach($url in Get-Content $urlsDir)
{
try
{
// do something
// declare X
}
catch
{
// write host or soemthing with exception
continue
}
finally
{
// dispose X
}
}
but when I put this code in RunWithElevatedPrivileges, it completely stops on first error and won't continue execution,
[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges({
# Iterate through all webs in a text file
foreach($url in Get-Content $urlsDir)
{
try
{
// do something
// declare X
}
catch
{
// write host or soemthing with exception
continue
}
finally
{
// dispose X
}
}
});

It could be related to whether or not you've specified an ErrorAction, I'm not sure how this relates to try catch though. I have done something similar where my foreach would not stop unless explicitly stated.
Basically you need to specify what should happen if an error occurs on each call, as so
Call-Something $SomeParam -ErrorAction Stop
and unless you specify it as above for each call or in the start of your script as below errors can be silently ignored.
// at the start of your script
$ErrorActionPreference = "Stop"
For more information you can read about ErrorAction in powershell here for instance
https://blogs.msdn.microsoft.com/kebab/2013/06/09/an-introduction-to-error-handling-in-powershell/
And according to msdn these are the valid values:
Stop: Displays the debug message and stops executing. Writes an error to the console.
Inquire: Displays the debug message and asks you whether you want to continue. Note that adding the Debug common parameter to a command--when the command is configured to generate a debugging message--changes the value of the $DebugPreference variable to Inquire.
Continue: Displays the debug message and continues with execution.
SilentlyContinue: No effect. The debug message is not (Default) displayed and execution continues without interruption.

Related

PDFCreator to convert pdf to txt in PowerShell

Updated Comment: I'm attempting to use PDFCreator to convert pdf files into txt files via PowerShell but it still doesn't seem to be working.
Any help is appreciated!
$PDFCreator = New-Object -ComObject PDFCreator.JobQueue
$PDF = 'C:\Users\userName\Downloads\SampleACORD.pdf'
$TXT = 'C:\Users\userName\Downloads\SampleACORD.txt'
try {
$PDFCreator.initialize()
if($PDFCreator.WaitForJob(5)){
$PJ = $PDFCreator.NextJob
}
if($PJ){
$PJ.PrintFile($PDF)
$PJ.ConvertTo($TXT)
}
} catch {
$_
Break
}
finally {
if($PDFCreator){
$PDFCreator.ReleaseCom()
}
}
You are getting that because $PJ is $null. NextJob isn't returning anything.
To guard against this, WaitForJob(int) returns a bool, $true if a job arrived and $false if not, so you should know after WaitForJob completes whether there is a job to get or not:
if( $PDFCreator.WaitForJob(5) ){
$PJ = $PDFCreator.NextJob
$PJ.allowDefaultPrinterSwitch('C:\Users\userName\Downloads\SampleACORD.txt', $true)
$PJ.ConvertTo($TXT)
} else {
# Handle the no jobs case here
}
You could also do a null check against $PJ before trying to call $PJ.allowDefaultPrinterSwitch:
if( $PJ ){
$PJ.allowDefaultPrinterSwitch('C:\Users\userName\Downloads\SampleACORD.txt', $true)
$PJ.ConvertTo($TXT)
}
Here is some more information on the PDFCreator.JobQueue API, which you may find useful.
To address your issue in the comments, where the file is not being produced, this page of the documentation explains the logical flow of how the conversion process should work:
Call the Initialize() method with your COM Object.
Call WaitForJob(timeOut) if you are waiting for one print job to get in the queue. The parameter timeOut specifies the maximum time the queue waits for the print job to arrive.
Now you are able to get the next job in the queue by calling the property NextJob.
Setup the profile of the job with the method SetProfileByGuid(guid). The guid parameter is used to assign the appropriate conversion profile.
Start the conversion on your print job with ConvertTo(path). The path parameter includes the full path to the location where the converted file should be saved and its full name.
The property IsFinished informs about the conversion state. If the print job is done, IsFinished returns true.
If you want to know whether the job was successfully done, consider the property IsSuccessful. It returns true if the job was converted successfully otherwise false.
In your case, I'm not sure how essential the profile would be, but it does look like your code fails to wait for completion. The following code will wait for the conversion job to finish (and check for success if you need to):
# Wait for completion
while( -Not $PJ.IsFinished ){
Start-Sleep -Seconds 5
}
# Check for success
if( $PJ.IsSuccessful ){
# success case
} else {
# failure case
}
Unrelated, but good practice, wrap your code in a try/finally block, and put your COM release in that block. This way your COM connection closes cleanly even in the event of a terminating error:
$PDFCreator = New-Object -ComObject PDFCreator.JobQueue
try {
# Handle PDF creator calls
} finally {
if( $PDFCreator ){
$PDFCreator.ReleaseCom()
}
}
The finally block is guaranteed to execute before returning to a parent scope, so whether the code succeeds or fails, the finally block will be run.

Why doesn't Pester catch errors using a trap

I'm wondering why I get the following behaviour when running this script. I have the script loaded in PowerShell ISE (v4 host) and have the Pester module loaded. I run the script by pressing F5.
function Test-Pester {
throw("An error")
}
Describe "what happens when a function throws an error" {
Context "we test with Should Throw" {
It "Throws an error" {
{ Test-Pester } | Should Throw
}
}
Context "we test using a try-catch construct" {
$ErrorSeen = $false
try {
Test-Pester
}
catch {
$ErrorSeen = $true
}
It "is handled by try-catch" {
$ErrorSeen | Should Be $true
}
}
Context "we test using trap" {
trap {
$ErrorSeen = $true
}
$ErrorSeen = $false
Test-Pester
It "is handled by trap" {
$ErrorSeen | Should Be $true
}
}
}
I then get the following output:
Describing what happens when a function throws an error
Context we test with Should Throw
[+] Throws an error 536ms
Context we test using a try-catch construct
[+] is handled by try-catch 246ms
Context we test using trap
An error
At C:\Test-Pester.ps1:2 char:7
+ throw("An error")
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (An error:String) [], RuntimeException
+ FullyQualifiedErrorId : An error
[-] is handled by trap 702ms
Expected: {True}
But was: {False}
at line: 40 in C:\Test-Pester.ps1
40: $ErrorSeen | Should Be $true
Question
Why is the trap{} apparently not running in the final test?
Here are two solutions to the problem based on the comments/suggested answers from #PetSerAl and #Eris. I have also read and appreciated the answer given to this question:
Why are variable assignments within a Trap block not visible outside it?
Solution 1
Although variables set in the script can be read within the trap, whatever you do within the trap happens to a copy of that variable, i.e. only in the scope that is local to the trap. In this solution we evaluate a reference to $ErrorSeen so that when we set the value of the variable, we are actually setting the value of the variable that exists in the parent scope.
Adding a continue to the trap suppresses the ErrorRecord details, cleaning up the output of the test.
Describe "what happens when a function throws an error" {
Context "we test using trap" {
$ErrorSeen = $false
trap {
Write-Warning "Error trapped"
([Ref]$ErrorSeen).Value = $true
continue
}
Test-Pester
It "is handled by trap" {
$ErrorSeen | Should Be $true
}
}
}
Solution 2
Along the same lines as the first solution, the problem can be solved by explicitly setting the scope of the $ErrorSeen variable (1) when it is first created and (2) when used within the trap {}. I have used the Script scope here, but Global also seems to work.
Same principle applies here: we need to avoid the problem where changes to the variable within the trap only happen to a local copy of the variable.
Describe "what happens when a function throws an error" {
Context "we test using trap" {
$Script:ErrorSeen = $false
trap {
Write-Warning "Error trapped"
$Script:ErrorSeen = $true
continue
}
Test-Pester
It "is handled by trap" {
$ErrorSeen | Should Be $true
}
}
}
According to this blog, you need to tell your trap to do something to the control flow:
The [...] thing you notice is that when you run this as script, you will receive both your error message and the red PowerShell error message.
. 'C:\Scripts\test.ps1'
Something terrible happened!
Attempted to divide by zero.
At C:\Scripts\test.ps1:2 Char:3
+ 1/ <<<< null
This is because your Trap did not really handle the exception. To handle an exception, you need to add the "Continue" statement to your trap:
trap { 'Something terrible happened!'; continue }
1/$null
Now, the trap works as expected. It does whatever you specified in the trap script block, and PowerShell does not get to see the exception anymore. You no longer get the red error message.

Catch all Powershell errors using Try/Catch/Finally

I have a 1500+ lines Powershell script that has been enhanced to log progress and errors to Azure Table Storage.
The problem is that not all errors are logged.
Is this the easiest way to accomplish this without changing the script too much and catching all errors?
$ErrorActionPreference = "Stop"
function a
{
"a"
}
function b
{
"b"
Get-Content foo
}
function c
{
"c"
}
Try
{
a
b
c
}
Catch
{
"Catch & Log Error"
$Error[0].Exception
}
Finally
{
"The End"
}
UPDATE
The output is:
a
b
Catch & Log Error
Cannot find path 'C:\foo' because it does not exist.
The End
You can place this at the top of your script:
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
... all errors will then 'act' like terminating errors & be caught by a try...catch block.

Detecting a .NET exception in PS

I am making several calls to .NET classes in order in my PS script like this:
[class1]::MethodA()
[class1]::MethodB()
if(/*check if last method threw an error*/)
{
"MethodB failed! Exiting."
return
}
[class2]::MethodC()
[class2]::MethodD()
I really want to check whether there was an exception from MethodB before moving on. How do check for this?
Use a try/catch block:
try
{
[class1]::MethodB()
}
catch
{
Write-Host "MethodB failed!"
exit
}

Powershell try/catch rethrow not propagating error (Powershell 2.0)

I have a try-catch statement within a try-catch statement. The inner catch catches the error, but the throw does not cause the error to be caught in the out catch statement. Breifly, my script is formatted similar to:
$ErrorPreference = "Stop"
try
{
getStuffFromDB
putStuffInDB
}
catch
{
write-host ("Error: " + $error[0])
}
function getStuffFromDB
{
try
{
-- database query statement
}
catch
{
throw
}
finally
{
close connection and clean up
}
}
function putStuffInDB
{
try
{
-- database insert statements statement
}
catch
{
throw
}
finally
{
close connection and clean up
}
}
When I ran the script there were no errors, but I noticed the SQL Server database that I was attempting to populate was missing data. When I re-ran the script in debug, the function 'putStuffInDB' had an error that was caught in the catch block. But when I stepped the message did not get 'thrown' to the outer catch block, but processed the finally block and terminated.
I am obviously missing something that I am not seeing. I have used the construct in C# in the past and never had issues with errors being 'passed' to the outer catch block.
I am not seeing that behavior. I ran the following in PowerShell ISE and it produces the expected results. Is it possible that the errors in the database were not in fact thrown as exceptions? I believe in SQL Server for example, certain errors under a given error level are not thrown as exceptions back to the ADO.NET provider.
$ErrorActionPreference = 'Stop'
function Throw1 {
try {
Write-Host "Throw1.Try"
throw "Error from Throw1"
}
catch {
Write-Host "Throw1.Catch"
throw
}
finally {
Write-Host "Throw1.Finally"
}
}
function Throw2 {
try {
Write-Host "Throw2.Try"
throw "Error from Throw2"
}
catch {
Write-Host "Throw2.Catch"
throw
}
finally {
Write-Host "Throw2.Finally"
}
}
function Test {
try {
Throw1
Throw2
}
catch {
Write-Host $error[0]
}
}
Test
Produces the following:
Throw1.Try
Throw1.Catch
Throw1.Finally
Error from Throw1
The variable you want to set is $ErrorActionPreference, not $ErrorPreference.
(Josh did set the right variable.)
I realized that the problem was of my own doing. In the POSH functions to create the SQLServer entries I returned the primary key of the data set created. The design of the functions was such that the function would return the primary key. The design mistake was that I put a return statement in the finally block which superceded the throw back to the outer catch. I have changed the design removing the return statement. The try/catch now works correctly.