Azure Function Apps - PowerShell's ErrorActionPreference - powershell

By default the environment's $ErrorActionPreference is set to 'Continue'. When a cmdlet throws an error the script will continue.
I want it to 'Stop' and get caught in the catch of my try-catch block. In my script I am able to set:
$ErrorActionPreference = "Stop"
and if I print the value to the screen I can see that it is now set to 'Stop' and not 'Continue'. However, when my cmdlet throws an error it still continues. It is ignoring my error action preference and behaving according to the default value.
Can anyone shed any light on this?

So, I'm not sure what you are doing, but for me it works correctly:
Write-Output "PowerShell Timer trigger function executed at:$(get-date)";
try {
Get-Process 'hh' -erroraction stop
}
catch{
Write-Output "caught"
exit
}
Write-Output "never reached"
or like this:
Write-Output "PowerShell Timer trigger function executed at:$(get-date)";
$global:erroractionpreference = 1
try {
Get-Process 'hh'
}
catch{
Write-Output "caught"
exit
}
Write-Output "i do not work"
Both print the same (except for timestamp, ofc):
2017-02-02T19:56:35.916 PowerShell Timer trigger function executed
at:02/02/2017 19:56:35
2017-02-02T19:56:35.933 caught
2017-02-02T19:56:35.933 Function completed (Success,
Id=6097f1bf-06e1-4a1d-ba27-392607d9b33c)

Related

How to stop a child process with Ctrl+C without breaking the main script and stream child output to the main script in PowerShell?

I am writing a powershell script that is running on Linux. The purpose of this script is to run the MS SQL Server and show its output, but when the user presses Ctrl+C or some error happens, the script takes a copy of the data folder and then after that exits.
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Command,
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Args
)
BEGIN {
Write-Output "started $PSScriptRoot"
$currentErrorLevel = $ErrorActionPreference
# [console]::TreatControlCAsInput = $true
}
PROCESS {
try {
$ErrorActionPreference = 'SilentlyContinue';
$IsSqlRunning = Get-Process -name "sqlservr";
if ($null -eq $IsSqlRunning) {
start-process "/opt/mssql/bin/sqlservr" -wait -NoNewWindow
}
}
catch {
$ErrorActionPreference = $currentErrorLevel;
Write-Error $_.Exception
}
}
End {
$ErrorActionPreference = $currentErrorLevel;
#do backup
Create-Backup "/opt/mssql/bin/data"
Write-Output "finishd!"
}
I got a couple of problems with this script:
When I press Ctrl+C it breaks the main script and it never reaches to the Create-Backup section at the bottom of the script.
If I remove -Wait then the script won't show the sql log output
So my prefered solution is to run the sql with -Wait parameter and prevent the powershell to exit the code after I press Ctrl+C, but instead Ctrl+C close the sql instance
So I'm looking for a way to achieve both.
For simplicity I'll assume that your function only needs to support a single input object, so I'm using a simple function body without begin, process and end blocks, which is equivalent to having just an end block:
[CmdletBinding()]
PARAM (
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Command,
[Parameter(ValueFromPipelineByPropertyName)]
[string] $Args
)
Write-Output "started $PSScriptRoot"
# No need to save the current value, because the modified
# value is local to the function and goes out of scope on
# exiting the function.
$ErrorActionPreference = 'SilentlyContinue';
try {
$IsSqlRunning = Get-Process -name "sqlservr";
if ($null -eq $IsSqlRunning) {
start-process "/opt/mssql/bin/sqlservr" -wait -NoNewWindow
}
}
catch {
Write-Error $_.Exception
}
finally {
# This block is *always* called - even if Ctrl-C was used.
Create-Backup "/opt/mssql/bin/data"
# CAVEAT: If Ctrl-C was used to terminate the run,
# you can no longer produce *pipeline* input at this point
# (it will be quietly ignored).
# However, you can still output to the *host*.
Write-Host "finished!"
}
If you really need to support multiple input objects, it gets more complicated.

Error action not stopping script

Consider this simple code:
Read-Host $path
try {
Get-ChildItem $Path -ErrorAction Continue
}
Catch {
Write-Error "Path does not exist: $path" -ErrorAction Stop
Throw
}
Write-Output "Testing"
Why is 'Testing' printed to the shell if an invalid path is specified?
The script does not stop in the catch block. What am I doing wrong?
In your Try Catch block, you need to set Get-ChildItem -ErrorAction Stop
so the exception is caught in the Catch block.
With continue, you are instructing the command to not produce a terminating error when an actual error occurs.
Edit:
Also, your throw statement is useless there and you do not need to specify an error-action for Write-Error.
Here's the modified code.
$path = Read-Host
try {
Get-ChildItem $Path -ErrorAction stop
}
Catch {
Write-Error "Path does not exist: $path"
}
Additional note
You could apply this default behavior (if that is what you want) to the entire script by setting the default action to stop using :
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
I think this is what you need:
$path = Read-Host 'Enter a path'
try {
Get-ChildItem $Path -ErrorAction Stop
}
Catch {
Throw "Path does not exist: $path"
}
Write-Output "Testing"
Per Sage's answer, you need to change to -ErrorAction Stop in the Try block. This forces the Get-ChildItem cmdlet to throw a terminating error, which then triggers the Catch block. By default (and with the Continue ErrorAction option) it would have thrown a non-terminating error which are not caught by a try..catch.
If you then want your code to stop in the Catch block, use Throw with the message you want to return. This will produce a terminating error and stop the script (Write-Error -ErrorAction Stop will also achieve a terminating error, it's just a more complicated method. Typically you should use Write-Error when you want to return non-terminating error messages).

I don't understand how trap works

I'm trying to make a trap command that traps the exception that a file is not found. This is the code:
Trap {
Clear-Host
Write-Host "The file you are looking for does not exist"
}
Get-ItemProperty C:\fake
Am I misunderstanding how to do this?
Traps only execute on terminating errors (errors that stop the pipeline), so first you'll need change the ErrorAction behavior of the offending cmdlet:
Trap {
Clear-Host
Write-Host "The file you are looking for does not exist"
}
Get-ItemProperty C:\fake -ErrorAction Stop
To suppress the error record from subsequently bubbling up to the caller, return from the current scope from inside the trap:
Trap {
Clear-Host
Write-Host "The file you are looking for does not exist"
return
}
Get-ItemProperty C:\fake -ErrorAction Stop
In order to not have to specify the -ErrorAction Stop parameter argument explicitly all the time, set the $ErrorActionPreference variable at the start of your script/function:
$ErrorActionPreference = 'Stop'
or use the $PSDefaultParameterValues hashtable to set it for specific cmdlets:
$PSDefaultParameterValues['Get-ItemProperty:ErrorAction'] = 'Stop'
$PSDefaultParameterValues['Do-OtherStuff:ErrorAction'] = 'Stop'
$PSDefaultParameterValues['Set-*:ErrorAction'] = 'Stop'

Try & Catch not working when using Invoke-Command

There's a strange issue in my script.
I'm invoking a command on some servers. If I can't connect to the server (because it's offline or something) I still want to log that (Server is offline > log.txt)
Apparently, when an error occurs in Try block, the Catch block is not executed.
To test this, I wrote a value to the ComputerName parameter that doesn't exist.
Here's my code:
Try {
Invoke-Command -ComputerName hui -ScriptBlock $sb
}
Catch {
Write-Host "Hello"
}
But the Script never puts out "Hello"
The exception I get is a PSRemotingTransportException. I also tried to specify the exception, also didn't work
When I set a breakpoint at the Invoke-Command line, it also ignores the catch block. Why?
It's not a terminating error, so it will never be caught by try/catch. Add -ErrorAction Stop:
Try {
Invoke-Command -ComputerName hui -ScriptBlock $sb -ErrorAction Stop
}
Catch {
Write-Host "Hello"
}

PowerShell: error checking in a while true loop?

I have a while($true) loop with a start-sleep -s 60 at the end of it. The purpose is to start an external PowerShell script as a different user which will run through a list of servers, check the event log for changes within the last minute and react accordingly.
Since my while loop (below) is using the -credential flag to run the script as someone else, I'm concerned about errors (e.g. account locked out, expired password, missing file, etc).
I tried an if ($error) statement, and changed the filename of the external script, but I was never alerted. I'm thinking it's because it never stops to re-check itself?
while($true) {
# Start the scan
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1'
# Sleep 60 seconds
start-sleep -s 60
}
I suppose I could change my scheduled task to run every minute, but so far this loop seems to have been working great. Just want to introduce error checking while the loop is active.
Have you tried try/catch blocks? Wrong credentials is a terminating error, so the rest of the code in the try block won't run after a credentials-error. When you catch it, you can do whatever you want.. Ex.
try {
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1'
} catch {
#Catches terminating exceptions and display it's message
Write-Error $_.message
}
If you want to catch all errors, add -ErrorAction Stopto the Start-Processline. As said, credentials should be a terminating error, which make the erroraction parameter unnecessary
EDIT Why would you use Start-Process to run a script in the first place? I switched it to Invoke-Command which runs a powershell script remotely. When the scriptfile is missing, you will recieve a NON-terminating error. Because it's a non-terminating error, we need to use the -ErrorAction Stop parameter. To catch the missing-file error and every other error(like credentials), use something like this:
try { Invoke-Command -ScriptBlock { & c:\batch\02-Scan.ps1 } -ErrorAction Stop
} catch {
if ($_.Exception.GetType().Name -eq "CommandNotFoundException") {
Write-Error "File is missing"
} else {
Write-Error "Something went wrong. Errormessage: $_"
#or throw it directly:
#throw $_
}
}
Maybe?
while($true) {
# Start the scan
try{
Start-Process powershell -Credential $credentials -ArgumentList '-noprofile -command & c:\batch\02-Scan.ps1' -ErrorAction Stop
}
catch {
send-alert
break
}
# Sleep 60 seconds
start-sleep -s 60
}