Posts RSS Comments RSS 249 Posts and 391 Comments till now

Archive for the 'ErrorHandling' Category

Get-CitrixHotfix: The Bitter/Sweet of Write-Verbose

There are a whole host of of Write-* Cmdlets.

Write-Debug
Write-Error
Write-Host
Write-Output
Write-Progress
Write-Verbose
Write-Warning

Each one of these are useful, but I want to specifically talk about Write-Verbose. Note, Write-Debug work basically the same. Also, for those of you that follow the Powershell Podcast over at http://powerscripting.wordpress.com some of this was covered already in Episode 11.

The way Write-Verbose works is that it uses $VerbosePreference to determine what to do. This is very useful because it gives you the ability to easily control if the string is written to the host. I think this is a good time to clarify that write-verbose only writes to the host and does NOT pollute the output stream which is SUPER useful.

Lets point out the goods and bads

Goods
1) Can use a switch Parameter to easily control the console info (I normally use $verbose)
2) It writes to host so you dont pollute the object output
3) Nice for writing Data to the console on scripts that take a long time to run

Bads
1) It has a header on every line that cannot be removed “VERBOSE:”
2) It is Yellow (eek!)

The good news is you can control the color using $host.PrivateData
More Info http://ps1.soapyfrog.com/2007/01/29/debug-and-verbose-colouring/

The following is a script that I wrote that collects Citrix Hotfixes. I have a large number of servers so I wanted to be able see where I was and I also wanted a overview of the hotfixes, but I also wanted to output and object for filtering purposes. In this case Write-Verbose was perfect. I was able to write to the screen (host) what I wanted to see without changing the object output. I was also able to control whether it was outputed by using a switch parameter to control $VerbosePreference.

Param($Server,$log,[switch]$Farm,[switch]$Verbose,[switch]$debug)
# Get-CitrixHotfix.ps1

if($Verbose){$VerbosePreference = "Continue"}
if($debug){Set-PSDebug -Step}

Write-Host "`nProcessing…`n"

if($Farm)
{
    # Get Farm Object
    $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
    $CTXFarm = [system.Activator]::CreateInstance($type)
    $CTXFarm.Initialize(1)

    # Creating Collection for Custom Objects
    $myCol = @()

    foreach($Srv in $CTXFarm.Servers)
    {
        # Create Custom Object
        $myobj = "" | Select-Object Name,Hotfix
        $myobj.Name = $Srv.ServerName
        $myobj.Hotfix = @()

        Write-Verbose $Srv.ServerName

        # Get Hotfix Information for the Server and add to Custom Object
        $CTXServer = $CTXFarm.GetServer2(6,$Srv.ServerName)
        $CTXServer.winServerObject2.hotfixes | %{Write-Verbose " – $($_.Name)";$myobj.HotFix += $_.name}

        # Add Server Object to Collection
        $myCol += $myobj
    }
    # Output Collection
    if($log)
    {
        @(foreach($obj in $mycol)
        {
            Write-Output $obj.Name
            foreach($hf in $obj.Hotfix){write-Output " – $hf"}
        }) | out-File $log -enc ASCII
    }
    else
    {
        $mycol
    }
}
else
{
    $myobj = "" | Select-Object Name,HotFix
    $myobj.Name = $Server
    $myobj.HotFix = @()
    $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
    $mfServer = [system.Activator]::CreateInstance($type)
    $mfServer.Initialize(6,$Server)
    Write-Output $Server
    $mfServer.winServerObject2.hotfixes | foreach-Object{$myobj.HotFix += $_.Name}
    $myobj
}

write-host

if($debug){Set-PSDebug -off}

Error Handling: Part3 – The Power of Set-PSDebug

From a scripting perspective this CMDLet is one of the greatest tools you could possibly have access to. The guys over at the Powershell Podcast also did a quick overview in Latest Podcast at http://powerscripting.net

Lets start with an overview of the CMDLet. A good portion of these will be covered in the help file (PS> get-help Set-PSDebug -full) So I highly recommed reviewing that documentation as well.
-trace
    0 – turn off tracing
    1 – trace script lines as they are executed
    2 – trace basically everything
-step: Will prompt you for action
    [Y] Yes (Default): Will Process the line and prompt again at the next.
    [A] Yes to All: Will process all lines, Usually used after you Caught what you were looking for.
    [N] No: Will STOP processing on all lines, effectively terminating the script.
    [L] No to All: Will STOP processing on all lines, effectively terminating the script.
    [S] Suspend: This option is awesome. This will open another runspace where you can inspect the
         environment from the shell. Basically, if you want to figure out what the state of a variable or object
         is at a given time. You can just Suspend and get the information or change it.
         Type exit goes to the next prompt.

Set-PSDebug -step
Continue with this operation?
   1+ C:\tools\Testme.ps1 hello
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
DEBUG:    1+ C:\tools\Testme.ps1 hello

Continue with this operation?
   2+ $myhi = $hi
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
DEBUG:    2+ $myhi = $hi

Continue with this operation?
   3+ Write-Host "$Myhi was passed" -fore Green
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
DEBUG:    3+ Write-Host "$Myhi was passed" -fore Green
hello was passed

    Here are some gotcha’s for Stepping
    - “[A] Yes to All” effectively turns off stepping
    - “[N] No” and “[L] No to All” both do the same thing… Kill the processing.
    - -step Automatically sets debug trace to level 1

-off: Turns off Debugging

-strict: Variable must be assigned a value before being referenced (Kind of like Option Explict in Vbscript.)

Set-PSDebug -strict
$myvar                     # This line will throw an error
$myvar = "Hi There"
$myvar                     # This line will output "Hi There"

What does the Debug output look like?

Here is the script we will be debugging

Param($hi)
function foo{
   Param($iFoo)
   Return "$iFoo was Passed"
}
$myvar = foo $hi
Write-Host $myvar

Here is the script with Set-PSDebug -Trace 1

PS: Set-PSDebug -Trace 1
PS: C:\tools\Testme.ps1 Hi
DEBUG:    1+ C:\tools\Testme.ps1 Hi
DEBUG:    2+ function foo{
DEBUG:    6+ $myvar = foo $hi
DEBUG:    4+    Return "$iFoo was Passed"
DEBUG:    7+ Write-Host $myvar
Hi was Passed

Things to Notice
- Line numbers are referenced in the order they were called
- The Debug info is Prefaced with “DEBUG:”
- Stepping is not used by default

Here is the script with Set-PSDebug -Trace 2

PS: Set-PSDebug -Trace 2
PS: C:\tools\Testme.ps1 Hi
DEBUG:    1+ C:\tools\Testme.ps1 Hi
DEBUG:     ! CALL script ‘Testme.ps1′
DEBUG:    2+ function foo{
DEBUG:    6+ $myvar = foo $hi
DEBUG:     ! CALL function ‘foo’  (defined in file ‘C:\tools\Testme.ps1′)
DEBUG:    4+    Return "$iFoo was Passed"
DEBUG:     ! SET $myvar = ‘Hi was Passed’.
DEBUG:    7+ Write-Host $myvar
Hi was Passed

Things to Notice
- You Now see calls to scripts, functions and variable assignments
- Calls to Scripts, Functions, and Setting Variable are prefaced with !
- Calls to function tell you where they came from

Summary:
I hope this post does justice to Set-PSDebug. It is extremely useful and powerful for writing scripts. I truly hope you take some time to play with this CMDLet and see where it takes you. It has done wonders for me.

Bonus:
Wanna see how the evironment effects a CMDLet or even just a little peak under the hood? Use Set-PSDebug -trace 2

69# Set-PSDebug -trace 2
DEBUG:    1+ Set-PSDebug -trace 2
70# Get-ChildItem c:\fes
DEBUG:    1+ Get-ChildItem c:\fes
DEBUG:    2+                                     if ($ErrorView -ne "CategoryView") {
DEBUG:    3+                                        $myinv = $_.InvocationInfo
DEBUG:     ! SET $myinv = ‘System.Management.Automation.InvocationInfo’.
DEBUG:    4+                                        switch -regex ($myinv.MyCommand.CommandType)
DEBUG:   24+                                                if ($myinv.MyCommand.Name)
DEBUG:   26+                                                    $myinv.MyCommand.Name + " : "; break;
DEBUG:   26+                                                    $myinv.MyCommand.Name + " : "; break;
DEBUG:    2+                                     if ($_.InvocationInfo) {
DEBUG:    3+                                         $posmsg = $_.InvocationInfo.PositionMessage
DEBUG:     ! SET $posmsg =
At line:1 char:14
+ Get-ChildItem  <<<< c:\fes’
.
DEBUG:    7+                                     if ($ErrorView -eq "CategoryView") {
DEBUG:   11+                                         $_.Exception.Message + $posmsg
Get-ChildItem : Cannot find path ‘C:\fes’ because it does not exist.
At line:1 char:14
+ Get-ChildItem  &lt;&lt;&lt;&lt; c:\fes

Error Handling: Part2 – Throw “A word about Trapping”

I was writing a post on this, but Joel (Jaykul) beat me to the punch (he did a great job to.)

http://huddledmasses.org/trap-exception-in-powershell

Error Handling: Part1 – $Error Objects

Error handling is one thing that is not discussed as much as it should be. It is one the most important parts of working with any scripting language and in this case the interactive nature of Powershell. We have had much discussion in the IRC channel over the last couple of weeks, discussing this exact topic. So I thought I should do my best to post what I have gleaned from the discussion.

This is one of at least a two part (maybe Three) series.
* Error Handling: Part1 – $Error Objects
* Error Handling: Part2 – Throw “A word about Trapping”
* Error Handling: Part3 – The Power of Set-PSDebug

Ask anyone that writes scripts. The ratio for Error correction in code is INSANE. Its almost 2/1. A 10 line script soon becomes 30 and a simple script becomes insanely complicated add to this the interactive nature of Powershell and you have the potential for many headaches. While Error handling in Powershell is far superior than anything to date… it still has some qwerks. I will do my best to cover some of these. Before we proceed from here, It is VERY important that you understand scope. I started to write this up, but to be honest, I think the Powershell team did a great job here. Please take some time to read the about file for scope.
PS> get-help about_Scope

Now, lets start off with some of the basics:

There are several built-in objects in powershell that control and provide information about Errors
$Error
> This is a object that stores an array of RICH Error objects
> Type: System.Management.Automation.ErrorRecord
$MaximumErrorCount:
> Total number of ErrorRecord objects that will be saved in $error
$LASTEXITCODE
> Exit Code of the Last script or native command than ran (i.e. ping.exe)
> This does NOT include cmdlets
$?
> Boolean Value that is $false if an error occured on the last command.
$ErrorActionPreference
> Determines the Default action if error is thrown
> Type System.Management.Automation.ActionPreference
> Continue: Outputs error, but keeps processing
> SilentlyContinue: No output and it keeps going
> Inquire: Prompt user for action
> Stop: Outputs error and halts processing
$ErrorView
> This is how the error will output to the screen
> This truly is a variable and can contain anything, but will use NormalView if anything other than CategoryView is the value
> NormalView: Default. Outputs semi-verbose description,Line Number, and character
> CategoryView: Only displayes the category the error is in.

Now that we have an overview of the objects that make up the error environment in Powershell lets deep dive into a couple of the more important ones.

$Error: This is by far the object with the most amount of data… it really does contain pretty much everything you could possibly want to know about an error. It is important to realize that this is truly an object. It has properties, methods, and even a Type just as all the other objects have. You can see this by piping $error to get-member

PS&gt; $error | get-member
   TypeName: System.Management.Automation.ErrorRecord

Name                      MemberType Definition
====                      ========   ========
Equals                    Method     System.Boolean Equals(Object obj)
GetHashCode               Method     System.Int32 GetHashCode()
GetObjectData             Method     System.Void GetObjectData(SerializationInfo info, StreamingContext context)
GetType                   Method     System.Type GetType()
get_CategoryInfo          Method     System.Management.Automation.ErrorCategoryInfo get_CategoryInfo()
get_ErrorDetails          Method     System.Management.Automation.ErrorDetails get_ErrorDetails()
get_Exception             Method     System.Exception get_Exception()
get_FullyQualifiedErrorId Method     System.String get_FullyQualifiedErrorId()
get_InvocationInfo        Method     System.Management.Automation.InvocationInfo get_InvocationInfo()
get_TargetObject          Method     System.Object get_TargetObject()
set_ErrorDetails          Method     System.Void set_ErrorDetails(ErrorDetails value)
ToString                  Method     System.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;}
TargetObject              Property   System.Object TargetObject {get;}

Note: Because $error is an array it inherits from [System.Array] and its methods. Clear() is the most notable.

I could probably do a whole post on just $error, but that is beyond the scope of this one so I want just cover a few properties and one method
ErrorDetails
> This provides the Details of the Error. I wanted to note this is often blank
Exception
> This is the actual exception that was thrown
> This is an object so it will have properties and methods of its own.
TargetObject
> This is the object that operation was being performed when the error occured.
InvocationInfo
> This contains detail information about the command that was run (i.e. Line Number, CMDlet, and Position)
Clear()
> This method clears the $error array.

More Info: http://msdn2.microsoft.com/en-us/library/system.management.automation.errorrecord.aspx

$ErrorActionPreference: I would say this is the second most important object for error handling. I have personally seen this question posted at least 10 times on the News Groups, “What is the powershell equivalent to “On Error Resume Next.” The answer lies in this object more specifically by setting $ErrorActionPreference to “SilentlyContinue.” One thing I would like to point out about this particular object is that it is scope specific. This is very important to understand. Learning to control error action at the lowest level is key to writing good scripts and knowing the state of this object can save you a ton of heartache. I cant tell you how many times I beat my head against the wall trying to figure out why I wasnt getting any error information… hmmm, $erroractionpreference set to “SilentlyContinue,” maybe that is the problem.

More Info Here: http://msdn2.microsoft.com/en-us/library/system.management.automation.actionpreference.aspx

In Summary, I think this a good overview of the Error Variables/Objects that you have access to, but it really only scratches the surface. I would suggest spending some time on MSDN looking through system.management.automation Namespace and get a good idea of all the information there.