Powershell capturing an error (ExtendedTypeSystemException) - powershell

I am trying to catch an error nicely, as I would do in Java. The program is similar to this:
try
{
New-Object System.DirectoryServices.DirectoryEntry($SearchString, $username, $pass)
Write-Ouput "Continue program"
}
catch
{
Write-Output "Some error"
}
The exception is never captured. In fact, the catch method executes when everything goes well.
From the different errors I can get, it doesn't look like an exception is raised:
format-default : The following exception occurred while retrieving member "distinguishedName": "The server is not operational.
"
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
format-default : The following exception occurred while retrieving member "distinguishedName": "The user name or password is
incorrect.
"
+ CategoryInfo : NotSpecified: (:) [format-default], ExtendedTypeSystemException
+ FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatDefaultCommand
How can I capture these errors in a more user-friendly way?
Maybe in this case it is not possible.
After some testing, I think the problem is that no exception is raised for some reason, despite of what the error message says. So that is why I can't capture it. I have also tried to use getType, close and other methods but I get the same error:
The following exception occurred while retrieving member "GetType": "The user name or password is incorrect.
"
...
This solution does not seem to work either, same behaviour.
Question related.

need add 'erroraction'
try
{
New-Object System.DirectoryServices.DirectoryEntry($SearchString, $username, $pass) -ErrorAction Stop
Write-Ouput "Continue program"
}
catch
{
$Err = $error[0]
Write-Output $Err.Exception
}

You could also just default back to this...
If ($(New-Object System.DirectoryServices.DirectoryEntry($SearchString, $username, $pass)) -eq $false)
{Write-Verbose 'Continue program' -Verbose}
Else {Write-Warning -Message 'Something went wrong with your directory command'}
# Results
<#
WARNING: Something went wrong with your directory command
#>
... though like you try/catch is my preference. I am not in an ADDS environment to test the try/catch one at this time.
Know that you are not the only one stressed out by this. See:
Powershell DirectoryService object error neither caught nor trapped
Yet, after a bit of digging at my notes on ADDS (ADSI stuff from back in the day) in the past, here's the real deal on this namespace. The result is not really $true or $false, unlike System.DirectoryServices.AccountManagement; with what you are using, is provided with incorrect credentials you get the error. If the credentials are correct the returned object will contain a valid result, the result is Boolean output.
So, if you really want to stick with Try/Catch, do this instead:
try
{
$AuthResult = [bool](New-Object System.DirectoryServices.DirectoryEntry($SearchString, $username, $pass)).distinguishedName -ne 'False'
'Continue program'
}
catch {Write-Warning -Message 'Bad credentials passed'}
# Resuls
<#
WARNING: Bad credentials passed
#>
You of course, if so inclined you can trap error data and output as needed.
Yet, that is not much different than what the check provided in the If/Then. So, your choice.

I managed to do it with this solution:
$o = New-Object DirectoryServices.DirectoryEntry ($path, $user, $pass)
if ($path -eq 'LDAP://') {
'Computer is not member of a domain.' } elseif ($o.DistinguishedName) {
'Invalid user credentials.' } But if for some reason you must retrieve the actual error message, you can do it like this:
$o = New-Object DirectoryServices.DirectoryEntry ($path, $user, $pass)
try {
$o | Out-Default } catch {
$_.Exception.InnerException.Message }

This worked for me. The object will instantiate even though there might be an error that won't show up until you try to use it later. Using this, you can set a boolean to whether the distinguishedName was set or not:
$Entry = New-Object System.DirectoryServices.DirectoryEntry($SearchString, $username, $pass)
$succeeded = [bool]$Entry.distinguishedName
if ($succeeded)
{
Write-Ouput "Continue program"
}
else
{
Write-Output "Some error"
}

Related

Powershell script to determine if web page exists

I need a way to determine from a PS script if any web page is up or down, regardless of whether it first prompts for credentials. Even if the page requires that java is installedd or whatever other reason. The goal here is to determine that the page is there and it shouldn't matter whether it works properly or if it can be displayed. After all is said and done it should just tell me that site/page is UP or DOWN after executing the script with .\sitecheck.ps1 'https://trac.edgewall.org/login'
It'd also be nice if we could print why the page is down (like when you get a 401 error) and print the error message and status code (integer).
I'm trying to work off of this script which obviously doesn't work properly because I'm trying to find a solution:
# First we create the request.
$url = $args[0]
$HTTP_Request = [System.Net.WebRequest]::Create($url)
# We then get a response from the site.
$HTTP_Response = $HTTP_Request.GetResponse()
# We then get the HTTP code as an integer.
$HTTP_Status = [int]$HTTP_Response.StatusCode
If ($HTTP_Status -eq 200) {
Write-Host "Site is OK!"
}
Else {
Write-Host "The Site may be down, please check!"
}
# Finally, we clean up the http request by closing it.
If ($HTTP_Response -eq $null) { } Else { $HTTP_Response.Close()}
Someone responded with this answer to a similar question on this site:
"If the URL needs credentials, you need to add $HTTP_Request.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials. You need a Try..Catch around the $HTTP_Response = $HTTP_Request.GetResponse() line, and if that fails, $HTTP_Response will be null and so can't be closed because it's already null - like when you get a (404) Not Found, you will have no response and error will be You cannot call a method on a null-valued expression if you try to do .Close() on it."
Unfortunately I don't exactly know how to do that. Currently I'm getting the error below. Most of the actual error message is accurate since I haven't entered the correct credentials hence a 401 error code:
Exception calling "GetResponse" with "0" argument(s): "The remote
server returned an error: (401) Unauthorized." At
C:\Users\test\sitecheck.ps1:11 char:1
+ $HTTP_Response = $HTTP_Request.GetResponse()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WebException
Don't expect to receive a 200 because you haven't accessed the page yet. Look, I can even click on the hyperlink you posted here on StackOverflow: before accessing the page the banner ask for login (I haven't accessed the page yet)
Then, because I don't have the credentials what I receive is a 401 Unauthorized.
So what I suggest you to do is to check if Apache Subversion is up and running instead:
# First we create the request.
$url = $args[0]
$HTTP_Request = [System.Net.WebRequest]::Create('https://svn.edgewall.org')
# We then get a response from the site.
$HTTP_Response = $HTTP_Request.GetResponse()
# We then get the HTTP code as an integer.
$HTTP_Status = [int]$HTTP_Response.StatusCode
If ($HTTP_Status -eq 200) {
Write-Host "Site is OK!"
}
Else {
Write-Host "The Site may be down, please check!"
}
# Finally, we clean up the http request by closing it.
If ($HTTP_Response -eq $null) { } Else { $HTTP_Response.Close()}
**
EDIT:
**
After your comment I've found a solution for you here:
Paste this code in a .ps1 file and execute it like in picture:
$url = $args[0]
try {
$HttpWebResponse = $null;
$HttpWebRequest = [System.Net.HttpWebRequest]::Create($url);
$HttpWebResponse = $HttpWebRequest.GetResponse();
if ($HttpWebResponse) {
Write-Host -Object $HttpWebResponse.StatusCode.value__;
Write-Host -Object $HttpWebResponse.GetResponseHeader("X-Detailed-Error");
}
}
catch {
$ErrorMessage = $Error[0].Exception.ErrorRecord.Exception.Message;
$Matched = ($ErrorMessage -match '[0-9]{3}')
if ($Matched) {
Write-Host -Object ('HTTP status code was {0} ({1})' -f $HttpStatusCode, $matches.0);
}
else {
Write-Host -Object $ErrorMessage;
}
$HttpWebResponse = $Error[0].Exception.InnerException.Response;
$HttpWebResponse.GetResponseHeader("X-Detailed-Error");
}
This script will always print you the status code of the page. So now when you target https://trac.edgewall.org/login it will return you 401 which is the right status code.
You can see a list of all the error codes here: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

How to properly access elements of Internet Explorer?

I have a script that tests whether one of our websites is working properly.
Test 1: It checks the HTTP status. (no problem here)
Test 2: It checks whether user authorization is working because we have had issues with this in the past.
Then it waits one hour and runs both tests again.
The overall structure of the script is as follows (pseudocode):
while (1 -eq 1) {
test1
if ($result -eq "pass") {
test2
}
start-sleep -seconds 3600
}
Test 2 is where the problem occurs. How this test works is: navigate to the login page, enter credentials, click login button. Check URL, as successful login leads to a different URL from the login page. This works fine, except on (seemingly random) loops it cannot access the elements of the page: the username and passwords fields and the login button. The exceptions that get thrown vary depending on how I change the code around to try to fix the problem.
Some error messages I've gotten include:
System.Runtime.InteropServices.COMException The RPC server is
unavailable. (Exception from HRESULT: 0x800706BA)
System.Runtime.InteropServices.COMException
The property 'value' cannot be found on this object. Verify that the property exists and
can be set.
System.Runtime.InteropServices.COMException
OperationStopped: (:) [], COMException
HResult: -2147352319
No such interface supported
System.Runtime.InteropServices.COMException
OperationStopped: (:) [], COMException
HResult: -2147352319
This command is not supported.
The offending code is anything that tries to access a page element with getElementByID, for example:
$ie.Document.getElementByID("username").value = $userame
Again, the weird thing is that the code runs fine for hours and hours... but then, usually in the middle of night, it randomly becomes unable to access the page elements.
Following is the full code of Test 2 in case that helps:
$ie = New-Object -ComObject "internetExplorer.Application"
#$ie.Visible = $true
$ie.navigate2($loginPage)
while ($ie.busy -eq $true) {start-sleep -seconds 1}
$ie.Document.getElementByID("username").value = $username
$ie.Document.getElementByID("password").value = $password
$ie.Document.getElementByID("login-button").click()
while ($ie.busy -eq $true) {start-sleep -seconds 1}
if ($ie.Document.url -eq $landingPage) {
#success
} else {
#failure
}
$ie.quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($ie) | out-null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
$process = get-process -name "iexplore"
while ($process) {
try {
stop-process -name "iexplore" -ea stop
} catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
#do nothing
}
try {
$process = get-process -name "iexplore" -ea stop
} catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
$process = $null
}
}
Until I can figure out what the problem is, I've implemented a workaround. I put the offending code in a try block. In the catch block, I restart the script. Following is not exactly how it is implemented in my script but it gives you the idea:
try {
$ie.Document.getElementByID("username").value = $username
} catch {
start powershell {$scriptPath}
exit
}
When the script restarts, the elements of the page can once again be accessed with getElementByID.

test-path returns permission denied - how to do error handling

i try to do error handling within my powershell script. but i always get an fatal. i tried a few things, e. g. try{ } catch{ } - but i did it not get to work.
any ideas or Solutions?
Function Check-Path($Db)
{
If ((Test-Path $Db) –eq $false) {
Write-Output "The file $Db does not exist"
break
}
}
It Returns:
Test-Path : Zugriff verweigert
In K:\access\access.ps1:15 Zeichen:6
+ If ((Test-Path $Db) -eq $false) {
+ ~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (K:\ss.mdb:String) [Test-Path], UnauthorizedAccessException
+ FullyQualifiedErrorId : ItemExistsUnauthorizedAccessError,Microsoft.PowerShell.Commands.TestPathCommand
Somewhat confusingly Test-Path actually generates an error in a number of cases. Set the standard ErrorAction parameter to SilentlyContinue to ignore it.
if ((Test-Path $Db -ErrorAction SilentlyContinue) -eq $false) {
I cannot answer directly. So this has to do:
I strongly disagree with your answer. Test-Path does show $false when you run it against a network share that is not accessible, but it will be false also (without Exception) when the Server is not reachable.
So your answer simply ignores anything but a reachable share.
What is neccessary however is a try-catch-block that handles this better:
[cmdletbinding()]
param(
[boolean]$returnException = $true,
[boolean]$returnFalse = $false
)
## Try-Catch Block:
try {
if ($returnException) {
## Server Exists, but Permission is denied.
Test-Path -Path "\\Exists\Data\" -ErrorAction Stop | Out-Null
} elseif ($returnFalse) {
## Server does not exist
Test-Path -Path "\\NoExists\Data\" -ErrorAction Stop | Out-Null
}
} catch [UnauthorizedAccessException] {
## Unauthorized
write-host "No Access Exception"
} catch {
## an error has occurred
write-host "Any other Exception here"
}
The really important part however is the ErrorAction on the Test-Path command, otherwise the exception will be wrapped around a system management error and is thus not catchable. This is in detail explained here:
PowerShell catching typed exceptions

Try/catch does not seem to have an effect

I am new to powershell, and I am trying to add error handling via try/catch statements, but they don't seem to actually be catching the error. This is powershell v2 CP3.
$objComputer = $objResult.Properties;
$strComputerName = $objComputer.name
write-host "Checking machine: " $strComputerName
try
{
$colItems = get-wmiobject -class "Win32_PhysicalMemory" -namespace "root\CIMV2" -computername $strComputerName -Credential $credentials
foreach ($objItem in $colItems)
{
write-host "Bank Label: " $objItem.BankLabel
write-host "Capacity: " ($objItem.Capacity / 1024 / 1024)
write-host "Caption: " $objItem.Caption
write-host "Creation Class Name: " $objItem.CreationClassName
write-host
}
}
Catch
{
write-host "Failed to get data from machine (Error:" $_.Exception.Message ")"
write-host
}
finally
{ }
When it fails to contact a specific machine, I get this in console, and not my clean catch message:
Get-WmiObject : The RPC server is
unavailable. (Exception from HRESULT:
0x800706BA) At Z:\7.0 Intern
Programvare\Powershell\Get memory of
all computers in AD.ps1:25 char:34
+ $colItems = get-wmiobject <<<< -class "Win32_PhysicalMemory"
-namespace "root\CIMV2" -computername $strComputerName -Credential
$credentials
+ CategoryInfo : InvalidOperation: (:) [Get-WmiObject],
COMException
+ FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
I was able to duplicate your result when trying to run a remote WMI query. The exception thrown is not caught by the Try/Catch, nor will a Trap catch it, since it is not a "terminating error". In PowerShell, there are terminating errors and non-terminating errors . It appears that Try/Catch/Finally and Trap only works with terminating errors.
It is logged to the $error automatic variable and you can test for these type of non-terminating errors by looking at the $? automatic variable, which will let you know if the last operation succeeded ($true) or failed ($false).
From the appearance of the error generated, it appears that the error is returned and not wrapped in a catchable exception. Below is a trace of the error generated.
PS C:\scripts\PowerShell> Trace-Command -Name errorrecord -Expression {Get-WmiObject win32_bios -ComputerName HostThatIsNotThere} -PSHost
DEBUG: InternalCommand Information: 0 : Constructor Enter Ctor
Microsoft.PowerShell.Commands.GetWmiObjectCommand: 25857563
DEBUG: InternalCommand Information: 0 : Constructor Leave Ctor
Microsoft.PowerShell.Commands.GetWmiObjectCommand: 25857563
DEBUG: ErrorRecord Information: 0 : Constructor Enter Ctor
System.Management.Automation.ErrorRecord: 19621801 exception =
System.Runtime.InteropServices.COMException (0x800706BA): The RPC
server is unavailable. (Exception from HRESULT: 0x800706BA)
at
System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Management.ManagementScope.InitializeGuts(Object o)
at System.Management.ManagementScope.Initialize()
at System.Management.ManagementObjectSearcher.Initialize()
at System.Management.ManagementObjectSearcher.Get()
at Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()
errorId = GetWMICOMException errorCategory = InvalidOperation
targetObject =
DEBUG: ErrorRecord Information: 0 : Constructor Leave Ctor
System.Management.Automation.ErrorRecord: 19621801
A work around for your code could be:
try
{
$colItems = get-wmiobject -class "Win32_PhysicalMemory" -namespace "root\CIMV2" -computername $strComputerName -Credential $credentials
if ($?)
{
foreach ($objItem in $colItems)
{
write-host "Bank Label: " $objItem.BankLabel
write-host "Capacity: " ($objItem.Capacity / 1024 / 1024)
write-host "Caption: " $objItem.Caption
write-host "Creation Class Name: " $objItem.CreationClassName
write-host
}
}
else
{
throw $error[0].Exception
}
If you want try/catch to work for all errors (not just the terminating errors) you can manually make all errors terminating by setting the ErrorActionPreference.
try {
$ErrorActionPreference = "Stop"; #Make all errors terminating
get-item filethatdoesntexist; # normally non-terminating
write-host "You won't hit me";
} catch{
Write-Host "Caught the exception";
Write-Host $Error[0].Exception;
}finally{
$ErrorActionPreference = "Continue"; #Reset the error action pref to default
}
Alternatively... you can make your own try/catch function that accepts scriptblocks so that your try/catch calls are not as kludge. I have mine return true/false just in case I need to check if there was an error... but it doesn't have to. Also, exception logging is optional, and can be taken care of in the catch, but I found myself always calling the logging function in the catch block, so I added it to the try/catch function.
function log([System.String] $text){write-host $text;}
function logException{
log "Logging current exception.";
log $Error[0].Exception;
}
function mytrycatch ([System.Management.Automation.ScriptBlock] $try,
[System.Management.Automation.ScriptBlock] $catch,
[System.Management.Automation.ScriptBlock] $finally = $({})){
# Make all errors terminating exceptions.
$ErrorActionPreference = "Stop";
# Set the trap
trap [System.Exception]{
# Log the exception.
logException;
# Execute the catch statement
& $catch;
# Execute the finally statement
& $finally
# There was an exception, return false
return $false;
}
# Execute the scriptblock
& $try;
# Execute the finally statement
& $finally
# The following statement was hit.. so there were no errors with the scriptblock
return $true;
}
#execute your own try catch
mytrycatch {
gi filethatdoesnotexist; #normally non-terminating
write-host "You won't hit me."
} {
Write-Host "Caught the exception";
}
It is also possible to set the error action preference on individual cmdlets, not just for the whole script. This is done using the parameter ErrorAction (alisa EA) which is available on all cmdlets.
Example
try
{
Write-Host $ErrorActionPreference; #Check setting for ErrorAction - the default is normally Continue
get-item filethatdoesntexist; # Normally generates non-terminating exception so not caught
write-host "You will hit me as exception from line above is non-terminating";
get-item filethatdoesntexist -ErrorAction Stop; #Now ErrorAction parameter with value Stop causes exception to be caught
write-host "you won't reach me as exception is now caught";
}
catch
{
Write-Host "Caught the exception";
Write-Host $Error[0].Exception;
}
This is my solution. When Set-Location fails it throws a non-terminating error which is not seen by the catch block. Adding -ErrorAction Stop is the easiest way around this.
try {
Set-Location "$YourPath" -ErrorAction Stop;
} catch {
Write-Host "Exception has been caught";
}
Adding "-EA Stop" solved this for me.
Edit: As stated in the comments, the following solution applies to PowerShell V1 only.
See this blog post on "Technical Adventures of Adam Weigert" for details on how to implement this.
Example usage (copy/paste from Adam Weigert's blog):
Try {
echo " ::Do some work..."
echo " ::Try divide by zero: $(0/0)"
} -Catch {
echo " ::Cannot handle the error (will rethrow): $_"
#throw $_
} -Finally {
echo " ::Cleanup resources..."
}
Otherwise you'll have to use exception trapping.
In my case, it was because I was only catching specific types of exceptions:
try
{
get-item -Force -LiteralPath $Path -ErrorAction Stop
#if file exists
if ($Path -like '\\*') {$fileType = 'n'} #Network
elseif ($Path -like '?:\*') {$fileType = 'l'} #Local
else {$fileType = 'u'} #Unknown File Type
}
catch [System.UnauthorizedAccessException] {$fileType = 'i'} #Inaccessible
catch [System.Management.Automation.ItemNotFoundException]{$fileType = 'x'} #Doesn't Exist
Added these to handle additional the exception causing the terminating error, as well as unexpected exceptions
catch [System.Management.Automation.DriveNotFoundException]{$fileType = 'x'} #Doesn't Exist
catch {$fileType='u'} #Unknown

RPC_E_SERVERCALL_RETRYLATER during powershell automation

I'm using PowersHell to automate iTunes but find the error handling / waiting for com objects handling to be less than optimal.
Example code
#Cause an RPC error
$iTunes = New-Object -ComObject iTunes.Application
$LibrarySource = $iTunes.LibrarySource
# Get "playlist" objects for main sections
foreach ($PList in $LibrarySource.Playlists)
{
if($Plist.name -eq "Library") {
$Library = $Plist
}
}
do {
write-host -ForegroundColor Green "Running a loop"
foreach ($Track in $Library.Tracks)
{
foreach ($FoundTrack in $Library.search("$Track.name", 5)) {
# do nothing... we don't care...
write-host "." -nonewline
}
}
} while(1)
#END
Go into itunes and do something that makes it pop up a message - in my case I go into the Party Shuffle and I get a banner "Party shuffle automatically blah blah...." with a "Do not display" message.
At this point if running the script will do this repeatedly:
+ foreach ($FoundTrack in $Library.search( <<<< "$Track.name", 5)) {
Exception calling "Search" with "2" argument(s): "The message filter indicated
that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVER
CALL_RETRYLATER))"
At C:\Documents and Settings\Me\My Documents\example.ps1:17 char:45
+ foreach ($FoundTrack in $Library.search( <<<< "$Track.name", 5)) {
Exception calling "Search" with "2" argument(s): "The message filter indicated
that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVER
CALL_RETRYLATER))"
At C:\Documents and Settings\Me\My Documents\example.ps1:17 char:45
If you waited until you you had a dialog box before running the example then instead you'll get this repeatedly:
Running a loop
You cannot call a method on a null-valued expression.
At C:\Documents and Settings\Me\example.ps1:17 char:45
+ foreach ($FoundTrack in $Library.search( <<<< "$Track.name", 5)) {
That'll be because the $Library handle is invalid.
If my example was doing something important - like converting tracks and then deleting the old ones, not handling the error correctly could be fatal to tracks in itunes.
I want to harden up the code so that it handles iTunes being busy and will silently retry until it has success. Any suggestions?
Here's a function to retry operations, pausing in between failures:
function retry( [scriptblock]$action, [int]$wait=2, [int]$maxRetries=100 ) {
$results = $null
$currentRetry = 0
$success = $false
while( -not $success ) {
trap {
# Set status variables at function scope.
Set-Variable -scope 1 success $false
Set-Variable -scope 1 currentRetry ($currentRetry + 1)
if( $currentRetry -gt $maxRetries ) { break }
if( $wait ) { Start-Sleep $wait }
continue
}
$success = $true
$results = . $action
}
return $results
}
For the first error in your example, you could change the inner foreach loop like this:
$FoundTracks = retry { $Library.search( "$Track.name", 5 ) }
foreach ($FoundTrack in $FoundTracks) { ... }
This uses the default values for $wait and $maxRetries, so it will attempt to call $Library.search 100 times, waiting 2 seconds between each try. If all retries fail, the last error will propagate to the outer scope. You can set $ErrorActionPreference to Stop to prevent the script from executing any further statements.
COM support in PowerShell is not 100% reliable. But I think the real issue is iTunes itself. The application and COM model wasn't designed, IMO, for this type of management. That said, you could implement a Trap into your script. If an exception is raised, you could have the script sleep for a few seconds.
Part of your problem might be in how $Track.name is being evaluated. You could try forcing it to fully evaluate the name by using $($Track.name).
One other thing you might try is using the -strict parameter with your new-object command/