Stoppt die Vorratsdatenspeicherung! Jetzt klicken & handeln!Willst du auch bei der Aktion teilnehmen? Hier findest du alle relevanten Infos und Materialien:

six demon bag

Wind, fire, all that kind of thing!

2019-11-13

Catching Exceptions in PowerShell Default Output Formatting

Yesterday I came across a question on StackOverflow that turned out to be rather interesting. The person asking the question used code similar to the below snippet for validating user credentials:

$user = 'user'
$pass = 'pass'
$path = 'LDAP://' + ([ADSI]'').DistinguishedName

New-Object DirectoryServices.DirectoryEntry ($path, $user, $pass)

which produces errors like this when the computer is not a member of a domain:


format-default : The following exception occurred while retrieving member "distinguishedName": "Unknown error (0x80005000)"

or like this when the credentials are invalid:

format-default : The following exception occurred while retrieving member "distinguishedName": "The user name or password is incorrect."

At first I thought, well, put the statement in a try/catch, maybe set $ErrorActionPreference = 'Stop' and you're done. However, that proved to not be the case:

PS C:\> $ErrorActionPreference = 'Stop'
PS C:\> try {New-Object DirectoryServices.DirectoryEntry ($path, $user, $pass)} catch {'error'}
format-default : The following exception occurred while retrieving member
"distinguishedName": "Unknown error (0x80005000)"
    + CategoryInfo          : NotSpecified: (:) [format-default], ExtendedTypeSystemException
    + FullyQualifiedErrorId : CatchFromBaseGetMember,Microsoft.PowerShell.Commands.FormatD...

Upon closer inspection I realized that the error was not thrown because the creation of the DirectoryEntry object failed. In fact, that object was created just fine (well, sort of):

PS C:\> $o = New-Object DirectoryServices.DirectoryEntry ($path, $user, $pass)
PS C:\> $o | Get-Member

   TypeName: System.DirectoryServices.DirectoryEntry

Name                        MemberType Definition
----                        ---------- ----------
ConvertDNWithBinaryToString CodeMethod static string ConvertDNWithBinaryToString(psobject ...
ConvertLargeIntegerToInt64  CodeMethod static long ConvertLargeIntegerToInt64(psobject deI...

PS C:\> $o | Format-List *

AuthenticationType :
Children           :
Guid               :
ObjectSecurity     :
Name               :
...

Only when actually trying to output the objet the error appears:

PS C:\> $o
format-default : The following exception occurred while retrieving member
"distinguishedName": "Unknown error (0x80005000)"
...

meaning that apparently the object got passed to PowerShell's output formatting routines, and the error occurred there. Taking a quick look at the object's DefaultDisplayPropertySet confirmed that there should be 2 properties in the default output, one of them being distinguishedName.

PS C:\> $o.PSStandardMembers.DefaultDisplayPropertySet

ReferencedPropertyNames : {distinguishedName, Path}
MemberType              : PropertySet
Value                   : DefaultDisplayPropertySet {distinguishedName, Path}
TypeNameOfValue         : System.Management.Automation.PSPropertySet
Name                    : DefaultDisplayPropertySet
IsInstance              : False

Now, in my (not so humble) opinion, the best way to deal with this situation would be to check the relevant properties of the object if they have a value:

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

Posted 20:46 [permalink]