Getting a remote session stack trace - powershell

How do I obtain the stack trace of an exception thrown on a remote computer?
This question is almost identical, however the asker was satisfied with getting the SerializedRemoteInvocationInfo.PositionMessage. My situation has multiple scripts calling one another, so I need the full call stack to understand my problem.
The following snippet shows an empty stack trace:
$session = New-PSSession -ComputerName AComputer -UseSSL
$script = {C:\AScriptThatFails.ps1}
try{
Invoke-Command -Session $session -ScriptBlock $script
}
catch{
Write-Output = $_.Exception.SerializedRemoteException.StackTrace #Empty!?
}

It looks like the error record's .ScriptStackTrace property contains the remote script execution's stack trace:
try {
Invoke-Command -ErrorAction Stop -Session $session -ScriptBlock $script
}
catch {
$_ # output the error record
$_.ScriptStackTrace # output the script stack trace
}
Note the use of -ErrorAction Stop to escalate remote errors to script-terminating ones, so that the try / catch handler is triggered by any error, not just by script-terminating ones (that is, also by non-terminating and statement-terminating errors).
If this escalation is undesired, use the following technique:
$hadScriptTerminatingError = $false
try {
# User -ErrorVariable to collect the errors that occur.
Invoke-Command -Session $session -ScriptBlock $script -ErrorVariable errs
} catch { $hadScriptTerminatingError = $true }
$succeeded = $? -and -not $hadScriptTerminatingError
# Analyze the collected errors, if any.
foreach ($err in $errs) {
$err # output the error record
$err.ScriptStackTrace # output the script stack trace
}

Related

Exit not Exiting in invoke-command in remote ps session

I have a Function invokes a command that starts a new ps session on a remote server. The invoke command has an Exit clause however this is not exiting?
Function CreateID{
Invoke-Command -Session $Script:sesh -ScriptBlock{
Set-Location c:\
Import-Module ActiveDirectory
Try
{
If (Get-ADGroupMember "$Using:IDGroup" | Where-Object Name -match
"$Using:Computer")
{
Write-Host "Already in $using:IDGroup Exiting Script"
Disconnect-PSSession -Session $Script:sesh
Exit-PSSession
Exit
}
}
Catch
{ }
Write-Host "Did not Exit"
}
}
The Get-AD command works fine so where it should not display "did not exit" it does - how can i exit from a scriptblock in a remote ps session?
I am trying the disconnect session and Exit-pssession to see if they would do the same as simply exit but none of those are working.
I have also tried Break and no luck.
Ok so i figured this out - the ps session and invoke-command are red herrings. The basis of this is that you cannot Exit a Try/Catch statement.
i had to do this to get it to work - now it Exits. I just cannot use a Try/ Catch - if anyone knows how to exit a Try/Catch let me know!
#Try
#{
If (Get-ADGroupMember "$Using:IDGroup" | Where-Object Name -match
"$Using:Computer")
{
Write-Host "Already in $using:IDGroup Exiting Script"
Disconnect-PSSession -Session $Script:sesh
Exit-PSSession
Exit
}
#}
#Catch
#{ }
I don't know if this works for PSSession or not, and I don't have an environment to test it, but you can use this to exit powershell within a try catch
[Environment]::Exit(0)
Try/Catch should work in an invoke-command.
I don't usually invoke-commands to sessions, rather I use -ComputerName.
This worked fine for me:
invoke-command -ComputerName "MyDomainController" -ScriptBlock {
try { get-aduser "ValidUser" } catch { "doh!" }
}
I just tried this as well and it also worked:
$sess1 = New-PSSession -ComputerName MyDomainController
invoke-command -Session $sess1 -ScriptBlock { try { get-aduser "ValidUser" } catch { "doh!" } }
If I change either of those "ValidUser" values to invalid users I see the "doh!" as well.
Perhaps it's because you're trying to end the session from within the session.
You should deal with the output of the invoke-command or the function and then handle the session based on that.
Like using my lame example...
$sess1 = New-PSSession -ComputerName MyDomainController
$DoStuff = invoke-command -Session $sess1 -ScriptBlock { try { get-aduser "ValidUser" } catch { "doh!" } }
If ($DoStuff -eq "doh!") { Remove-PSSession $sess1 } else { $DoStuff }

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"
}

Start-DscConfiguration doesn't throw exceptions?

I noticed that if applying a configuration through Start-DscConfiguration fails, it writes to the error stream but doesn't
throw an Exception? That is, if I do the following:
try{
Start-DscConfiguration -Path ".\MyConfig" -Wait -Verbose
}catch{
#...
}
...it never ends up in the catch handler. I suspect this may have something to do with the fact that without the "-Wait",
Start-DscConfiguration starts an async job for this, and async commands probably don't throw exceptions, but in a synchronous
scenario, I would very much like to know if my configuration could be applied.
What is the proper way to determine if Start-DscConfiguration has completed succesfully?
The only way I know is to check the global "$error" variable and compare the number of error records before and after your call to Start-DscConfiguration. If there's more afterwards then something must have gone wrong during the call, so throw your own exception:
Configuration TestErrorHandling {
Node "localhost" {
Script ErroringResource {
GetScript = { return $null; }
TestScript = { return $false; }
SetScript = { throw new-object System.InvalidOperationException; }
}
}
}
$errorCount = $error.Count;
write-host "starting dsc configuration"
$mof = TestErrorHandling;
Start-DscConfiguration TestErrorHandling –Wait –Verbose;
write-host "dsc configuration finished"
if( $error.Count -gt $errorCount )
{
$dscErrors = $error[$errorCount..($error.Count - 1)];
write-host "the following errors occurred during dsc configuration";
write-host ($dscErrors | fl * | out-string);
throw $dscErrors[-1];
}
There's another way to make it cause an exception. Try saving it into the ErrorVariable like this :
try
{
Start-DscConfiguration -Path ".\MyConfig" -Wait -Verbose -ErrorVariable ev
}
catch
{
$myException = $_
}
Weirdly so, this throws the exception when there's an error (which is what you wanted). You can get the value of your exception in the $myexception variable, and also could get just a one liner description of your error using $ev
PS: Note that while mentioning ev in the errorVariable parameter, you do it without the '$' symbol - since you're only specifying the variable 'name'.
Start-DscConfiguration when used without -Wait will create a job object - with one child job for every computername. PowerShell job objects have an Error stream which contains all the errors. You can check this stream as well
$job = Start-DscConfiguration -Force -Verbose -Path C:\Temp\Demo\ -ComputerName localhost
Receive-Job $job -Wait
'Errors in job = ' + ($job.childjobs[0].Error.Count)

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
}

Catching errors in the Exchange Management Shell

I'm trying to write a powershell script that creates an Exchange Mailbox. This works fine as long as the Mailbox doesn't already exist, but when I try to catch any error and report it back the script just runs through as if everythign were fine.
I ran the script on an already existing user and it shows the error, but it returns normally as if the mailbox was created.
I found this question, which solved the "why", I guess the Enable-Mailbox command only throws non-terminating errors.
Anyway, all the suggested solutions to catch these errors fail. The cmdlet seems to ignore the $ErrorActionPreference variable, $? is always $true, regardless if an error occured or not. $error always contains something, so here as well nothing to check against.
This is the script code I'm using, very basic.
param( [string]$uid, [string]$email )
trap [Exception] {
"ERROR: " + $_.Exception.Message
exit
}
Enable-Mailbox -Identity $uid -Database HaiTest-MBDataBase-01 -PrimarySmtpAddress $email
"SUCCESS: mailbox created successfully"
It works with everything else, it's just the Exchange Management Shell that causes trouble. The Exchange environment is an Exchange 2010 server.
Is there any way to check the cmdlets for errors?
Trapping errors works for terminating errors only, looks like the error you get from Enable-Mailbox is not a terminating error. You can force the error to be a terminating error by passing the ErrorAction variable a value of 'Stop'. You can also use try/catch (in PowerShell 2.0) instead of trap:
param( [string]$uid, [string]$email )
trap {
"ERROR: " + $_.Exception.Message
exit
}
Enable-Mailbox -Identity $uid -Database HaiTest-MBDataBase-01 -ErrorAction Stop -PrimarySmtpAddress $email
"SUCCESS: mailbox created successfully"
So far, the only reliable solution I have found to the strange exception handling behavior of the Exchange 2010 Cmdlets is adding "-ErrorAction SilentlyContinue", to suppress the exceptions, then looking for the expected results.
For instance, when enabling a mailbox, I look for the existence of the mailbox, like this...
function Enable-MailboxSafely {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
$Identity,
$Database,
[switch]$WhatIf
)
$output = [pscustomobject][ordered]#{
Identity = $Identity.ToString()
Mailbox = $null
Message = 'Unknown Error'
Success = $false
}
$output.Mailbox = Get-Mailbox -Identity $Identity -ErrorAction SilentlyContinue
if ($output.Mailbox -ne $null) {
$output.Message = 'Mailbox already exists.'
$output.success = $true
}
Else {
$null = Enable-Mailbox -Identity $Identity -Database:$Database -ErrorAction SilentlyContinue
# Have to look for a mailbox, as a workaround to the Exchange 2010 Cmdlets not implementing exceptions correctly.
$output.Mailbox = Get-Mailbox -Identity $Identity -DomainController:$DomainController -ErrorAction SilentlyContinue
if ($output.Mailbox -ne $null) {
$output.Message = "Mailbox created for [$Identity] on database [$Database]."
$output.success = $true
}
else {
$output.Message = "Failed to create mailbox for [$Identity] on database [$Database]."
$output.Success = $false
}
}
Write-Output $output
}
Enable-MailboxSafely -Identity Somebody