Skip to main content

Resources

Display the Properties of an Item As a Table in Windows PowerShell

Problem

You have a set of items (for example, error records, directory items, or .NET objects), and you want to display summary information about them in a table format.

Solution

To display summary information about a set of items, pass those items to the FormatTable cmdlet. This is the default type of formatting for sets of items in PowerShell and provides several useful features.

To use PowerShell’s default formatting, pipe the output of a cmdlet (such as the GetProcess cmdlet) to the FormatTable cmdlet:

GetProcess | FormatTable

To display specific properties (such as Name and WorkingSet,) in the table formatting, supply those property names as parameters to the FormatTable cmdlet:

GetProcess | FormatTable Name,WS

To instruct PowerShell to format the table in the most readable manner, supply the –Auto flag to the FormatTable cmdlet. PowerShell defines “WS” as an alias of the WorkingSet for processes:

GetProcess | FormatTable Name,WS Auto

To define a custom column definition (such as a process’s Working Set in megabytes), supply a custom formatting expression to the FormatTable cmdlet:

$fields = "Name",@{Label = "WS (MB)"; Expression = {$_.WS / 1mb}; Align = "Right"} GetProcess | FormatTable $fields Auto

Discussion

The FormatTable cmdlet is one of the three PowerShell formatting cmdlets. These cmdlets include FormatTable, FormatList, and FormatWide. The FormatTable cmdlet takes input and displays information about that input as a table. By default, PowerShell takes the list of properties to display from the *.format.ps1xml files in PowerShell’s installation directory. You can display all properties of the items if you type FormatTable *, although this is rarely a useful view.

The Auto parameter to FormatTable is a helpful way to automatically format the table to use screen space as efficiently as possible. It does come at a cost, however. To figure out the best table layout, PowerShell needs to examine each item in the incoming set of items. For small sets of items, this doesn’t make much difference, but for large sets (such as a recursive directory listing) it does. Without the Auto parameter, the FormatTable cmdlet can display items as soon as it receives them. With the Auto flag, the cmdlet can only display results after it receives all the input.

Perhaps the most interesting feature of the FormatTable cmdlet is illustrated by the last example—the ability to define completely custom table columns. You define a custom table column similarly to the way that you define a custom column list. Rather than specify an existing property of the items, you provide a hashtable. That hashtable includes up to three keys: the column’s label, a formatting expression, and alignment. The FormatTable cmdlet shows the label as the column header and uses your expression to generate data for that column. The label must be a string, the expression must be a script block, and the alignment must be either "Left", "Center",or "Right". In the expression script block, the $_ variable represents the current item being formatted.

The expression shown in the last example takes the working set of the current item and divides it by 1 megabyte (1 MB).

For more information about the FormatTable cmdlet, type GetHelp FormatTable.

Create a Hashtable or Associative Array

Problem

You have a collection of items that you want to access through a label that you provide.

Solution

To define a mapping between labels and items, use a hashtable (associative array):

PS >$myHashtable = @{} PS > PS >$myHashtable = @{ Key1 = "Value1"; "Key 2" = 1,2,3 } PS >$myHashtable["New Item"] = 5

PS >

PS >$myHashTable

Name
Value

Key 2
{1, 2, 3}

New Item
5

Key1
Value1

Discussion

Hashtables are much like arrays that allow you to access items by whatever label you want—not just through their index in the array. Because of that freedom, they form the keystone of a huge number of scripting techniques. Since they allow you to map names to values, they form the natural basis for lookup tables such as ZIP codes and area codes. Since they allow you to map names to fully featured objects and script blocks, they can often take the place of custom objects. Since you can map rich objects to other rich objects, they can even form the basis of more advanced data structures such as caches and object graphs.

This label and value mapping also proves helpful in interacting with cmdlets that support advanced configuration parameters, such as the calculated property parameters available on the FormatTable and SelectObject cmdlets.

Create a Registry Key Value

Problem

You want to add a new key value to an existing registry key.

Solution

To add a value to a registry key, use the NewItemProperty cmdlet. Example 182 adds MyProgram.exe to the list of programs that start when the current user logs in.

Example 182. Creating new properties on a registry key

PS >NewItemProperty . Name MyProgram Value c:\temp\MyProgram.exe

PSPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Softw are\Microsoft\Windows\CurrentVersion\Run PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Softw

are\Microsoft\Windows\CurrentVersion PSChildName : Run PSDrive : HKCU PSProvider : Microsoft.PowerShell.Core\Registry MyProgram : c:\temp\MyProgram.exe

PS >GetItemProperty .

PSPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_U SER\Software\Microsoft\Windows\CurrentVersion\Run PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_U

SER\Software\Microsoft\Windows\CurrentVersion PSChildName : Run PSDrive : HKCU PSProvider : Microsoft.PowerShell.Core\Registry FolderShare : "C:\Program Files\FolderShare\FolderShare.exe" /ba

ckground TaskSwitchXP : d:\lee\tools\TaskSwitchXP.exe

Example 182. Creating new properties on a registry key (continued)

ctfmon.exe
: C:\WINDOWS\system32\ctfmon.exe

Ditto
: C:\Program Files\Ditto\Ditto.exe

QuickTime Task
: "C:\Program Files\QuickTime Alternative\qttask.exe

" atboottime

H/PC Connection Agent : "C:\Program Files\Microsoft ActiveSync\wcescomm.ex

e" MyProgram : c:\temp\MyProgram.exe

Discussion

In the registry provider, PowerShell treats registry keys as items and key values as properties of those items. To create a key property, use the NewItemProperty cmdlet.

For more information about the NewItemProperty cmdlet, type GetHelp NewItemProperty. For more information about the registry provider, type GetHelp Registry.

Manage Distribution Groups

Problem

You want to get and modify information about distribution groups from the Exchange Management Shell.

Solution

To retrieve information about a distribution group, or distribution groups, use the GetDistributionGroup cmdlet:

$group = GetDistributionGroup "Stock Traders"

$group | FormatList *

To modify information about a group, use the SetDistributionGroup cmdlet. This example updates the Stock Traders distribution group to accept messages only from other members of the Stock Traders distribution group:

$group | SetDistributionGroup –AcceptMessagesOnlyFromDLMembers "Stock Traders" To add a user to a distribution group, use the AddDistributionGroupMember cmdlet:

$group | AddDistributionGroupMember –Member *preeda* To list members of a distribution group, use the GetDistributionGroupMember cmdlet:

$group | GetDistributionGroupMember

Discussion

For more information about the GetDistributionGroup cmdlet, type GetHelp GetDistributionGroup. For more information about the SetDistributionGroup cmdlet, type GetHelp SetDistributionGroup. For more information about the AddDistributionGroupMember cmdlet, type GetHelp AddDistributionGroupMember. For more information about the GetDistributionGroupMember cmdlet, type GetHelp GetDistributionGroupMember.

How to Customize Windows Shell, Profile, and Prompt

Problem

You want to customize PowerShell’s interactive experience with a personalized prompt, aliases, and more.

Solution

When you want to customize aspects of PowerShell, place those customizations in your personal profile script. PowerShell provides easy access to this profile script by storing its location in the $profile variable.

By default, PowerShell’s security policies prevent scripts (including your profile) from running. Once you begin writing scripts, though, you should configure this policy to something less restrictive.

To create a new profile (and overwrite one if it already exists):

NewItem type file force $profile

To edit your profile:

notepad $profile

To see your profile file:

GetChildItem $profile

Once you create a profile script, you can add a function called Prompt that returns a string. PowerShell displays the output of this function as your commandline prompt.

function Prompt

{

"PS [$env:COMPUTERNAME] >"

}

This example prompt displays your computer name, and look like: PS [LEEDESK]>

You may also find it helpful to add aliases to your profile. Aliases let you to refer to common commands by a name that you choose. Personal profile scripts let you automatically define aliases, functions, variables, or any other customizations that you might set interactively from the PowerShell prompt. Aliases are among the most common customizations, as they let you refer to PowerShell commands (and your own scripts) by a name that is easier to type.

If you want to define an alias for a command but also need to modify the parameters to that command, then define a function instead.

For example:

SetAlias new NewObject SetAlias iexplore 'C:\Program Files\Internet Explorer\iexplore.exe'

Your changes will become effective once you save your profile and restart PowerShell. To reload your profile immediately, run the command:

. $profile

Functions are also very common customizations, with the most popular of those being the Prompt function.

Discussion

Although the Prompt function returns a simple string, you can also use the function for more complex tasks. For example, many users update their console window title (by changing the $host.UI.RawUI.WindowTitle variable) or use the WriteHost cmdlet to output the prompt in color. If your prompt function handles the screen output itself, it still needs to return a string (for example, a single space) to prevent PowerShell from using its default. If you don’t want this extra space to appear in your prompt, add an extra space at the end of your WriteHost command and return the backspace ("`b") character.

Example 12. An example PowerShell prompt

function Prompt

{ $id = 1 $historyItem = GetHistory Count 1 if($historyItem) {

$id = $historyItem.Id + 1 }

WriteHost ForegroundColor DarkGray "`n[$(GetLocation)]" WriteHost NoNewLine "PS:$id > " $host.UI.RawUI.WindowTitle = "$(GetLocation)"

"`b" }

In addition to showing the current location, this prompt also shows the ID for that command in your history. This lets you locate and invoke past commands with relative ease:

[C:\] PS:73 >5 * 5 25

[C:\] PS:74 >1 + 1 2

[C:\] PS:75 >InvokeHistory 73 5 * 5

[C:\] PS:76 > Although the profile referenced by $profile is the one you will almost always want to use, PowerShell actually supports four separate profile scripts.

Return Data from a Script, Function, or Script Block in Windows PowerShell

Problem

You want your script or function to return data to whatever called it.

Solution

To return data from a script or function, write that data to the output pipeline:

## GetTomorrow.ps1 ## Get the date that represents tomorrow

function GetDate

{

GetDate

}

$tomorrow = (GetDate).AddDays(1) $tomorrow

Discussion

In PowerShell, any data that your function or script generates gets sent to the output pipeline, unless something captures that output. The GetDate function generates data (a date) and does not capture it, so that becomes the output of the function. The portion of the script that calls the GetDate function captures that output and then manipulates it.

Finally, the script writes the $tomorrow variable to the pipeline without capturing it, so that becomes the return value of the script itself.

Some .NET methods—such as the System.Collections.ArrayList class produce output, even though you may not expect them to. To prevent them from sending data to the output pipeline, either capture the data

or cast it to [void]: PS >$collection = NewObject System.Collections.ArrayList PS >$collection.Add("Hello") 0 PS >[void] $collection.Add("Hello")

Even with this “pipeline output becomes the return value” philosophy, PowerShell continues to support the traditional return keyword as a way to return from a function or script. If you specify anything after the keyword (such as return "Hello"), PowerShell treats that as a "Hello" statement followed by a return statement.

If you want to make your intention clear to other readers of your script, you can use the WriteOutput cmdlet to explicitly send data down the pipeline. Both produce the same result, so this is only a mat

ter of preference.

If you write a collection (such as an array or ArrayList) to the output pipeline, PowerShell in fact writes each element of that collection to the pipeline. To keep the collection intact as it travels down the pipeline, prefix it with a comma when you return it. This returns a collection (that will be unraveled) with one element: the collection you wanted to keep intact.

function WritesObjects

{ $arrayList = NewObject System.Collections.ArrayList [void] $arrayList.Add("Hello") [void] $arrayList.Add("World")

$arrayList }

function WritesArrayList

{ $arrayList = NewObject System.Collections.ArrayList [void] $arrayList.Add("Hello") [void] $arrayList.Add("World")

,$arrayList }

$objectOutput = WritesObjects

# The following command would generate an error # $objectOutput.Add("Extra")

$arrayListOutput = WritesArrayList $arrayListOutput.Add("Extra")

Although relatively uncommon in PowerShell’s world of fully structured data, you may sometimes want to use an exit code to indicate the success or failure of your script. For this, PowerShell offers the exit keyword.

Determine the Current Location

Problem

You want to determine the current location from a script or command.

Solution

To retrieve the current location, use the GetLocation cmdlet. The GetLocation cmdlet provides the Drive and Path as two common properties:

$currentLocation = (GetLocation).Path

As a shortform for (GetLocation).Path, use the $pwd automatic variable.

Discussion

The GetLocation cmdlet returns information about the current location. From the information it returns, you can access the current drive, provider, and path.

This current location affects PowerShell commands and programs that you launch from PowerShell. It does not apply when you interact with the .NET Framework, however. If you need to call a .NET method that interacts with the filesystem, always be sure to provide fully qualified paths:

[System.Reflection.Assembly]::LoadFile("d:\documents\path_to_library.dll") If you are sure that the file exists, the ResolvePath cmdlet lets you translate a relative path to an absolute path:

$filePath = (ResolvePath library.dll).Path If the file does not exist, use the JoinPath cmdlet in combination with the GetLocation cmdlet to specify the file:

$filePath = JoinPath (GetLocation) library.dll

Another alternative that combines the functionality of both approaches is a bit more advanced but also lets you specify relative locations. It comes from methods in the PowerShell $executionContext variable, which provides functionality normally used by cmdlet and provider authors:

$executionContext.SessionState.Path.` GetUnresolvedProviderPathFromPSPath("..\library.dll")

For more information about the GetLocation cmdlet, type GetHelp GetLocation.

Uninstall an Application from PowerShell

Problem

You want to uninstall a specific software application.

Solution

To uninstall an application, use the GetInstalledSoftware script to retrieve the command that uninstalls the software. Since the UninstallString uses batch file syntax, use cmd.exe to launch the uninstaller:

PS > $software = GetInstalledSoftware UnwantedProgram PS > cmd /c $software.UninstallString

Alternatively, use the Win32_Product WMI class for an unattended installation:

$application = GetWmiObject Win32_Product filter "Name='UnwantedProgram'" $application.Uninstall()

Discussion

The UninstallString provided by applications starts the interactive experience you would see if you were to uninstall the application through the Add/Remove Programs entry in the Control Panel. If you need to remove the software in an unattended manner, you have two options: use the “quiet mode” of the application’s uninstaller (for example, the /quiet switch to msiexec.exe), or use the software removal functionality of the Win32_Product WMI class as demonstrated in the solution.

Create a Temporary File in Windows PowerShell

Problem

You want to create a file for temporary purposes and want to be sure that the file does not already exist.

Solution

Use the [System.IO.Path]::GetTempFilename() method from the .NET Framework to create a temporary file:

$filename = [System.IO.Path]::GetTempFileName() (... use the file ...) RemoveItem Force $filename

Discussion

It is common to want to create a file for temporary purposes.

Often, people create this temporary file wherever they can think of: in C:\, the script’s current location, or any number of other places. Although this may work on the author’s system, it rarely works well elsewhere. For example, if the user does not use their Administrator account for daytoday tasks, your script will not have access to C:\ and will fail.

Another difficulty comes from trying to create a unique name for the temporary file. If your script just hardcodes a name (no matter how many random characters it has), it will fail if you run two copies at the same time. You might even craft a script smart enough to search for a filename that does not exist, create it, and then use it. Unfortunately, this could still break if another copy of your script creates that file after you see that it is missing—but before you actually create the file.

Finally, there are several security vulnerabilities that your script might introduce should it write its temporary files to a location that other users can read or write.

Luckily, the authors of the .NET Framework provided the [System.IO.Path]:: GetTempFilename() method to resolve these problems for you. It creates a unique filename in a reliable location in a secure manner. The method returns a filename, which you can then use as you want.

Remember to delete this file when your script no longer needs it; otherwise, your script will waste disk space and cause needless clutter on your users’ systems. Remember: your scripts should solve the adminis

trator’s problems, not cause them!

By default, the GetTempFilename() method returns a file with a .tmp extension. For most purposes, the file extension does not matter, and this works well. In the rare instances when you need to create a file with a specific extension, the [System.IO. Path]::ChangeExtension() method lets you change the extension of that temporary file. The following example creates a new temporary file that uses the .cs file extension:

$filename = [System.IO.Path]::GetTempFileName() $newname = [System.IO.Path]::ChangeExtension($filename, ".cs") MoveItem $filename $newname (... use the file ...) RemoveItem $newname

Manage PowerShell Security in an Enterprise

Problem

You want to control PowerShell’s security features in an enterprise setting.

Solution

To manage PowerShell’s security features enterprisewide:

  • Apply PowerShell’s Group Policy templates to control PowerShell’s execution policy through Group Policy.
  • Deploy Microsoft Certificate Services to automatically generate Authenticode codesigning certificates for domain accounts.
  • Apply software restriction policies to prevent PowerShell from trusting specific script publishers.

Discussion

Apply PowerShell’s Group Policy templates

The administrative templates for Windows PowerShell let you override the machine’s local execution policy preference at both the machine and peruser level. To obtain the PowerShell administrative templates, visit http://www.microsoft.com/ downloads and search for “Administrative templates for Windows PowerShell.”

Although Group Policy settings override local preferences, PowerShell’s execution policy should not be considered a security measure that protects the system from the user. It is a security measure that

helps prevent untrusted scripts from running on the system. As mentioned in the introduction, PowerShell is only a vehicle that allows users to do what they already have the Windows permissions to do.

Once you install the administrative templates for Windows PowerShell, launch the Group Policy Object Editor MMC snapin. Rightclick Administrative Templates and then select Add/Remove Administrative Templates. You will find the administrative template in the installation location you chose when you installed the administrative templates for Windows PowerShell. Once added, the Group Policy Editor MMC snapin provides PowerShell as option under its Administrative Templates node.

The default state is Not Configured. In this state, PowerShell takes its execution pol icy from the machine’s local preference. If you change the state to one of the Enabled options (or Disabled), PowerShell uses this configuration instead of the machine’s local preference.

PowerShell respects these Group Policy settings no matter what. This includes settings that the machine’s administrator may consider to reduce security—such as an Unrestricted group policy overriding an

AllSigned local preference.

Peruser Group Policy settings override the machine’s local preference, while permachine Group Policy settings override peruser settings.

Deploy Microsoft Certificate services

Although outside the scope of this book, Microsoft Certificate Services lets you automatically deploy codesigning certificates to any or all domain users. This provides a significant benefit, as it helps protect users from accidental or malicious script tampering.

For an introduction to this topic, visit http://technet.microsoft.com and search for “Enterprise Design for Certificate Services.”

Apply software restriction policies

While not common, you may sometimes want to prevent PowerShell from running scripts signed by specific publishers. If the script would normally be subject to signature verification (for example, it is a remote script, or PowerShell’s execution policy is set to AllSigned), PowerShell lets you configure this through certificate rules in the computer’s software restriction policies.

PowerShell does not support software restriction policy path rules.

To configure these certificate rules, launch the Local Security Policy MMC snapin listed in the Administrative Tools group of the Start menu. Expand the Software Restriction Policies node, rightclick Additional Rules, and then select New Certificate Rule.

Browse to the certificate that represents the publisher you want to block, and then click OK to block that publisher.

You can also create certificate policy that allows only certificates from a centrally administered whitelist. To do this, select either Allow only all administrators to manage Trusted Publishers or Allow only enterprise administrators to manage Trusted Publishers from the Trusted Publishers Management dialog.

Get the Properties of a Group in Windows PowerShell

Problem

You want to get and list the properties of a specific security or distribution group.

Solution

To list the properties of a group, use the [adsi] type shortcut to bind to the group in Active Directory, and then pass the group to the FormatList cmdlet: $group = [adsi] "LDAP://localhost:389/cn=Management,ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$group | FormatList *

Discussion

The solution retrieves the Management group from the Sales West OU. By default, the FormatList cmdlet shows only the DN of the group, so we type FormatList * to display all properties.

If you know the property for which you want the value, specify it by name:

PS >$group.Member CN=SmithRobin,OU=West,OU=Sales,DC=Fabrikam,DC=COM CN=MyerKen,OU=West,OU=Sales,DC=Fabrikam,DC=COM

Unlike groups, some types of Active Directory objects don’t allow you to retrieve their properties by name this way. Instead, you must call the Get() method to retrieve specific properties:

PS >$group.Get("name") Management

Search for a Security or Distribution Group in PowerShell

Problem

You want to search for a specific group, but don’t know its DN.

Solution

To search for a security or distribution group, use the [adsi] type shortcut to bind to a container that holds the group in Active Directory, and then use the System. DirectoryServices.DirectorySearcher class from the .NET Framework to search for the group:

$domain = [adsi] "LDAP://localhost:389/dc=Fabrikam,dc=COM" $searcher = NewObject System.DirectoryServices.DirectorySearcher $domain $searcher.Filter = '(&(objectClass=Group)(name=Management))'

$groupResult = $searcher.FindOne() $group = $groupResult.GetDirectoryEntry()

Discussion

When you don’t know the full DN of a group, the System.DirectoryServices. DirectorySearcher class from the .NET Framework lets you search for it.

You provide an LDAP filter (in this case, searching for groups with the name of Management), and then call the FindOne() method. The FindOne() method returns the first search result that matches the filter, so we retrieve its actual Active Directory entry. Although the solution searches on the group’s name, you can search on any field in Active Directory—the mailNickname and sAMAccountName are two other good choices.

When you do this search, always try to restrict it to the lowest level of the domain possible. If we know that the Management group is in the Sales OU, it would be better to bind to that OU instead:

$domain = [adsi] "LDAP://localhost:389/ou=Sales,dc=Fabrikam,dc=COM"

For more information about the LDAP search filter syntax, search http://msdn. microsoft.com for “Search Filter Syntax.”

Place Formatted Information in a String in Windows PowerShell

Problem

You want to place formatted information (such as rightaligned text or numbers rounded to a specific number of decimal places) in a string.

Solution

Use PowerShell’s formatting operator to place formatted information inside a string.

PS >$formatString = "{0,8:D4} {1:C}`n" PS >$report = "Quantity Price`n" PS >$report += "`n" PS >$report += $formatString f 50,2.5677 PS >$report += $formatString f 3,9 PS >$report Quantity Price

0050 $2.57 0003 $9.00

Discussion

PowerShell’s string formatting operator (f) uses the same string formatting rules as the String.Format() method in the .NET Framework. It takes a format string on its left side, and the items you want to format on its right side.

In the solution, you format two numbers: a quantity and a price. The first number ({0}) represents the quantity and is rightaligned in a box of 8 characters (,8). It is formatted as a decimal number with 4 digits (:D4). The second number ({1}) represents the price, which you format as currency (:C).

For a detailed explanation of PowerShell’s formatting operator, see “Simple Opera tors”, PowerShell Language and Environment.

Although primarily used to control the layout of information, the stringformatting operator is also a readable replacement for what is normally accomplished with string concatenation:

PS >$number1 = 10 PS >$number2 = 32 PS >"$number2 divided by $number1 is " + $number2 / $number1 32 divided by 10 is 3.2

The string formatting operator makes this much easier to read:

PS >"{0} divided by {1} is {2}" f $number2, $number1, ($number2 / $number1) 32 divided by 10 is 3.2

In addition to the string formatting operator, PowerShell provides three formatting commands (FormatTable, FormatWide, and FormatList) that lets you to easily gen erate formatted reports.

Access Windows Management Instrumentation Data

Problem

You want to work with data and functionality provided by the WMI facilities in Windows.

Solution

To retrieve all instances of a WMI class, use the GetWmiObject cmdlet:

GetWmiObject ComputerName Computer Class Win32_Bios To retrieve specific instances of a WMI class, using a WMI filter, supply an argument to the –Filter parameter of the GetWmiObject cmdlet:

GetWmiObject Win32_Service Filter "StartMode = 'Auto'"

To retrieve instances of a WMI class using WMI’s WQL language, use the [WmiSearcher] type shortcut:

$query = [WmiSearcher] "SELECT * FROM Win32_Service WHERE StartMode = 'Auto'"

$query.Get() To retrieve a specific instance of a WMI class using a WMI filter, use the [Wmi] type shortcut:

[Wmi] 'Win32_Service.Name="winmgmt"'

To retrieve a property of a WMI instance, access that property as you would access a .NET property:

$service = [Wmi] 'Win32_Service.Name="winmgmt"' $service.StartMode

To invoke a method on a WMI instance, invoke that method as you would invoke a .NET method:

$service = [Wmi] 'Win32_Service.Name="winmgmt"' $service.ChangeStartMode("Manual") $service.ChangeStartMode("Automatic")

To invoke a method on a WMI class, use the [WmiClass] type shortcut to access that WMI class. Then, invoke that method as you would invoke a .NET method:

$class = [WmiClass] "Win32_Process" $class.Create("Notepad")

Discussion

Working with WMI has long been a staple of managing Windows systems—especially systems that are part of corporate domains or enterprises. WMI supports a huge amount of Windows management tasks, albeit not in a very userfriendly way.

Traditionally, administrators required either VBScript or the WMIC commandline tool to access and manage these systems through WMI. While powerful and useful, these techniques still provided plenty of opportunities for improvement. VBScript lacks support for an ad hoc investigative approach, and WMIC fails to provide (or take advantage of) knowledge that applies to anything outside WMIC.

In comparison, PowerShell lets you work with WMI just like you work with the rest of the shell. WMI instances provide methods and properties, and you work with them the same way you work with methods and properties of other objects in PowerShell.

Not only does PowerShell make working with WMI instances and classes easy once you have them, but it also provides a clean way to access them in the first place. For most tasks, you need only to use the simple [Wmi], [WmiClass],or [WmiSearcher] syntax as shown in the solution.

Along with WMI’s huge scope, though, comes a related problem: finding the WMI class that accomplishes your task.

Some advanced WMI tasks require that you enable your security privileges or adjust the packet privacy settings used in your request. The syntax given by the solution does not directly support these tasks, but PowerShell still supports these options by providing access to the underlying objects that represent your WMI query.

When you want to access a specific WMI instance with the [Wmi] accelerator, you might at first struggle to determine what properties WMI lets you search on. These properties are called key properties on the class.

For more information about the GetWmiObject cmdlet, type GetHelp GetWmiObject.

Work with .NET Objects

Problem

You want to use and interact with one of the features that make PowerShell so powerful—its intrinsic support for .NET objects.

Solution

PowerShell offers ways to access methods (both static and instance) and properties.

To call a static method on a class, place the type name in square brackets, and then separate the class name from the method name with two colons:

[ClassName]::MethodName(parameter list)

To call a method on an object, place a dot between the variable that represents that object and the method name:

$objectReference.MethodName(parameter list)

To access a static property on a class, place the type name in square brackets, and then separate the class name from the property name with two colons:

[ClassName]::PropertyName

To access a property on an object, place a dot between the variable that represents that object and the property name:

$objectReference.PropertyName

Discussion

One feature that gives PowerShell its incredible reach into both system administration and application development is its capability to leverage Microsoft’s enormous and broad .NET Framework. The .NET Framework is a large collection of classes. Each class embodies a specific concept and groups closely related functionality and information. Working with the .NET Framework is one aspect of PowerShell that introduces a revolution to the world of management shells.

An example of a class from the .NET Framework is System.Diagnostics.Process— the grouping of functionality that “provides access to local and remote processes, and enables you to start and stop local system processes.”

The terms type and class are often used interchangeably.

Classes contain methods (which allow you to perform operations) and properties (which allow you to access information).

For example, the GetProcess cmdlet generates System.Diagnostics.Process objects, not a plaintext report like traditional shells. Managing these processes becomes incredibly easy, as they contain a rich mix of information (properties) and operations (methods). You no longer have to parse a stream of text for the ID of a process—you can just ask the object directly!

PS >$process = GetProcess Notepad PS >$process.Id 3872

Static methods

[ClassName]::MethodName(parameter list)

Some methods apply only to the concept the class represents. For example, retrieving all running processes on a system relates to the general concept of processes, instead of a specific process. Methods that apply to the class/type as a whole are called static methods.

For example:

PS >[System.Diagnostics.Process]::GetProcessById(0) This specific task is better handled by the GetProcess cmdlet, but it demonstrates PowerShell’s capability to call methods on .NET classes. It calls the static GetProcessById method on the System.Diagnostics.Process class to get the process with the ID of 0. This generates the following output:

Handles
NPM(K)
PM(K)
WS(K) VM(M)
CPU(s)
Id ProcessName

0
0
0
16
0

0 Idle

Instance methods

$objectReference.MethodName(parameter list)

Some methods relate only to specific, tangible realizations (called instances) of a class. An example of this would be stopping a process actually running on the system, as opposed to the general concept of processes. If $objectReference refers to a specific System.Diagnostics.Process (as output by the GetProcess cmdlet, for example), you may call methods to start it, stop it, or wait for it to exit. Methods that act on instances of a class are called instance methods.

The term object is often used interchangeably with the term instance.

For example:

PS >$process = GetProcess Notepad

PS >$process.WaitForExit() Stores the process with an ID of 0 into the $process variable. It then calls the WaitForExit() instance method on that specific process to pause PowerShell until the process exits.

To learn about the different sets of parameters (overloads) that a given method supports, type that method name without any parameters:

PS >$now = GetDate

PS >$now.AddDays

MemberType : Method OverloadDefinitions : {System.DateTime AddDays(Double value)}

TypeNameOfValue
: System.Management.Automation.PSMethod

Value
: System.DateTime AddDays(Double value)

Name
: AddDays

IsInstance
: True

Static properties

[ClassName]::PropertyName

or

[ClassName]::PropertyName = value

Like static methods, some properties relate only to information about the concept that the class represents. For example, the System.DateTime class “represents an instant in time, typically expressed as a date and time of day.” It provides a Now static property that returns the current time:

PS >[System.DateTime]::Now

Saturday, June 2, 2007 4:57:20 PM This specific task is better handled by the GetDate cmdlet, but it demonstrates PowerShell’s capability to access properties on .NET objects.

Although relatively rare, some types allow you to set the value of some static properties as well: for example, the [System.Environment]::CurrentDirectory property. This property represents the process’s current directory—which represents PowerShell’s startup directory, as opposed to the path you see in your prompt.

Instance properties

$objectReference.PropertyName

or

$objectReference.PropertyName = value

Like instance methods, some properties relate only to specific, tangible realizations (called instances) of a class. An example of this would be the day of an actual instant in time, as opposed to the general concept of dates and times. If $objectReference refers to a specific System.DateTime (as output by the GetDate cmdlet or [System. DateTime]::Now, for example), you may want to retrieve its day of week, day, or month. Properties that return information about instances of a class are called instance properties.

For example:

PS >$today = GetDate PS >$today.DayOfWeek Saturday

This example stores the current date in the $today variable. It then calls the DayOfWeek instance property to retrieve the day of the week for that specific date.

With this knowledge, the next questions are: “How do I learn about the functionality available in the .NET Framework?” and “How do I learn what an object does?”

Output Warnings, Errors, and Terminating Errors in PowerShell

Problem

You want your script to notify its caller of a warning, error, or terminating error.

############################################################################## ## ## GetWarningsAndErrors.ps1 ## ## Demonstrates the functionality of the WriteWarning, WriteError, and throw ## statements ## ##############################################################################

WriteWarning "Warning: About to generate an error" WriteError "Error: You are running this script" throw "Could not complete operation."

Solution

To write warnings and errors, use the WriteWarning and WriteError cmdlets, respectively. Use the throw statement to generate a terminating error.

Discussion

When you need to notify the caller of your script about an unusual condition, the WriteWarning, WriteError, and throw statements are the way to do it. If your user should consider the message as more of a warning, use the WriteWarning cmdlet. If your script encounters an error (but can reasonably continue past that error), use the WriteError cmdlet. If the error is fatal and your script simply cannot continue, use a throw statement.

Verify Integrity of File Sets

Problem

You want to determine whether any files have been modified or damaged in a set of files.

Solution

To verify the integrity of file sets, use the GetFileHash script provided “Program: Get the MD5 or SHA1 Hash of a File” to generate the signatures of those files in question. Do the same for the files on a known good system. Finally, use the CompareObject cmdlet to compare those two sets.

Discussion

To generate the information from the files in question, use a command like:

dir C:\Windows\System32\WindowsPowerShell\v1.0 | GetFileHash | ExportCliXml c:\temp\PowerShellHashes.clixml

This command gets the hash values of the files from C:\Windows\System32\ WindowsPowerShell\v1.0, and uses the ExportCliXml cmdlet to store that data in a file.

Transport this file to a system with files in a known good state, and then import the data from that file.

$otherHashes = ImportCliXml c:\temp\PowerShellHashes.clixml

You can also map a network drive to the files in question and skip the export, transport, and import steps altogether:

net use x: \\leedesk\c$\Windows\System32\WindowsPowerShell\

v1.0

$otherHashes = dir x: | GetFileHash

Generate the information from the files you know are in a good state:

$knownHashes = dir C:\Windows\System32\WindowsPowerShell\v1.0 | GetFileHash Finally, use the CompareObject cmdlet to detect any differences: CompareObject $otherHashes $knownHashes Property Path,HashValue

If there are any differences, the CompareObject cmdlet displays them in a list, as shown in Example 191.

Example 191. The CompareObject cmdlet showing differences between two files

PS >CompareObject $otherHashes $knownHashes Property Path,HashValue

Path HashValue SideIndicator

Example 191. The CompareObject cmdlet showing differences between two files (continued)

system.management.aut... 247F291CCDA8E669FF9FA... => system.management.aut... 5A68BC5819E29B8E3648F... =

PS >CompareObject $otherHashes $knownHashes Property Path,HashValue | >> SelectObject Path >>

Path

system.management.automation.dllhelp.xml system.management.automation.dllhelp.xml

For more information about the CompareObject cmdlet, type GetHelp CompareObject. For more information about the ExportCliXml and ImportCliXml cmdlets, type GetHelp ExportCliXml and GetHelp ImportCliXml, respectively.

Display the Properties of an Item As a List in Windows PowerShell

Problem

You have an item (for example, an error record, directory item, or .NET object), and you want to display detailed information about that object in a list format.

Solution

To display detailed information about an item, pass that item to the FormatList cmdlet. For example, to display an error in list format, type the commands:

$currentError = $error[0] $currentError | FormatList Force

Discussion

The FormatList cmdlet is one of the three PowerShell formatting cmdlets. These cmdlets include FormatTable, FormatList, and FormatWide. The FormatList cmdlet takes input and displays information about that input as a list. By default, PowerShell takes the list of properties to display from the *.format.ps1xml files in PowerShell’s installation directory. To display all properties of the item, type FormatList *. Sometimes, you might type FormatList * but still not get a list of the item’s properties. This happens when the item is defined in the *.format.ps1xml files, but does not define anything to be displayed for the list command. In that case, type

FormatList Force.

For more information about the FormatList cmdlet, type GetHelp FormatList.

Find Items in an Array Greater or Less Than a Value

Problem

You have an array and want to find all elements greater or less than a given item or value.

Solution

To find all elements greater or less than a given value, use the –gt, ge, lt, and –le comparison operators:

PS >$array = "Item 1","Item 2","Item 3","Item 1","Item 12" PS >$array ge "Item 3" Item 3 PS >$array lt "Item 3" Item 1 Item 2 Item 1 Item 12

Discussion

The gt, ge, lt, and le operators are useful ways to find elements in a collection that are greater or less than a given value. Like all other PowerShell comparison operators, these use the comparison rules of the items in the collection. Since the array in the solution is an array of strings, this result can easily surprise you:

PS >$array lt "Item 2" Item 1 Item 1 Item 12

The reason for this becomes clear when you look at the sorted array—"Item 12" comes before "Item 2" alphabetically, which is the way that PowerShell compares arrays of strings.

PS >$array | SortObject Item 1 Item 1 Item 12 Item 2 Item 3

Use the ArrayList Class for Advanced Array Tasks

Problem

You have an array that you want to frequently add elements to, remove elements from, search, and modify.

Solution

To work with an array frequently after you define it, use the System.Collections. ArrayList class:

PS >$myArray = NewObject System.Collections.ArrayList PS >[void] $myArray.Add("Hello") PS >[void] $myArray.AddRange( ("World","How","Are","You") ) PS >$myArray Hello World How Are You PS >$myArray.RemoveAt(1) PS >$myArray Hello How Are You

Discussion

Like most other languages, arrays in PowerShell stay the same length once you create them. PowerShell allows you to add items, remove items, and search for items in an array, but these operations may be time consuming when you are dealing with large amounts of data. For example, to combine two arrays, PowerShell creates a new array large enough to hold the contents of both arrays and then copies both arrays into the destination array.

In comparison, the ArrayList class is designed to let you easily add, remove, and search for items in a collection.

PowerShell passes along any data that your script generates, unless you capture it or cast it to [void]. Since it is designed primarily to be used from programming languages, the System.Collections.ArrayList

class produces output, even though you may not expect it to. To prevent it from sending data to the output pipeline, either capture the data or cast it to [void]:

PS >$collection = NewObject System.Collections.ArrayList PS >$collection.Add("Hello") 0 PS >[void] $collection.Add("World")

If you plan to add and remove data to and from an array frequently, the System. Collections.ArrayList class provides a more dynamic alternative.