OMG some things in Powershell are just too confusing to be useful. What if you need to see the last error message again. What if you want to write it into your script to email you when the error happens?
Well first, its all in $Error
However, $Error is an array. To access it really requires notation like this:
$Error[0]
The [0] says give me back the last error. Where [1] would say to give me back the second to last error message.
The Problem….
Ok now here is where it gets “funky”. If you just type $Error[0] you get the entire error message like so: (note I am using an error message from some Lync work I have been doing, the names have been changed to protect well me lol)
Set-CsUser : Management object not found for identity “Jerry.Springer@Contoso.com”.
At C:\Scripts\EnableLyncUsers.ps1:138 char:15
+ Set-CsUser <<<< -Identity $user.UserPrincipalName -SipAddress $user.UserPrincipalName
+ CategoryInfo : NotSpecified: (:) [Set-CsUser], ManagementException
+ FullyQualifiedErrorId : Microsoft.Rtc.Management.AD.ManagementException,Microsoft.Rtc.Management.AD.Cmdlets.S
etOcsUserCmdlet
BUT…. if you type write-host $Error[0] you get this:
Management object not found for identity “Jerry.Springer@Contoso.com”.
So what gives right??? Why when you use Write-Host OR even better when you try to email $Error[0] do we get the crappy short error message? Well I don’t have the answer BUT I do have a great work around.
The Solution….
[string]$ErrorString = $Error[0].Exception
[string]$ErrorString = $ErrorString + ” `n `n ”
[string]$ErrorString = $ErrorString + $Error[0].InvocationInfo.PositionMessage
(that’s 3 lines BTW)
As far as I can tell the only thing one needs are the short error message and the line, script, and command. To do this use the code above and then simply use Write-Host or email that new $ErrorString variable. If you need other data follow the info below from how I figured this out.
Emailing the Error? Simply use this code (replace stuff inside of < > then remove the < >):
[string]$ErrorString = $Error[0].Exception
[string]$ErrorString = $ErrorString + ” `n `n ”
[string]$ErrorString = $ErrorString + $Error[0].InvocationInfo.PositionMessage
$SmtpClient = new-object system.net.mail.smtpClient
$MailMessage = New-Object system.net.mail.mailmessage
$SmtpClient.Host = “<SMTP IP OR NAME>”
$mailmessage.from = <from@domain.com>
$mailmessage.To.add(“email1@domain.com,email2@domain.com”)
$mailmessage.Subject = “<Subject of Email>”
$MailMessage.IsBodyHtml = $false
$mailmessage.Body = $ErrorString
$smtpclient.Send($mailmessage)
How did I figure this out?
First I indexed $Error to get me the first result [0]
Next I used the power of Get-Member
$Error[0] | Get-Member
This dumped out all the properties
TypeName: System.Management.Automation.ErrorRecord
Name MemberType Definition
—- ———- ———-
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetObjectData Method System.Void GetObjectData(System.Runtime.Serialization.SerializationInfo inf…
GetType Method type GetType()
ToString Method string ToString()
CategoryInfo Property System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}
ErrorDetails Property System.Management.Automation.ErrorDetails ErrorDetails {get;set;}
Exception Property System.Exception Exception {get;}
FullyQualifiedErrorId Property System.String FullyQualifiedErrorId {get;}
InvocationInfo Property System.Management.Automation.InvocationInfo InvocationInfo {get;}
PipelineIterationInfo Property System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Int32, mscorlib,…
TargetObject Property System.Object TargetObject {get;}
PSMessageDetails ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Exc…
All of the properties normally can be accessed like this:
$Error[0].Exception
But if you try to write-host $Error[0].InvocationInfo you get:
System.Management.Automation.InvocationInfo
Well that’s not very useful… the reason for this is there are deeper items in the $Error[0].InvocationInfo tree. So if we go ahead and whip out get-member again on $Error[0].InvocationInfo lets see what we get:
TypeName: System.Management.Automation.InvocationInfo
Name MemberType Definition
—- ———- ———-
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
BoundParameters Property System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Cu…
CommandOrigin Property System.Management.Automation.CommandOrigin CommandOrigin {get;}
ExpectingInput Property System.Boolean ExpectingInput {get;}
HistoryId Property System.Int64 HistoryId {get;}
InvocationName Property System.String InvocationName {get;}
Line Property System.String Line {get;}
MyCommand Property System.Management.Automation.CommandInfo MyCommand {get;}
OffsetInLine Property System.Int32 OffsetInLine {get;}
PipelineLength Property System.Int32 PipelineLength {get;}
PipelinePosition Property System.Int32 PipelinePosition {get;}
PositionMessage Property System.String PositionMessage {get;}
ScriptLineNumber Property System.Int32 ScriptLineNumber {get;}
ScriptName Property System.String ScriptName {get;}
UnboundArguments Property System.Collections.Generic.List`1[[System.Object, mscorlib, Version=2.0.0.0, Culture=…
Ah… there’s more stuff. Lastly I just needed to figure out what items inside of $Error[0].InvocationInfo I needed. Turns out just one thing. So to write-host it all I needed to do is call:
Write-Host $Error[0].InvocationInfo.PositionMessage
Hope that opens your mind to how more complex objects work in Powershell.
Hey!
Did I help? Make Sense? Something Wrong? Put it in the comments. Love to hear when my write-ups help folks out.
Enjoy
-Eric