Tuesday, April 3, 2012

Month of Lunches - Day 23


Day 23 - Debugging Techniques

Todays Chapter covers the various techniques to debug your script. This has the potential to be the most immensely useful chapters in this book. Writing debugging into your script seems like a lot more work but in the end if you are writing a distributable module this could save you and the end users a lot of time troubleshooting issues with its functionality. So lets dive right in.

In debugging PowerShell Don explains that there are only really two types of errors. Syntax Errors and Logic errors. Syntax errors are exactly as they sound. These are a mistyping of a cmdlet, function, or parameter (basically a misspelling)  or a syntax error in the way you type your commands. These error are basic and can be resolved normally by a quick review of the script. Most of the Third party Scripting Environments have a spell check function. In PowerShell v3 it will actually underline a grammatical error as well and there are third party modules that create a function to spell check your script (James Brundages Spell Checker ” http://blogs.msdn.com/b/mediaandmicrocode/archive/2009/12/09/test-spelling.aspx). In going through this book there have been at least three occasions where I got stuck on a script due to one of these errors. Pay attention and read carefully.

The second type of error that Don discusses is a Logic Error. Dons explanation here is great, "This means that the script or command isn't doing what you want it to do". But as he explains these are not always easy to troubleshoot. Sometimes they display I fairly concise error that will lead you directly to the problem, sometimes they give you no error at all, and sometimes with certain commands they give a generic error. Such is the case with WMI if a connection could not be made with the remote computer. All you get in this case is "RPC Server Not Found" The book example of this is a very good illustration of what these can look like and also contains a syntax error. Lets see if you can find them, I caught the easy one.

                $Name = Read-Host "Enter Computer Name"
                          If (test-connection $name) (
                                get-wmiObject Win32_Bios -computername $Nmae
                          )

The First step in debugging is to identify your expectations. What exactly is it that you want the script to do.

The Second is to put in the pieces for the actual debugging. There are two ways we cover on how to do this. The first is Trace Code. With Trace Code you first need to set the $DebugPreference variable in PowerShell to allow you to see it. This can be done in the script so that you do not have to manually set it. As with the $ErrorActionPreference you set this to "Continue". The cmdlet for the Trace Code is Write-Debug and allows you to actually view what the script is doing. Book Example.

                $Debugpreference = "Continue"
                $Name = Read-Host "Enter Computer Name"
                Write-Debug "`$Name contains $name"
                          If (test-connection $name) (
                                Write-Debug "Test-Connection was True"
                                get-wmiObject Win32_Bios -computername $Nmae
                          ) else (
                                Write-Debug "Test-Connection was False"
                         )

The Second way of handling debugging is with Breakpoints. These are a defined area where a script will pause, allowing you to check what the script is doing at that exact moment (Properties of Variables, current output, etc…). These can be defined at particular lines, at the changing of a variable, or when a specific cmdlet completes. The easies way to set these is in the ISE from the menu or with their hotkey (F9).

All in all debugging in PowerShell is very useful for troubleshooting and you really do need to read up on these to learn more about them and integrate them into your own scripts.

Monday, April 2, 2012

Month of Lunches Posts

I have been seriously slacking on posting my month of lunches blogs. Im going to try and get better at that. Anyway its not that i was not doing them i was just emailing them to Don for submission and never posting them here. So here they all are.

On another note I entered the Scripting Games for the first time this year. I dont feel that i am an advanced powershell user by any stretch of the imagination but i am an IT Pro. I entered in the beginner category because i have no scripting back ground (at all) and have only done a couple of classes on powershell. We will see how it goes. I will be posting the scenarios and my script responses to them as they expire. That way i cant be accused of cheating.

Month of Lunches - Day 22


Day 22 - Trapping and Handling Errors

I can lie. I skipped ahead in the book when I ran into a problem at work and went through this chapter once already. It prompted me to go another route with my thinking and to re-write the script that I was writing to gather some info to include error handling. Ill get into that a little more later on.

PowerShell doesn’t handle non-terminating errors directly, so in order to make error handling work you need to turn that error into an exception, or as Don puts it turn a non-terminating error into a terminating error, and force the shell do what you tell it to do.

The first thing we cover in the chapter is PowerShells default error-handling variable. $ErrorActionPreference. By default this is set to continue when you start the shell. You can specify the $ErrorActionPreference for the entire script by setting this variable at the top of the script. The settings for this variable are
                SilentlyContinue - Just keep going and don’t display an error message for non-terminating errors
                Continue - Display the error message but keep going anyway
                Inquire - Ask what you want to do at an interactive prompt
                Stop - Stop executing and throw an exception

Just a side note here: Regardless of the setting of this variable in a script if a terminating error occurs an exception is thrown and the script will stop.

Another way of handling errors on a slightly more granular basis is by utilizing the -ErrorAction parameter that is available on almost all cmdlets that are built into PowerShell.
                Get-WmiObject Win32_Service -computer localhost,notonline -erroraction 'silentlycontinue'

The preceding command will query WMI's Win32 service namespace for the two listed PC's and unless you have a computer named notonline will cause an error, only it will not display because we have specified to silently continue. This method is especially useful in the pipeline to display errors associated with individual objects and allowing the script to continue.

The first actual way of error handling in a script that we cover in this chapter is a Trap construct. Don says this is the more complicated of the two error handling methods in the chapter mainly due to the traps use of scopes. I would agree with that. Basically a trap is a way to catch errors in a script, but it must be specified in the script before an error can occur but it must be in the same scope or the parent scope. I do not fully understand this yet and need to look into it further so for more info please read the available documentation (help about_trap).

The second method is the easier to read and understand Try construct. With a Try construct there are three parts (not all have to be used). The first is TRY, and holds the original command you want to perform. The second part is CATCH, and is what is executed if an exception is thrown by the try portion. This can be used to log the error information to a text file, execute another command to repair an issue, or any other number of things. The last piece is FINALLY, and executes on all objects passing through the Try construct regardless of error. This portion is option. Here is the book example:

                Function Get-Stuff {
                          Begin {
                                del c:\errors.txt -ea slientlycontinue
                          }
                          Process {
                                Try {
                                     Get-WMIobject Win32_bios -computer $_ -ea Stop
                                } Catch {
                                     $_ | Out-File C:\Errors.txt -append
                                }
                          }
                }
                'Localhost','NotOnLine','Server-R2' | Get-Stuff

 As I stated before I skipped ahead for error handling on a script I was trying to make for work and this is the method I chose. I will be posting that up for my final script but would like to go over it a little more before I do and document it. Maybe now that I know how, ill put it up as a module so if anyone else wants to use it they can. Until tomorrow have a great day.

Month of Lunches - Day 21


Day 21 - Creating your Own "cmdlets" and modules

I decided to try and get ahead and get through the last couple of chapters over the weekend since monday is the start of "The Scripting Games", and im going to take part this year. I want to test what I have learned so far. Problem is that its now late saturday night and im going through it for the third time.

Todays chapter is over building your functions to act like cmdlets, basically making them into cmdlets. Just writing a function doesn’t allow them to act like cmdlets you see. If you write in parameter functions then they accept them, but there are special things that you must do to allow them to accept pipeline input.

Pipeline function Is the first topic of the day, these are otherwise known as filtering functions. These types of functions allow for the input of either one or multiple objects. They are formated as such:


function get-manmodinfo {
     Begin {}
     Process {
          $computer = $_
               $computerSystem = Get-WmiObject -ComputerName $computer Win32_computerSystem                                 
               $processor = Get-WmiObject -ComputerName $computer win32_processor
               $obj = New-Object -TypeName PSObject
               $obj | Add-Member -MemberType Noteproperty `-name Manufacturer -Value $ComputerSystem.Manufacturer
               $obj | Add-Member -MemberType NoteProperty `-name Model -Value $computerSystem.Model
               Write-Output $obj
     }
     END {}
}
get-content computers.txt | get-manmodinfo | Format-Table -AutoSize 

The only issue with this is that it will now only accept pipeline inputs. You could add a parameter block and the script will still function with the pipeline input but the parameter command, Get-ManModInfo -computer (get-content computers.txt) will not accept the multiple objects. Don explains that in order to make these functions act as cmdlets do you will need to break them them down into two separate parts with a what he calls a worker function. Ive show this below:



function manmodinfowork {
          param ([string]$computername)
                $computerSystem = Get-WmiObject -ComputerName $computername win32_computerSystem
                $processor = Get-WmiObject -ComputerName $computername win32_processor

                $obj = New-Object -TypeName PSObject
                          $obj | Add-Member -MemberType Noteproperty `
                            -name Manufacturer -Value $computerSystem.Manufacturer
                          $obj | Add-Member -MemberType NoteProperty `
                            -name Model -Value $computerSystem.Model
          Write-Output $obj
}

Function Get-ManModInfo {
          Param (
                [string[]]$computername
          )
          Begin {
                $usedparameter = $false
                if ($PSBoundParameters.containskey('computername')) {
                          $usedparameter = $true
                }
          }
          Process {
                if ($UsedParameter) {
                          foreach ($computer in $computername) {
                                manmodinfowork -computer $computer
                }
                } else {
                          ManModInfoWork -computer $_
                }
          }
          End {}
}

get-manmodinfo -computer (get-content C:\Users\Chris\Documents\Scripts\computers.txt)
get-content C:\Users\Chris\Documents\Scripts\computers.txt | get-manmodinfo | Format-Table -AutoSize 


Now as you can see it will accept both the Pipeline and Parametized input acting just as a cmdlet would. You can add or modify this to add further Paramters in the WorkInfo function. The functions can get far more advanced from here and add all kinds of support for many other parts for PowerShell that I just don’t have the time or space to cover. You would be far better served to read the help file or any of the documentation available on advanced functions.

The last thing I will cover here is the packaging of the function as a module for use and distribution. This is a simple process and only required that you save the script file as a .PSM1 file. These are module files. Now you can place these in the module directory under c:\Windows\System32\windowspowershell\v1.0\modules. For use by anyone on the computer or place them in the personal profile modules directory under your profile\documents folder.

And that is it. Just distribute the PSM1 files for other personal to use and set back and wait for the complaints about the way it works to fly in, or the requests for more features. Some people want to be PowerShell Lazy without the work.

Month of Lunches - Day 20

Day 20 - Adding Logic and Loops
Up until now we havent really been scripting. Its basically been single or linked commands, and the most advanced things we covered is WMI, parameters, and functions. Today, according to Don, is when we start getting to "Scripting" and its slightly more programming oriented constructs.
First up, IF! The IF construct is similar to that of DOS or the CMD shell IF. It allows you to right a command that would return a true or false ($True or $False in PowerShell) and have another command performed. Microsoft expounds on the IF statement with ElseIF and Else. The ElseIF is what would run in the case that your IF statement is not true. Else would be what would run if the statement is neither true or false, say if your command returned a value instead of true or false. The syntax is pretty straight forward

If (property-eq '1000') {
     #command you want to perform#
}ElseIF { Property-eq '999') {
     #command you want to perform#
}Else {
     #final command you want to perform#
}

Here is an example that I created to look at every service name starting with the letter B. It labels the ones I am looking for and annotate the ones that I don’t. I even color coded the Write-Host output. I Went a little Obi Wan here but it gets the point across.


$services = Get-Service -Name 'B*'
     foreach($Service in$services) {
          If($service.servicename -eq 'BITS') {
               Write-Host'Background Intelligent Transfer Service' -fore Red
          } ElseIf($service.servicename -eq 'BDESVC'){
               Write-Host'Bitlocker Encryption Service' -fore Red
          } Else{
               Write-Host'These are not the services you are looking for'`
               -fore black-back White
          }
     }
The Next construct the we learn about is Switch. This will compare the contents a property to a wide range of values and runs a specified command for each. The syntax is very similar to the IF construct with the parentheses for the property you are looking for and curly braces for the commands. There is also a keyword to stop processing here, Break. Using this will cause the processing to cease immediately which could be useful in some situations.
Up next is the For construct. This will allow you to put a script block into a loop for a specified number of times. Once again the syntax is the same for this as it is with the previous two.
Now we get to ForEach. This is the most useful of these constructs and the one I have spent the most time on. Notice that I use this one in previous script example. ForEach allows you to enumerate each object in a collection and perform a command against it. In my previous example I created a variable that stored each service that started with the letter 'B'. I then put that variable into aForEach loop that looks at each service object and enumerates the service name property. If it matched the value I was looking for I had it Write-Host the name of the service. If it did not then I had it Write-Host that it wasn’t what I was looking for.
All in all this was a very enjoyable lesson and I learned quite a lot about the way that constructs work and how to format them. I actually wrote a script by myself and didn’t even use the internet. Im so proud of myself. : )

Month of Lunches - Day 19


Day 19 - From Command to Script to Function

Todays chapter is all about creating functions out of commands and scripts that you have created. Functions allow for easy sharing of the tools without all the fuss of teaching everyone how to modify it for their own use.

One way of doing this is to wrap your script In a function declaration much like the parameter declaration we learned about in chapter 17. Only with a function you actually give it a cmdlet like name. Here I have converted my previous parameterized script into a function, leaving the parameter in place so that we can still specify the -computer parameter when performing the function.

       function get-manmodinfo {
                                Param (
                                                $computer = 'localhost'
                                )
                               
                                $computerSystem = Get-WmiObject -ComputerName $computer win32_computerSystem
                                                write-host -NoNewLine "Computer Manufacturer: "
                                                Write-Host -ForeGroundColor red ` $computerSystem.manufacturer
                                                write-host -NoNewLine "Computer Model: "
                                                Write-Host -ForeGroundColor red ` $computerSystem.model
                }

Once this function is created in your sessions you can now just run

                Get-ManModInfo -computername 'anything'

This will display the same settings we were getting before. Now you can use this the same as a cmdlet for remote gathering of data or piping it out to export data or formatting a table or list. You can do what ever you want with it (within reason I suppose : ).

As we have gone over numerous times and I have read all over, PowerShell does not like text. Actually I remember reading "Every time you write a script that outputs text, God kills a puppy". So how do you get all those text tables and lists into an object. Section 19.5 has you covered there. Creating PSObjects.

In my previous example we are outputting with the Write-Host which is putting text to screen. Lets turn that into an object that PowerShell can better use. The way this is done is with the New-Object cmdlet. Below is my script. We will walk through and ill see if I can explain this in the right way.

                function get-manmodinfo {
                                param (
                                $computer = 'localhost'
                                )
   
                                $computerSystem = Get-WmiObject -ComputerName $computer win32_computerSystem

                                $obj = New-Object -TypeName PSObject
                                $obj | Add-Member -MemberType Noteproperty `
                                  -name Manufacturer -Value $ComputerSystem.Manufacturer
                                $obj | Add-Member -MemberType NoteProperty `
                                  -name Model -Value $computerSystem.Model

                                Write-Output $obj
                }

                get-manmodinfo | Format-Table -auto

Month of Lunches - Day 18

You know what really sucks. When you spend alot of time typing a paper and dont have autosave turned on, or backups and then you accidentally delete everything. Well that is exactly what happended to day 18's post.

I just didnt have the energy to re-write it. Dealing with a 3 year old, an 8 week old, and a whole list of Honey-Do's just zapped me.