Hey, Scripting Guy! How Can I Handle Errors in a Windows PowerShell Script?

ScriptingGuy1

 

 

Hey, Scripting Guy! QuestionHey Scripting Guy! When I used to write VBScripts, I liked the fact that I could use On Error Resume Next to handle any errors in my scripts. I have tried to find something like this in Windows PowerShell, but I have been unable to do so. Does Windows PowerShell have On Error Resume Next?


— OG

Hey, Scripting Guy! AnswerHello OG,

When I am scuba diving, one of the things I have to always do is anticipate what could possibly go wrong. The list of potential problems depends on the environment. For example, last year I was scuba diving off the coast of Key West, Florida. The area where we were diving was frequented by boats, and there were coral heads that if you followed them could cause you to get in really shallow water. Shallow depth and lots of high speed boats is not a good combination for a scuba diver. As a result I needed to pay close attention to both my location and my depth on that dive.  

On another dive, I was off of the coast of the Island of Hawaii (the big island) when I ran across the green Moray eel whose adorable face I immortalized in the following picture. Moray eels are normally very shy and will attempt to flee when a scuba diver approaches. However, they are also very near sighted, and additionally they do not hear very well. So when you have a creature with lots of extremely sharp teeth that does not see or hear very well, you have a definite potential for a problem. This is where error handling comes into play.

Image of a Moray eel

OG, when you descend to the depths of your script, you must also anticipate being run over with a speed boat, or being bitten by a 10-feet-long near-sighted and nearly deaf eel—at least metaphorically. When it comes to handling errors in your script, you need to have an understanding of how the script will be used. This is sometimes called the “use case scenario,” and it describes how the user will interact with the script. If the use case scenario is simple, the user may not need to do anything more than type the name of the script inside the Windows PowerShell console. A script such as Get-Bios.ps1 could get by without much need for any error handling.

Get-Bios.ps1

Get-WmiObject -class Win32_Bios

When you examine the Get-Bios.ps1 script, you can see that it does not receive any input from the command line. When your script accepts command-line input, you are opening the door for all kinds of potential problems. Depending on how you accept command-line input, you may need to test the input data to ensure that it corresponds to the type of input the script is expecting. The Get-Bios.ps1 script does not accept command-line input; therefore you avoided one potential source of errors.

Another source of potential errors in a script is one that requires elevated permissions to work correctly. Beginning with Windows Vista, the operating system makes it much easier to run and to allow the user to work without requiring constant access to administrative rights. As a result, more and more users and even network administrators are no longer running their computers with a user account that is a member of the local administrators group. The User Account Control (UAC) feature makes it easy to provide elevated rights for interactive programs, but Windows PowerShell and other scripting languages are not UAC aware and will therefore not prompt when elevated rights are required to perform a specific activity. It is therefore incumbent upon the script writer to take rights into account when writing scripts. The Get-Bios.ps1 script, however, does not use a WMI class that requires elevated rights. As the script is currently written, anyone who is a member of the local users groupand that includes everyone who is logged on interactivelyhas permission to run the Get-Bios.ps1 script. So testing for rights and permissions before making an attempt to obtain information from the WMI class Win32_Bios is not required.

What are some of the things that could go wrong with the Get-Bios.ps1 script? Well, the Get-Bios.ps1 script would fail if the Windows PowerShell script execution policy was set to restricted. When the script execution policy is set to restricted, Windows PowerShell scripts will not run. When Windows PowerShell scripts do not run, you could not write code that would pick up the script execution policy. Because the script execution policy is stored in the registry, you could write a VBScript script that would query and set the policy prior to launching the Windows PowerShell script, but that would not be the best way to manage the problem. The best way to manage the script execution policy is to use Group Policy to set it to the appropriate level for your network. On a stand-alone computer, you can set the execution policy by opening Windows PowerShell as an administrator, and using the Set-ExecutionPolicy cmdlet. In most cases, the remotesigned setting is appropriate. The command would therefore be the one seen here:

PS C:> Set-ExecutionPolicy remotesigned
PS C:>

The script execution policy is generally dealt with once, and there are no more problems associated with it. In addition, the error message that is associated with the script execution policy is relatively clear; it will tell you that script execution is disabled on the system. It also refers you to a Help article that explains the various settings. This is seen here:

File C:Documents and SettingsedLocal SettingsTemptmp2A7.tmp.ps1 cannot be
loaded because the execution of scripts is disabled on this system. Please see
“get-help about_signing” for more details.
At line:1 char:66
+ C:Documents` and` SettingsedLocal` SettingsTemptmp2A7.tmp.ps1 <<<<

About the only thing that could actually go wrong with the Get-Bios.ps1 script would be if the WMI provider that supplies the Win32_Bios WMI class information was corrupted or missing. To check for the existence of the appropriate WMI provider, you will need to know the name of the provider for the WMI class. You can use the Windows Management Instrumentation Tester (WbemTest), which is included as part of the WMI installation. If a computer has WMI installed on it, it has WbemTest. Because it resides in the system folders, you can launch it directly from within the Windows PowerShell console by typing the name of the executable:

PS C:> wbemtest
PS C:>

After WbemTest appears, the first thing you will need to do is to connect to the appropriate WMI namespace. To do this, press Connect. In most cases this will be the RootCimv2 namespace. On Windows Vista and later, the RootCimv2 namespace is the default WMI namespace for WbemTest. On earlier versions of Windows, the default WbemTest namespace is RootDefault. Change or accept the namespace as appropriate, and press Connect.

The display changes to a series of buttons, many of which appear to have cryptic names and functionality. To obtain information about the provider for a WMI class, you will need to open the class. Press Open class, and type the name of the WMI class in the box. We are looking for the provider name for the Win32_Bios WMI class, so that is the name that is typed here. Press OK after you have entered the class name. The Object Editor for the Win32_BIOS WMI class now appears. This is seen in the following image. The first box of the Object Editor for Win32_BIOS lists the qualifiers. One of the qualifiers is provider. WbemTest says that the provider for Win32_Bios is CIMWin32.

Image of the obect editor for Win32_BIOS

 

Armed with the name of the WMI provider, you can use the Get-WmiObject cmdlet to determine if the provider is installed on the computer. To do this, you will query for instances of the __provider WMI class. All WMI classes that begin with a double underscore are system classes. The __provider WMI class is the class from which all WMI providers are derived. By limiting the query to providers with the name of “cimwin32,” you can determine if the provider is installed on the system. This is seen here:

PS C:> Get-WmiObject -Class __provider -filter “name = ‘cimwin32′”


__GENUS                       : 2
__CLASS                       : __Win32Provider
__SUPERCLASS                  : __Provider
__DYNASTY                     : __SystemClass
__RELPATH                     : __Win32Provider.Name=”CIMWin32″
__PROPERTY_COUNT              : 24
__DERIVATION                  : {__Provider, __SystemClass}
__SERVER                      : OFFICE
__NAMESPACE                   : ROOTcimv2
__PATH                        : \OFFICEROOTcimv2:__Win32Provider.Name=”CIMWi
                                n32″
ClientLoadableCLSID           :
CLSID                         : {d63a5850-8f16-11cf-9f47-00aa00bf345c}
Concurrency                   :
DefaultMachineName            :
Enabled                       :
HostingModel                  : NetworkServiceHost
ImpersonationLevel            : 1
InitializationReentrancy      : 0
InitializationTimeoutInterval :
InitializeAsAdminFirst        :
Name                          : CIMWin32
OperationTimeoutInterval      :
PerLocaleInitialization       : False
PerUserInitialization         : False
Pure                          : True
SecurityDescriptor            :
SupportsExplicitShutdown      :
SupportsExtendedStatus        :
SupportsQuotas                :
SupportsSendStatus            :
SupportsShutdown              :
SupportsThrottling            :
UnloadTimeout                 :
Version                       :

PS C:>

For the purposes of determining if the provider exists, you do not need all the information to be returned to the script. It is easier to treat the query as if it returned a Boolean value, by using the If statement. If the provider exists, you will perform the query. This is seen here:

If(Get-WmiObject -Class __provider -filter “name = ‘cimwin32′”)
 {
  Get-WmiObject -class Win32_bios
 }

If the cimwin32 WMI provider does not exist, you display a message that states the provider is missing. This is seen here:

Else
 {
  “Unable to query Win32_Bios because the provider is missing”
 }


The completed CheckProviderThenQuery.ps1 script is shown here.

CheckProviderThenQuery.ps1

If(Get-WmiObject -Class __provider -filter “name = ‘cimwin32′”)
 {
  Get-WmiObject -class Win32_bios
 }
Else
 {
  “Unable to query Win32_Bios because the provider is missing”
 }

OG, we hope that our discussion of anticipating problems with a script has been useful. Join us tomorrow as we continue our survey of error handling in Windows PowerShell scripts. If you would like to be among the first to be informed about everything that is happening on the Script Center, you can follow us on Twitter. We also make postings over at the Scripting Guys group on Facebook. If you get stuck some night while you are working on a script, you can post to the Official Scripting Guys forum. We have an excellent group of moderators and other active members who are always willing to help other scripters. Until tomorrow, peace.

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

 

0 comments

Discussion is closed.

Feedback usabilla icon