Summary: Microsoft Scripting Guy Ed Wilson teaches you how to run Windows PowerShell Scripts against multiple computers in this step-by-step article.
Hey, Scripting Guy! I am wondering on the best way to cause my script to work against multiple computers.
Hello LS, Microsoft Scripting Guy Ed Wilson here. I received a real nice email message this morning from my friend Jit who lives in Canberra, Australia. He was telling me that with the holidays, his children are out of school for a while, and he is afraid they will drive him crazy. I suggested he teach them Windows PowerShell. It will keep them entertained for hours. The kids can work their way through the Scripting Wife articles, and solve all the Beginner events from the 2010 Scripting Games. Even as the year 2010 comes to a close, have no fear, the events from the 2010 Scripting Games will remain posted, and you are free to work them at your leisure. In fact, when I was writing the events, I had this this very scenario in mind (well, ok, not the cheap babysitter scenario, but I did envision something interesting enough that a non-computer professional would enjoy solving.) Anyway, my last trip over to Australia, I wrote an article that talked about using Windows PowerShell to track items in a list. During that flight, I stopped in Nadi, Fiji and I still have some change from Fiji. The best souvenirs I have from the month long trip are the pictures I took, such as this one of a string ray.
In the article about packing for an Australian trip I hard-coded items into an array. That is, actually, one approach to your problem of running a script against multiple computers. If you have a fairly small collection of computers which you routinely work with, an array of computer names works very well. What makes the array of computer names work especially well for WMI queries is the ability of the computername (CN is an alias for this parameter) to accept an array of computer names. This script shows the technique.
$computers = "hyperv","hyperv-box"
Get-WmiObject -Class win32_bios -cn $computers |
Format-table __Server, Manufacturer, Version -AutoSize
If, on the other hand, I have more than four or five computers which I routinely work with, or if I have different groups of computers to query, I will store the computer names in a text file. The composition of the text file is simple; each computer name receives its own line. Two things are essential to avoid potential problems: make sure that there are no spaces after the computer name before the carriage return to the next line, and make sure that there are no blank lines after the last computer name in the list. A sample text file that contains computer names for a script is seen in the following figure.
The Get-Content Windows PowerShell cmdlet retrieves the list of computer names from the text file, and converts the text into an array of computer names. One advantage of reading a text file is that multiple text files can be used. For example, one file might list critical servers, and another list might list moderately critical servers. I might run a particular script more frequently against critical servers, than I would run it against moderately critical servers (of course, the example script that checks bios versioning is a script that only has to run as frequently as the hardware vendor updates the bios.) Because a text file of computer names might have computers that are not online, I specified silentlyContinue for the ErrorAction parameter (EA is an alias for the parameter.) ErrorAction is a common parameter, and is therefore not specifically listen in the Help file for the Get-WmiObject Windows PowerShell cmdlet. Common parameters are parameters that are available to any cmdlet. These parameters are implemented by Windows PowerShell itself and do not have to be coded by cmdlet developers. The silentlyContinue value for ErrorAction parameter causes the cmdlet to ignore errors and to continue processing. In the Get-BiosVersionReadTxtFile.ps1 this means that if a computer is not online, that instead of displaying an error, the output will only display data from computers that respond to the query. The Script is seen here.
$computers = Get-Content -Path C:\fso\Computers.txt
Get-WmiObject -Class win32_bios -cn $computers -EA silentlyContinue |
Format-table __Server, Manufacturer, Version –AutoSize
When the script runs, the output seen in the following figure appears.
If I have more than a dozen computers to manage, or if the makeup of the list of computers changes very frequently, I prefer to query Active Directory Domain Services (AD DS) for a current list of computers. Unfortunately, this particular change necessitated a rather “radical” modification to the script. The first thing I have to do is to import the Microsoft ActiveDirectory module. (For more information about how to obtain and using the Microsoft ActiveDirectory module refer to this Hey Scripting Guy! blog article.) After the ActiveDirectory module loads, I use the Get-ADComputer cmdlet to return a collection of computer objects. I pipeline the results to the ForEach-Object cmdlet-Object cmdlet. Unfortunately, this slows things significantly, but is necessary because the Get-WmiObject cmdlet does not accept pipelined input for the computername parameter. Because I am inside a Foreach-Object process statement, I have to create a custom object to emit to the Format-Table cmdlet. This enables me to retrieve a single list output, instead of lots of individual tables. The complete Get-BiosVersionQueryAD.ps1 script appears here.
Import-Module -Name ActiveDirectory
Get-ADComputer -filter * |
Get-WmiObject -Class win32_bios -cn $_.name -EA silentlyContinue |
Select-Object __Server, Manufacturer, Version } |
Format-Table -Property * -AutoSize
When the Get-BiosVersionQueryAD.ps1 script runs, the output seen in the following figure appears.
There are other ways of retrieving input lists of computer names but these are the top three methods that I use. Other ways of retrieving input would including querying a Microsoft Excel spreadsheet, querying a Microsoft Access database, or even a SQL Server database. One could, of course, query a Microsoft Word file but that would be an awful lot like querying a text file, but would have the associated overhead of working with COM objects. Instead of querying Microsoft Word files, I copy the text from the document, and paste it into Notepad. The Get-Content Windows PowerShell cmdlet is just much too easy to use to justify the overhead of working with Word files for simply maintaining a listing of computer names.
LS, that is all there is to retrieving input lists of computer names. Script design week will continue tomorrow when I will talk about how to work with the pipeline.
I invite you to follow me on Twitter or Facebook. If you have any questions, send email to me at firstname.lastname@example.org or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy