Skip to main content

Creating an Error Log

I am actually enjoying my ride at 35,000 feet right now.  I am completely disconnected from the world, listening to AD/DC and writing PowerShell code.  I know, I am a bit weird at times but it pays the bills.
This is day 1 or 5 in how to add error logs to your code.
Before I took off, I saw a request on the PowerShell Facebook page about how to record the error objects created during a script run when user accounts are not present.  This leads to a very interesting dilemma.  PowerShell records all errors in the current session as an error object.  Give it a try.
PS C:\> Bad-Command
Bad-Command : The term 'Bad-Command' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At line:1 char:1
+ Bad-Command
+ ~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Bad-Command:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException


PS C:\> $Error[0] | GM


   TypeName: System.Management.Automation.ErrorRecord

Name                  MemberType     Definition                                                                                                                                      
----                  ----------     ----------                                                                                                                                      
Equals                Method         bool Equals(System.Object obj)                                                                                                                   
GetHashCode           Method         int GetHashCode()                                                                                                                                
GetObjectData         Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISerializ...
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       string FullyQualifiedErrorId {get;}                                                                                                              
InvocationInfo        Property       System.Management.Automation.InvocationInfo InvocationInfo {get;}                                                                               
PipelineIterationInfo Property       System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {get;}                                                             
ScriptStackTrace      Property       string ScriptStackTrace {get;}                                                                                                                   
TargetObject          Property       System.Object TargetObject {get;}                                                                                                               
PSMessageDetails      ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Exception.InnerException.PSMessageDetails };}  

The dilemma that you will face is do you use this object or create a customized one?  Personally, it depends on what I am trying to accomplish.  If I need the exact error object, then I utilized PowerShells built in capability and I utilize the System.Management.Automation.ErrorRecord object that is created.  If I am looking at creating more customized error messages, then I create the object myself.
There is another problem that we need to consider.  When you create your own error object, you can pipe it to Export-CSV and utilize the –Append parameter to simply add your custom error object to your error log.  The error object created by PowerShell is not a 2D object.  It has multiple levels of depth that a CSV file cannot store.  Here is an example:
PS C:\> $Error[0] | Select *


PSMessageDetails      :
Exception             : System.Management.Automation.CommandNotFoundException: The term 'Bad-Command' is not recognized as the name of a cmdlet, function, script file, or operable
                        program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
                           at System.Management.Automation.CommandDiscovery.LookupCommandInfo(String commandName, CommandTypes commandTypes, SearchResolutionOptions
                        searchResolutionOptions, CommandOrigin commandOrigin, ExecutionContext context)
                           at System.Management.Automation.CommandDiscovery.LookupCommandProcessor(String commandName, CommandOrigin commandOrigin, Nullable`1 useLocalScope)
                           at System.Management.Automation.ExecutionContext.CreateCommand(String command, Boolean dotSource)
                           at System.Management.Automation.PipelineOps.AddCommand(PipelineProcessor pipe, CommandParameterInternal[] commandElements, CommandBaseAst commandBaseAst,
                        CommandRedirection[] redirections, ExecutionContext context)
                           at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[]
                        pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext funcContext)
                           at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
                           at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
TargetObject          : Bad-Command
CategoryInfo          : ObjectNotFound: (Bad-Command:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
ErrorDetails          :
InvocationInfo        : System.Management.Automation.InvocationInfo
ScriptStackTrace      : at , : line 1
PipelineIterationInfo : {}

You can see that our error object has a property called Exception.  When I expand this property and pipe it to Get-Member, you can see that it is an object itself.
    TypeName: System.Management.Automation.CommandNotFoundException

Name                        MemberType Definition                                                                                                                                     
----                        ---------- ----------                                                                                                                                    
Equals                      Method     bool Equals(System.Object obj), bool _Exception.Equals(System.Object obj)                                                                     
GetBaseException            Method     System.Exception GetBaseException(), System.Exception _Exception.GetBaseException()                                                           
GetHashCode                 Method     int GetHashCode(), int _Exception.GetHashCode()                                                                                                
GetObjectData               Method     void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), void ISerial...
GetType                     Method     type GetType(), type _Exception.GetType()                                                                                                     
ToString                    Method     string ToString(), string _Exception.ToString()                                                                                                
CommandName                 Property   string CommandName {get;set;}                                                                                                                 
Data                        Property   System.Collections.IDictionary Data {get;}                                                                                                    
ErrorRecord                 Property   System.Management.Automation.ErrorRecord ErrorRecord {get;}                                                                                   
HelpLink                    Property   string HelpLink {get;set;}                                                                                                                     
HResult                     Property   int HResult {get;set;}                                                                                                                        
InnerException              Property   System.Exception InnerException {get;}                                                                                                        
Message                     Property   string Message {get;}                                                                                                                          
Source                      Property   string Source {get;set;}                                                                                                                      
StackTrace                  Property   string StackTrace {get;}                                                                                                                      
TargetSite                  Property   System.Reflection.MethodBase TargetSite {get;}                                                                                                
WasThrownFromThrowStatement Property   bool WasThrownFromThrowStatement {get;set;}


Only an XML file can handle multidimensional data.  The problem with the Export-CliXML cmdlet is that it does not have the capability to append to itself.  That is just the way XML is. 

Comments

Popular posts from this blog

How to list all the AD LDS instances on a server

AD LDS allows you to provide directory services to applications that are free of the confines of Active Directory.  To list all the AD LDS instances on a server, follow this procedure: Log into the server in question Open a command prompt. Type dsdbutil and press Enter Type List Instances and press Enter . You will receive a list of the instance name, both the LDAP and SSL port numbers, the location of the database, and its status.

How to run GPResult on a remote client with PowerShell

In the past, to run the GPResult command, you would need to either physically visit this client, have the user do it, or use and RDP connection.  In all cases, this will disrupt the user.  First, you need PowerShell remoting enabled on the target machine.  You can do this via Group Policy . Open PowerShell and type this command. Invoke-Command –ScriptBlock {GPResult /r} –ComputerName <ComputerName> Replace <ComputerName> with the name of the target.  Remember, the target needs to be online and accessible to you.

Error icon when creating a GPO Preference drive map

You may not have an error at all.  Take a look at the drive mapping below. The red triangle is what threw us off.  It is not an error.  It is simply a color representation of the Replace option of the Action field in the properties of the drive mappings. Create action This give you a green triangle. The Create action creates a new mapped drive for users. Replace Action The Replace action gives you a red triangle.  This action will delete and recreate mapped drives for users. The net result of the Replace action is to overwrite all existing settings associated with the mapped drive. If the drive mapping does not exist, then the Replace action creates a new drive mapping. Update Action The Update action will have a yellow triangle. Update will modify settings of an existing mapped drive for users. This action differs from Replace in that it only updates settings defined within the preference item. All other settings remain as configured on the ma...