Don’t Write PowerShell Scripts

Don’t Write PowerShell Scripts

  • Comments 1
  • Likes

Summary: Microsoft Scripting Guy Ed Wilson talks about when to not write a Windows PowerShell script.

Weekend Scripter

Microsoft Scripting Guy, Ed Wilson, is here. Network administrators do not script! ITPros do not like to script! I have heard statements like this for years. Yet, when I first wrote my VBScript workshop for Microsoft Premier Workshops, it quickly became the most popular of all workshop offerings. So what gives? Is there a disconnect? Do ITPros really like to write scripts? Is there a secret developer hiding inside net admins that is struggling to make itself known? I do not think so. Rather, I believe that the desire to “learn” scripting is the desire to ease network administration duties. I believe the impetus to acquire scripting skills is the wish to automate repetitive tasks. In the past, these twin needs and goals did require learning rudimentary (and at times more advanced) programming skills, but this is no longer the case.

Microsoft Windows PowerShell MVP, Don Jones, caused a minor tempest in the Windows PowerShell community when he declared that Windows PowerShell was not a “scripting language.” To an extent he was kidding, but he also had a point because Windows PowerShell is an automation tool.

I believe, within reason, that the days of writing scripts are rapidly drawing to a close. To the extent that customization is required, it can be handled in the pipeline. To the extent that new functionality is required, it is best handled as an advanced function.

I think a couple of examples are in order to illustrate what I am talking about. Consider the simple case of checking the version of the BIOS on the computer. In the old-fashioned VBScript days, a script such as the following was the order of the day.

GetBiosInformation.vbs

On Error Resume Next

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set colItems = objWMIService.ExecQuery("Select * from Win32_BIOS",,48)

For Each objItem in colItems

    Wscript.Echo "BiosCharacteristics: " & objItem.BiosCharacteristics

    Wscript.Echo "BIOSVersion: " & objItem.BIOSVersion

    Wscript.Echo "BuildNumber: " & objItem.BuildNumber

    Wscript.Echo "Caption: " & objItem.Caption

    Wscript.Echo "CodeSet: " & objItem.CodeSet

    Wscript.Echo "CurrentLanguage: " & objItem.CurrentLanguage

    Wscript.Echo "Description: " & objItem.Description

    Wscript.Echo "IdentificationCode: " & objItem.IdentificationCode

    Wscript.Echo "InstallableLanguages: " & objItem.InstallableLanguages

    Wscript.Echo "InstallDate: " & objItem.InstallDate

    Wscript.Echo "LanguageEdition: " & objItem.LanguageEdition

    Wscript.Echo "ListOfLanguages: " & objItem.ListOfLanguages

    Wscript.Echo "Manufacturer: " & objItem.Manufacturer

    Wscript.Echo "Name: " & objItem.Name

    Wscript.Echo "OtherTargetOS: " & objItem.OtherTargetOS

    Wscript.Echo "PrimaryBIOS: " & objItem.PrimaryBIOS

    Wscript.Echo "ReleaseDate: " & objItem.ReleaseDate

    Wscript.Echo "SerialNumber: " & objItem.SerialNumber

    Wscript.Echo "SMBIOSBIOSVersion: " & objItem.SMBIOSBIOSVersion

    Wscript.Echo "SMBIOSMajorVersion: " & objItem.SMBIOSMajorVersion

    Wscript.Echo "SMBIOSMinorVersion: " & objItem.SMBIOSMinorVersion

    Wscript.Echo "SMBIOSPresent: " & objItem.SMBIOSPresent

    Wscript.Echo "SoftwareElementID: " & objItem.SoftwareElementID

    Wscript.Echo "SoftwareElementState: " & objItem.SoftwareElementState

    Wscript.Echo "Status: " & objItem.Status

    Wscript.Echo "TargetOperatingSystem: " & objItem.TargetOperatingSystem

    Wscript.Echo "Version: " & objItem.Version

Next

With Windows PowerShell, it is possible to replace that exact script with the following command.

Get-WmiObject Win32_bios | Format-List *

The output from the VBScript script and the PowerShell command are nearly identical.

Now, suppose I decide that I need to run the command to get BIOS information from a large number of computers. The choice of where the computer names reside is a different discussion, but for now suppose the computer names are stored in a text file, one computer name per line. To solve this scenario, I can use the command that is shown here.

Get-WmiObject win32_bios -cn (Get-Content C:\fso\Servers.txt)

If I need to use different credentials (an account other than the one that I am currently using) to run the command, I use the Credential parameter as shown here.

Get-WmiObject win32_bios -cn (Get-Content C:\fso\Servers.txt) –credential nwtraders\administrator

When the command runs, a dialog box appears that prompts me to enter my password. This password is encrypted as it is transmitted because it is retrieved and handled as a secure string. The dialog box for the password is shown in the following image.

Image of text box

Well, this is all fine and dandy, but to be honest, it is a bit silly to read a text file that contains dozens (or even hundreds) of computer names, and then display the results to the console. It makes more sense to output the information somewhere. I like writing the data to a database and using the SQLPSX modules. It is really trivial to write information to a database. The easiest way to store the returned information is to use redirection arrows and write the returned data to a text file. This technique is shown here.

Get-WmiObject win32_bios -cn (Get-Content C:\fso\Servers.txt) >> c:\fso\biosInfo.txt

Because I am working interactively in the Windows PowerShell console, I might want to shorten the syntax a bit by using commonly available aliases for the commands. To do this, the previous command becomes:

gwmi win32_bios -cn (cat C:\fso\Servers.txt) >> c:\fso\biosInfo.txt

This command works because gwmi is an alias for the Get-WmiObject cmdlet. I need to let the Get-WmiObject cmdlet know what I intend to do with the results of my Get-Content cmdlet. I can use cn as an alias for the longer ComputerName parameter (in fact, I have cheated all along on this detail today). There are three default aliases for the Get-Content cmdlet. I found them by using the Get-Aliases cmdlet as shown here.

PS C:\> Get-Alias -Definition get-content

 

CommandType     Name                                      Definition

-----------     ----                                      ----------

Alias           cat                                       Get-Content

Alias           gc                                        Get-Content

Alias           type                                      Get-Content

If you do not like any of the default aliases, you can create your own alias. For example, if you like dogs better than cats, you can create a new alias as shown here. One thing to keep in mind, is that nothing is returned when you create a new alias in this manner.

PS C:\> New-Alias -Name dog -Value get-content

PS C:\>

You can always check to ensure that the new alias “took” by using the Get-Alias cmdlet as shown here.

PS C:\> Get-Alias dog

 

CommandType     Name                                      Definition

-----------     ----                                      ----------

Alias           dog                                       get-content

Or better yet, you can simply try the new command and see what happens. This is what I do here.

gwmi win32_bios -cn (dog C:\fso\Servers.txt)

None of this required any scripting. Now suppose in my testing, I detect that there is a bad BIOS version. I mean, it is the sort of thing that causes my servers to emit a BSOD (blue screen of death). In the past, I would need to write a script to check for that. But by using Windows PowerShell, I simply “tack” an additional cmdlet to the end of my pipeline. The revised command is shown here.

Get-WmiObject win32_bios -cn (Get-Content C:\fso\Servers.txt) |

Where-Object { $_.version -match '2170'}

Note: The previous command is a single logical command. I broke it at the pipeline character (|) so it would fit better in the blog format.

I can shorten this command by using aliases. In the short version, gwmi is an alias for the Get-WmiObject cmdlet. I use gc for Get-Content, and the question mark (?) as an alias for the Where-Object cmdlet. The short version of this command is shown here.

gwmi win32_bios -cn (gc C:\fso\Servers.txt) | ? { $_.version -match '2170'}

If I only need a list of the servers that will need the BIOS update, I add an additional cmdlet to the end of the command. This time, I use the Select-Object cmdlet to retrieve the __Server property (that is a double underscore character at the beginning of the __Server property). In WMI, there is always a system property named __Server that stores the name of the computer. This property exists in nearly every WMI class. The revised command is shown here.

Get-WmiObject win32_bios -cn (Get-Content C:\fso\Servers.txt) |

Where-Object { $_.version -match '2170'} | Select-Object __Server

Once again, this is a single logical command. I broke the command at the pipeline character (|) to better format the command for the blog.

I can shorten this command by using aliases. There is a default alias of Select for the Select-Object cmdlet. A short version of the command is shown here.

gwmi win32_bios -cn (gc C:\fso\Servers.txt) | ? { $_.version -match '2170'} | select __Server

Even by using aliases, the command is becoming a bit long. But the cool thing is that the command is built incrementally, and I can use the command buffer to retrieve and to edit my commands.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Well done, Ed!

    It's more a less a small command that does the whole job!

    Scripting is not reqired at all.

    And it does cetrainly accomplish more than the VbScript ...

    Great, Klaus