Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Running Windows PowerShell Scripts Against Multiple Computers

If there’s a problem with the Script Center – wait a minute, who said there was a problem with the Script Center? Boy, if we ever get our hands on that guy we’ll ….

Um, as we were saying, if there’s a problem with the Script Center it’s the fact that our scripts are almost all designed to be run against one computer at a time. That was actually done on purpose: because we are an educational site, we try to keep our scripts as simple as we can. Our concern is that, if we start to randomly toss in the code required to get a script to run against multiple machines, we run the risk of needlessly complicating and confusing matters. Therefore, we tend to keep our scripts short and sweet.

Yes, just like the Scripting Guys.

Of course, in real life things aren’t so simple; system administrators (that is, people who actually use scripts, as opposed to simply writing about them) need to manage multiple machines. As you might expect then, rarely does a day go by that the Scripting Guys don’t get at least one email that begins, “I really like your script that does X. But how can I get that script to run against multiple computers?”

In response to those questions, we posted our Remote/Multiple Computer Templates, templates that VBScript users can use as the foundation for a VBScript script that runs against multiple computers. That’s nice, but how do those templates help Windows PowerShell users?

Well, to be honest, they don’t. (Although we’ll see if we can create some similar templates for Windows PowerShell users.) But don’t despair, PowerShell users; we haven’t forgotten about you. In fact, this week – and next – we’ll show you different ways to get a script to run against multiple computers. This week we’ll show you how to supply multiple computer names as command-line arguments; how to read computer names from a text file; and, as a bonus, how to repeatedly prompt a user to enter a computer name. Next week – well, who knows what we’ll do next week? But you can bet we’ll have such tasks as reading computer names from an Excel spreadsheet and running a script against all the computers in an OU.

But first things first.

Using Command-Line Arguments

Let’s take a look at one of the “classic” ways to run a script against multiple computers: by supplying computer names as command-line arguments. For example, suppose we have a simple WMI script that retrieves BIOS information from a computer (or computers). In that case, we might start the script using a command similar to this:

bios.ps1 atl-fs-01 atl-fs-02

That’s pretty cool, isn’t it? Except for one thing: how do we grab those command-line arguments and use them within the script?

Well, here’s one way to do that:

foreach ($i in $args)
    {Get-WMIObject Win32_BIOS -computername $i}

You’re right: this is remarkably simple, isn’t it? By default, any command-line arguments supplied to a Windows PowerShell script are automatically placed into a special variable named $args. That means that the moment we run our sample command $args will be equal to this:

  • atl-fs-01

  • atl-fs-02

That was pretty easy. After all, we didn’t even have to do anything; like we said, our command-line arguments were automatically tucked away into $args for us. That’s good. Now here’s something even better: to access, and make us of, those arguments, all we need to do is set up a very basic foreach loop, one that cycles through each item in the $args collection:

foreach ($i in $args)

As you can see, we’re simply looping through the $args collection, using $i as our loop variable. (Note that the loop conditions are enclosed in parentheses.) And then, inside curly braces, we call the Get-WMIObject cmdlet, passing two parameters:

  • Win32_BIOS, the name of the WMI class we want to retrieve information from.

  • The –computername parameter, followed by the name of the computer we want to run the script against. In this case, the name of the computer is represented by the loop variable $i.

What’s that going to give us? That’s going to give us output similar to this:

SMBIOSBIOSVersion : 68DTT Ver. F.0F
Manufacturer      : Hewlett-Packard
Name              : EPP runtime BIOS - Version 1.1
SerialNumber      :
Version           : HP     - 15060620

SMBIOSBIOSVersion : 70DTT Ver. E.0A
Manufacturer      : Hewlett-Packard
Name              : EPP runtime BIOS - Version 1.2
SerialNumber      :
Version           : HP     - 16060624

Not bad, except for one problem; how are we supposed to know which computer is which?

Good question. But, again, don’t despair; here’s one way to fix that. Just change the script block so that it looks like this:

{$i + "`n" + "=========================="; Get-WMIObject Win32_BIOS -computername $i}

All we’ve done here is add a couple of things to our foreach script block. Before we call Get-WMIObject we’re echoing back:

  • The name of the computer (stored in the loop variable $i).

  • A carriage return-linefeed (stored in the “escape sequence” `n).

  • A series of equals signs.

In turn, that gives us output that looks like this:

atl-fs-01
==========================
SMBIOSBIOSVersion : 68DTT Ver. F.0F
Manufacturer      : Hewlett-Packard
Name              : EPP runtime BIOS - Version 1.1
SerialNumber      :
Version           : HP     - 15060620

atl-fs-02
==========================
SMBIOSBIOSVersion : 70DTT Ver. E.0A
Manufacturer      : Hewlett-Packard
Name              : EPP runtime BIOS - Version 1.2
SerialNumber      :
Version           : HP     - 16060624

This time around, even a Scripting Guy can tell which BIOS information belongs to which computer.

And yes, PowerShell being PowerShell, we could have created a single string rather than concatenating three separating strings. In other words, we could have done this:

{"$i`n=========================="; Get-WMIObject Win32_BIOS -computername $i}

We just thought that the concatenation might be a little easier for people to understand.

Reading Computer Names From a Text File

OK, so what about reading in computer names from a text file? For example, suppose you have a text file (C:\Scripts\Test.txt) that looks like this:

atl-fs-01
atl-fs-02
atl-fs-03
atl-fs-04
atl-fs-05

How do we get a script to run against each of the computers listed in this text file? Why, like this, of course:

$a = Get-Content "C:\Scripts\Test.txt"

foreach ($i in $a)
    {$i + "`n" + "=========================="; Get-WMIObject Win32_BIOS -computername $i}

Again, there’s not much to this script. In line 1, we simply use the Get-Content cmdlet to read the contents of the text file and store that information in a variable named $a. When Get-Content stores information in a variable, that information is stored as an array, with each line in the text file becoming a separate item in the array. And because each line in our text file just happens to be the name of a computer, we now have an array ($a) containing the name of each computer we want to run the script against.

The rest is very similar to the first script we showed you. We set up a foreach loop that, in this case, loops through the values in the array $a. And what do we do with each of those values? The same thing we did in the first script: we use the Get-WMIObject to retrieve information about the BIOS installed on that computer.

It’s that simple. As an extra special bonus, this is also how you could hardcode computer names in the script and then run that script against all those computers. Remember how, in line 1, we created an array by reading in lines from a text file? Well, alternatively, we could manually create that array. In that case, our script would look like this:

$a = "atl-fs-01", "atl-fs-02", "atl-fs-03", "atl-fs-04", "atl-fs-05"

foreach ($i in $a)
    {$i + "`n" + "=========================="; Get-WMIObject Win32_BIOS -computername $i}

You know, this is almost too easy.

Note. Don’t worry; we said “almost.”

Incidentally, for you diehard Windows PowerShell users, those users who like to employ the minimum number of keystrokes, this modified version of the script will do the same thing:

foreach ($i in Get-Content "C:\Scripts\Test.txt")
    {$i + "`n" + "=========================="; get-wmiobject win32_bios -computername $i}

The only difference here is that instead of using the Get-Content cmdlet to read in the contents and store that information in a variable, we’re simply placing the Get-Content command directly in the foreach loop.

And yes, Windows PowerShell allows you to do things like that. Pretty slick, huh?

Prompting the User to Enter a Computer Name

Let’s do one more before we call it a day. Here’s a script that repeatedly prompts the user to enter a computer name. After the user enters the name and presses ENTER, the script uses Get-WMIObject to retrieve BIOS information for that machine. It then prompts the user to enter the name of another computer; this continues until the user presses ENTER without entering a computer name.

Here’s what the script looks like:

do
    {
        $a = Read-Host "Please enter the computer name"
        if ($a -ne "")
        {$a + "`n" + "=========================="; Get-WMIObject Win32_BIOS -computername $a}
    }
while ($a -ne "")

As you can see, we start off by setting up a do while loop that runs as long as (while) the variable $a is not equal to (-ne) an empty string. Inside the loop, we start off by calling the Read-Host cmdlet, displaying the message “Please enter a computer name” and storing the user input in the variable $a. Read-Host prompts a user for input, then sits patiently until the user finishes typing and presses ENTER.

From there we set up an if statement that checks the value of $a. As long as $a is not an empty string, the script will retrieve the BIOS information from the computer whose name was just entered. Once that’s done we swing back to the top of the loop and repeat the process, asking the user to enter another computer name.

That’s all we have time for this week. See you next week for yet another exciting Windows PowerShell tip.