Dec 212010

If you are writing a script that you want to (might) use again it can be really useful to use script parameters. PowerShell does some really cool things with parameters that many folks don’t know or realize you can do. This post covers some of those items.

To start us off let’s look at how to use a script parameter.

### Test-Param.ps1 ###
Param($Variable1 = "Hello", $Variable2 = "World")
"$Variable1 $Variable2"

If we save this script and run it we will get an output like this:

>>Hello World

This simple case shows that our variables are being assigned to the default values. We can change these values by passing them to the script via the command line. These values are passed either in the order that they are sent, or statically assigned by using the variables name.

>>./Test-Param.ps1 "Goodbye"
>> Goodbye World

>> ./Test-Param.ps1 -Variable2 "Universe"
>> Hello Universe

>> ./Test-Param.ps1 "Universe" -Variable1 "Goodbye"
>> Goodbye Universe

As you can see, these are really pretty flexible. Another really cool part of params that you may have noticed by now if you are following along is that these variable names will use tab complete. So if you type “./test-param -” and then start hitting tab, you will cycle through all the names in the param block. If yo haven’t used a script in a long time, then you can quickly see what variables you might want to pass.

Now, sometimes you want to have the param feature, but you need to require the value. You can place some logic after the param block, but I like to just put code straight in there.

### Test-Param.ps1 ###
Param($Variable1 = Read-Host "Please input a value for Variable1",
      $Variable2 = Get-Content $Variable1

>> ./Test-Param.ps1
>> Please input a value for Variable1: c:ScriptTest.txt
>> c:ScriptTest.txt
>> Hello, World!

>> ./Test-Param.ps1 c:ScriptTest.txt
>> c:ScriptTest.txt
>> Hello, World!

>> ./Test-Param.ps1 c:ScriptTest.txt "Ignore my file"
>> c:ScriptTest.txt
>> Ignore my file

I think that just about covers it for this Quick Tip. Get started converting all your scripts to use a parameter block!

Dec 212010

NOTE: I have written a better script for generic multithreading which I have covered in my post HERE. If you are looking for a script to cover your every day needs, please read that article instead as I believe it is a better script. This script, however, is easier to understand if you are looking to learn this for yourself!

This post is actually the reason that I created the blog. When I first started looking into multithreading in PowerShell V2 I really didn’t find anyone on the web that really had a good explanation or how-to. So, why would you want to multithread your scripts? Well, if you have ever tried to run a certain script against every server or even workstation in your org you know that it can take a very long time to run because it is hitting each server, one at a time, in sequence. Wouldn’t it be great if you could run your script against 20 servers at a time? As it turns out, you can, and it’s easier than you think.
All of the work comes in understanding the 4 main cmdlets surrounding multithreading in PowerShell V2: Start-Job, Wait-Job, Get-Job and Receive-Job. The basic flow states that you start all jobs, then wait for all jobs to finish, then see what jobs you have (get) and receive all the output.

So, the above basic script starts a job,then waits for all jobs to finish and then receives all the data that all jobs (only one in our case) contain. Now this basic construct is limited,because if you try to pass a variable via the code block it won’t work. That is because whatever code block you pass is a bit like opening a new PowerShell session, pasting it in, and hitting enter. Any variables that you have aren’t in that new pristine environment. The guys over at Microsoft gave us a way to pass info in though with the cmdlets argument “ArgumentList” where you can use a variable from the host session to be passed to the new session. This, however, only works when calling a script, not a code block, so we have to also use the argument “FilePath” and provide a second PowerShell script. This is actually a good thing, because it means we can multithread any script we write as long as it has a consistent output and takes something as an argument. Take the following short script:

Now, this script is perfect because it is going to return an object and take a computer name as an argument when executed. Presumably, we would normally multi-thread something that has a much longer execution time, but keep in mind that if the host is offline this script could take a long time to run.

So there it is. It’s that easy to multithread any script that you’ve written, but this is the most basic construct. What happens if you want to control how many threads are open? How about letting the user know where the script is in execution and how things are going? Let’s start by adding a param block to the front of the script to get a bunch of information.

Now that we have a way to get some basic setting from the user let’s read in out computer list from the file provided and kill any currently running jobs.

Now let’s get our loop control going and start making some threads

So this section of code is pretty simple if you just break it down. First we use a while loop to hold there until the current number of running jobs is lower than the number we have declared as our maximum. The Write-Progress command is simply letting the user know what’s going on. Once we clear the while loop we are ready to add jobs to the list of running jobs. So we start another job with the Start-Job command and then write our progress out to the user. Once this block of code is done we want to wait for all the jobs to close. In my short example I used Get-Job | Wait-Job which does the job, but it hides the progress from our user, so instead I developed this little tidbit.

So this block of code is going to read in the computer names that we are still waiting on and show them in the write-progress command. Again I’ve used a while loop which would run unchecked if not for the start-sleep that I’ve placed in there, which is like saying “Hey, only check our progress every half second or so”. If I didn’t have the start-sleep it would simply max the processor.

Once all that is done, it is simply a matter of getting the output from all our workers. You can just use Get-Job | Receive-Job to spit is all out to the console or you can push any object out to PowerShell V2’s grid view which I love oh so much.

So there is a script which allows you to multithread any script in your arsenal. Enjoy!

The full text for the script I use is as follows:

Dec 212010

Often times you want to use an object’s specific property to let the user know what’s going on. The problem is that PowerShell won’t allow its dot notation within quotes. When you first try it you might be confused by the results.

$Files = Get-ChildItem
"This directory has $Files.Count files"

>> This directory has Contacts Desktop Documents.Count files

The reason is that PowerShell is only looking for a variable name and ignoring anything else. In this case it is converting all the items held in the $Files variable and displaying it as a string. That makes you tempted to create a variable whose only purpose is to be used in a string on the following line.

$Files = Get-ChildItem
$NumberFiles = $Files.Count
"This directory has $NumberFiles files"

>> This directory has 3 files

This will certainly get you by, but it adds lines of code and variables that won’t be used for anything else. Instead, you can make PowerShell think that you have created a variable simply by surrounding the variable and its dot notation in $() which basically creates a temporary variable which you can work with as you please.

$Files = Get-ChildItem
"This directory has $($Files.Count) files"

>> This directory has 3 files

In this manner you eliminate a useless variable and an excess line of code, but it doesn’t have to stop there. You can even place code blocks inside and really shorten things up. To make our sample code a single line I will use two sets of $(). One to make a temporary variable for Get-ChildItem and one to read the property “Count” and send the output to the string.

"This directory has $($(Get-ChildItem).count) files"

>> This directory has 3 files
Dec 212010

Sometimes your PowerShell code can start to fly off the right side of the screen, especially when you are using commands like write-progress. To space your code down, make it more tidy, and get it to fit on one screen just use the (backtick) key. That’s the ~ (Tilde) key without the shift!

Write-Progress -Activity "Testing New Lines" <code>
-Status "Still Testing" </code>
-CurrentOperation "Testing File $Filename" <code>
-PercentComplete ($Something / $SomethingElse * 100) </code>
-SecondsRemaining $Timer.TotalSeconds