Skip to main content

Windows

Retrieve a Specific Event Log Entry

Problem

You want to retrieve a specific event log entry.

Solution

To retrieve a specific event log entry, use the GetEventLog cmdlet to retrieve the entries in the event log, and then pipe them to the WhereObject cmdlet to filter them to the one you are looking for.

PS >GetEventLog System | WhereObject { $_.Index –eq 2920 }

Index Time Type Source EventID Message

2920 May 06 09:18 Info Service Control M... 7036 The Logical Disk...

Discussion

If you’ve listed the items in an event log or searched it for entries that have a message with specific text, you often want to get more details about a specific event log entry.

Since the GetEventLog cmdlet retrieves rich objects that represent event log entries, you can pipe them to the WhereObject cmdlet for equally rich filtering.

By default, PowerShell’s default table formatting displays a summary of event log entries. If you are retrieving a specific entry, however, you are probably interested in seeing more details about the entry. In this case, use the FormatList cmdlet to format these entries in a more detailed list view, as shown in Example 204.

Example 204. A detailed list view of an event log entry

PS > GetEventLog System | WhereObject { $_.Index –eq 2920 } | >> FormatList >>

Index
: 2920

EntryType
: Information

EventID
: 7036

Message
: The Logical Disk Manager Administrative Service servi

ce entered the stopped state.

Category
: (0)

CategoryNumber
: 0

ReplacementStrings : {Logical Disk Manager Administrative Service, stopped

} Source : Service Control Manager TimeGenerated : 5/6/2007 9:18:25 AM TimeWritten : 5/6/2007 9:18:25 AM UserName :

Index : 2919 (...)

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

Use Console Files to Load and Save Sets of Snapins in Windows PowerShell

Problem

You want to load PowerShell with a set of additional snapins, but do not want to modify your (or the user’s) profile.

Solution

Once you register a snapin on your system, you can add its snapin identifier to a PowerShell console file to load it. When you specify that file as the PsConsoleFile parameter of PowerShell.exe, PowerShell loads all snapins defined by the console file into the new session.

Save the list of currently loaded snapins to a console file:

ExportConsole Filename.psc1

Load PowerShell with the set of snapins defined in the file Filename.psc1:

PowerShell PsConsoleFile Filename.psc1

Discussion

PowerShell console files are simple XML files that list the identifiers of alreadyinstalled snapins to load. A typical console file looks like

Example 111. A typical PowerShell console file

1.0

Console files should be saved with the file extension .psc1.

Although it is common to load the console file with PowerShell’s commandline options (in scripts and automated tasks), you can also doubleclick on the console file to load it interactively.

Read a Key of User Input in Windows PowerShell

Problem

You want your script to get a single keypress from the user.

Solution

For most purposes, use the [Console]::ReadKey() method to read a key: PS >$key = [Console]::ReadKey($true)

PS >$key

KeyChar
Key
Modifiers

h
H
Alt

For highly interactive use (for example, when you care about key down and key up), use:

PS >$key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") PS >$key

VirtualKeyCode Character ControlKeyState KeyDown 16 ...ssed, NumLockOn True

PS >$key.ControlKeyState ShiftPressed, NumLockOn

Discussion

For most purposes, the [Console]::ReadKey() is the best way to get a keystroke from a user, as it accepts simple keypresses—as well as more complex keypresses that might include the Ctrl, Alt, and Shift keys.

The following function emulates the DOS pause command:

function Pause

{ WriteHost NoNewLine "Press any key to continue . . . " [Console]::ReadKey($true) | OutNull WriteHost

}

If you need to capture individual key down and key up events (including those of the Ctrl, Alt, and Shift keys), use the $host.UI.RawUI.ReadKey() method.

Program: Search the Windows Registry

Discussion

While the Windows Registry Editor is useful for searching the registry, it sometimes may not provide the power you need. For example, the registry editor does not support searches with wildcards or regular expressions.

In the filesystem, we have the SelectString cmdlet to search files for content. PowerShell does not have that for other stores, but we can write a script to do it. The key here is to think of registry key values like you think of content in a file:

  • Directories have items; items have content.
  • Registry keys have properties; properties have values.

Example 184 goes through all registry keys (and their values) for a search term and returns information about the match.

Example 184. SearchRegistry.ps1

############################################################################## ## ## SearchRegistry.ps1 ## ## Search the registry for keys or properties that match a specific value. ## ## ie: ## ## PS >SetLocation HKCU:\Software\Microsoft\ ## PS >SearchRegistry Run ## ##############################################################################

param([string] $searchText = $(throw "Please specify text to search for."))

## Helper function to create a new object that represents ## a registry match from this script function NewRegistryMatch {

param( $matchType, $keyName, $propertyName, $line )

$registryMatch = NewObject PsObject $registryMatch | AddMember NoteProperty MatchType $matchType $registryMatch | AddMember NoteProperty KeyName $keyName $registryMatch | AddMember NoteProperty PropertyName $propertyName $registryMatch | AddMember NoteProperty Line $line

$registryMatch }

## Go through each item in the registry foreach($item in GetChildItem Recurse ErrorAction SilentlyContinue) {

## Check if the key name matches if($item.Name match $searchText) {

NewRegistryMatch "Key" $item.Name $null $item.Name }

## Check if a key property matches foreach($property in (GetItemProperty $item.PsPath).PsObject.Properties) {

## Skip the property if it was one PowerShell added if(($property.Name eq "PSPath") or ($property.Name eq "PSChildName")) {

continue }

## Search the text of the property $propertyText = "$($property.Name)=$($property.Value)" if($propertyText match $searchText) {

Example 184. SearchRegistry.ps1 (continued)

NewRegistryMatch "Property" $item.Name $property.Name $propertyText } } }

Manage Operations Manager Agents

Problem

You want to manage Operations Manager agents on remote machines.

Solution

To retrieve information about installed agents, use the GetAgent cmdlet:

PS Monitoring:\Oxford.contoso.com >GetAgent | SelectObject DisplayName

DisplayName

Ibiza.contoso.com Denver.contoso.com Sydney.contoso.com

To remove an agent, use the UninstallAgent cmdlet:

PS Monitoring:\Oxford.contoso.com >GetAgent | WhereObject { $_.DisplayName match "Denver" } | >> UninstallAgent

To install an agent on a specific computer, use the InstallAgentByName function:

PS Monitoring:\Oxford.contoso.com >InstallAgentByName Oxford.contoso.com

Discussion

The GetAgent cmdlet returns a great deal of information about each agent it retrieves. The example in the solution filters this to show only the DisplayName, but you may omit the SelectObject cmdlet to retrieve all information about that agent.

If you need more control over the agent installation process, examine the content of the InstallAgentByName function:

PS Monitoring:\Oxford.contoso.com

>GetContent Function:\InstallAgentByName

The function simplifies the most common scenario for installing agents, but the InstallAgent cmdlet that supports it provides additional functionality.

For more information about the GetAgent cmdlet, type GetHelp GetAgent. For more information about the InstallAgent cmdlet, type InstallAgent. For more information about the UninstallAgent cmdlet, type GetHelp UninstallAgent.

Invoke a PowerShell Script From Outside PowerShell

Problem

You want to invoke a PowerShell script from a batch file, a logon script, scheduled task, or any other nonPowerShell application.

Solution

Launch PowerShell.exe in the following way:

PowerShell "& 'full path to script' arguments"

For example,

PowerShell "& 'c:\shared scripts\GetReport.ps1' Hello World"

Discussion

Supplying a single string argument to PowerShell.exe invokes PowerShell, runs the command as though you had typed it in the interactive shell, and then exits. Since the path to a script often contains spaces, you invoke the script by placing its name between single quotes, and after the & character. If the script name does not contain spaces, you can omit the single quotes and & character. This technique lets you invoke a PowerShell script as the target of a logon script, advanced file association, scheduled task and more.

If you are the author of the program that needs to run PowerShell scripts or commands, PowerShell lets you call these scripts and commands much more easily than calling its commandline interface.

If the command becomes much more complex than a simple script call, special characters in the application calling PowerShell (such as cmd.exe) might interfere with the command you want to send to PowerShell. For this situation, PowerShell supports an EncodedCommand parameter: a Base64 encoded representation of the Unicode string. erShell commands to a Base64 encoded form.

Example 14. Converting PowerShell commands into a Base64 encoded form

$commands = '1..10 | % { "PowerShell Rocks" }' $bytes = [System.Text.Encoding]::Unicode.GetBytes($commands) $encodedString = [Convert]::ToBase64String($bytes)

Once you have the encoded string, you can use it as the value of the EncodedCommand parameter, as shown in Example 15.

Example 15. Launching PowerShell with an encoded command from cmd.exe

Microsoft Windows [Version 6.0.6000] Copyright (c) 2006 Microsoft Corporation. All rights reserved.

C:\Users\Lee>PowerShell EncodedCommand MQAuAC4AMQAwACAAfAAgACUAIAB7ACAAIgBQAG8A↵ dwBlAHIAUwBoAGUAbABsACAAUgBvAGMAawBzACIAIAB9AA== PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks

Write Pipeline-Oriented Scripts with Cmdlet Keywords

Problem

Your script, function, or script block primarily takes input from the pipeline, and you want to write it in a way that makes this intention both easy to implement and easy to read.

Solution

To cleanly separate your script into regions that deal with the initialization, perrecord processing, and cleanup portions, use the begin, process, and end keywords, respectively.

Example 108. A pipelineoriented script that uses cmdlet keywords

function InputCounter

{ begin {

$count = 0 }

## Go through each element in the pipeline, and add up ## how many elements there were. process {

WriteDebug "Processing element $_" $count++ }

end { $count } }

This produces the following output:

PS >$debugPreference = "Continue" PS >dir | InputCounter DEBUG: Processing element CompareProperty.ps1 DEBUG: Processing element ConnectWebService.ps1 DEBUG: Processing element ConvertTextObject.ps1 DEBUG: Processing element ConvertFromFahrenheitWithFunction.ps1 DEBUG: Processing element ConvertFromFahrenheitWithLibrary.ps1 DEBUG: Processing element ConvertFromFahrenheitWithoutFunction.ps1 DEBUG: Processing element GetAliasSuggestion.ps1 (...) DEBUG: Processing element SelectFilteredObject.ps1 DEBUG: Processing element SetConsoleProperties.ps1 20

Discussion

If your script, function, or script block deals primarily with input from the pipeline, the begin, process, and end keywords let you express your solution most clearly. Readers of your script (including you!) can easily see which portions of your script deal with initialization, perrecord processing, and cleanup. In addition, separating your code into these blocks lets your script to consume elements from the pipeline as soon as the previous script produces them.

Take, for example, the GetInputWithForeach and GetInputWithKeyword functions shown in Example 109. The first visits each element in the pipeline with a foreach statement over its input, while the second uses the begin, process, and end keywords.

Example 109. Two functions that take different approaches to processing pipeline input

## Process each element in the pipeline, using a ## foreach statement to visit each element in $input function GetInputWithForeach($identifier) {

WriteHost "Beginning InputWithForeach (ID: $identifier)"

foreach($element in $input)

{ WriteHost "Processing element $element (ID: $identifier)" $element

}

WriteHost "Ending InputWithForeach (ID: $identifier)" }

## Process each element in the pipeline, using the ## cmdletstyle keywords to visit each element in $input function GetInputWithKeyword($identifier) {

begin { WriteHost "Beginning InputWithKeyword (ID: $identifier)" }

process

{ WriteHost "Processing element $_ (ID: $identifier)" $_

}

end { WriteHost "Ending InputWithKeyword (ID: $identifier)" } }

Both of these functions act the same when run individually, but the difference becomes clear when we combine them with other scripts or functions that take pipeline input. When a script uses the $input variable, it must wait until the previous script finishes producing output before it can start. If the previous script takes a long time to produce all its records (for example, a large directory listing), then your user must wait until the entire directory listing completes to see any results, rather than seeing results for each item as the script generates it.

If a script, function, or script block uses the cmdletstyle keywords, it must place all its code (aside from comments or its param statement if it uses one) inside one of the three blocks. If your code needs to define

and initialize variables or define functions, place them in the begin block. Unlike most blocks of code contained within curly braces, the code in the begin, process, and end blocks has access to variables and functions defined within the blocks before it.

When we chain together two scripts that process their input with the begin, process, and end keywords, the second script gets to process input as soon as the first script produces it.

PS >1,2,3 | GetInputWithKeyword 1 | GetInputWithKeyword 2 Beginning InputWithKeyword (ID: 1) Beginning InputWithKeyword (ID: 2) Processing element 1 (ID: 1) Processing element 1 (ID: 2) 1 Processing element 2 (ID: 1) Processing element 2 (ID: 2) 2 Processing element 3 (ID: 1) Processing element 3 (ID: 2) 3 Ending InputWithKeyword (ID: 1) Ending InputWithKeyword (ID: 2)

When we chain together two scripts that process their input with the $input variable, the second script can’t start until the first completes.

PS >1,2,3 | GetInputWithForeach 1 | GetInputWithForeach 2 Beginning InputWithForeach (ID: 1) Processing element 1 (ID: 1) Processing element 2 (ID: 1) Processing element 3 (ID: 1) Ending InputWithForeach (ID: 1) Beginning InputWithForeach (ID: 2) Processing element 1 (ID: 2) 1 Processing element 2 (ID: 2) 2 Processing element 3 (ID: 2) 3 Ending InputWithForeach (ID: 2)

When the first script uses the cmdletstyle keywords, and the second scripts uses the $input variable, the second script can’t start until the first completes.

PS >1,2,3 | GetInputWithKeyword 1 | GetInputWithForeach 2 Beginning InputWithKeyword (ID: 1) Processing element 1 (ID: 1) Processing element 2 (ID: 1) Processing element 3 (ID: 1)

Ending InputWithKeyword (ID: 1) Beginning InputWithForeach (ID: 2) Processing element 1 (ID: 2) 1 Processing element 2 (ID: 2) 2 Processing element 3 (ID: 2) 3 Ending InputWithForeach (ID: 2)

When the first script uses the $input variable and the second script uses the cmdletstyle keywords, the second script gets to process input as soon as the first script produces it.

PS >1,2,3 | GetInputWithForeach 1 | GetInputWithKeyword 2 Beginning InputWithKeyword (ID: 2) Beginning InputWithForeach (ID: 1) Processing element 1 (ID: 1) Processing element 1 (ID: 2) 1 Processing element 2 (ID: 1) Processing element 2 (ID: 2) 2 Processing element 3 (ID: 1) Processing element 3 (ID: 2) 3 Ending InputWithForeach (ID: 1) Ending InputWithKeyword (ID: 2)

Remove a File or Directory in PowerShell

Problem

You want to remove a file or directory.

Solution

To remove a file or directory, use the RemoveItem cmdlet:

PS >TestPath NewDirectory True PS >RemoveItem NewDirectory PS >TestPath NewDirectory False

Discussion

The RemoveItem cmdlet removes an item from the location you provide. The RemoveItem cmdlet doesn’t work only against the filesystem, however. Any providers that support the concept of items automatically support this cmdlet as well.

The RemoveItem cmdlet lets you specify multiple files through its Path, Include, Exclude, and Filter parameters.

If the item is a container (for example, a directory), PowerShell warns you that your action will also remove anything inside that container. You can provide the –Recurse flag if you want to prevent this message.

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

Manage Printers and Print Queues

Problem

You want to clear pending print jobs from a printer.

Solution

To manage printers attached to the system, use the Win32_Printer WMI class. By default, the WMI class lists all printers:

PS >GetWmiObject Win32_Printer | SelectObject Name,PrinterStatus

Name PrinterStatus

Microsoft Office Document Image Wr... 3 Microsoft Office Document Image Wr... 3 CutePDF Writer 3 Brother DCP1000 3

To clear the print queue of a specific printer, apply a filter based on its name and call the CancelAllJobs() method: PS >$device = GetWmiObject Win32_Printer Filter "Name='Brother DCP1000'" PS >$device.CancelAllJobs()

__GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 1 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : ReturnValue : 5

Discussion

The example in the solution uses the Win32_Printer WMI class to cancel all jobs for a printer. In addition to cancelling all print jobs, the Win32_Printer class supports other tasks:

PS >$device | GetMember MemberType Method

TypeName: System.Management.ManagementObject#root\cimv2\Win32_Printer

Name MemberType Definition

CancelAllJobs Method System.Management.ManagementBaseObject Can... Pause Method System.Management.ManagementBaseObject Pau... PrintTestPage Method System.Management.ManagementBaseObject Pri... RenamePrinter Method System.Management.ManagementBaseObject Ren... Reset Method System.Management.ManagementBaseObject Res... Resume Method System.Management.ManagementBaseObject Res... SetDefaultPrinter Method System.Management.ManagementBaseObject Set... SetPowerState Method System.Management.ManagementBaseObject Set...

Easily Import and Export Your Structured Data

Problem

You have a set of data (such as a hashtable or array) and want to save it to disk so that you can use it later. Conversely, you have saved structured data to a file and want to import it so that you can use it.

Solution

Use PowerShell’s ExportCliXml cmdlet to save structured data to disk, and the ImportCliXml cmdlet to import it again from disk.

For example, imagine storing a list of your favorite directories in a hashtable, so that you can easily navigate your system with a “Favorite CD” function. Example 85 shows this function.

Example 85. A function that requires persistent structured data

PS >$favorites = @{} PS >$favorites["temp"] = "c:\temp" PS >$favorites["music"] = "h:\lee\my music" PS >function fcd { >> param([string] $location) SetLocation $favorites[$location] >> } >> PS >GetLocation

Path

HKLM:\software

PS >fcd temp PS >GetLocation

Path

C:\temp

Unfortunately, the $favorites variable vanishes whenever you close PowerShell.

To get around this, you could recreate the $favorites variable in your profile, but another way is to export it directly to a file. This command assumes that you have already created a profile, and places the file in the same location as that profile:

PS >$filename = JoinPath (SplitPath $profile) favorites.clixml PS >$favorites | ExportCliXml $filename PS >$favorites = $null PS >$favorites PS >

Once it’s on disk, you can reload it using the ImportCliXml cmdlet, as shown in Example 86.

Example 86. Restoring structured data from disk

PS >$favorites = ImportCliXml $filename PS >$favorites

Name
Value

music
h:\lee\my music

temp
c:\temp

PS >fcd music PS >GetLocation

Path

H:\lee\My Music

Discussion

PowerShell provides the ExportCliXml and ImportCliXml cmdlets to let you easily move structured data into and out of files. These cmdlets accomplish this in a very datacentric and futureproof way—by storing only the names, values, and basic data types for the properties of that data.

By default, PowerShell stores one level of data: all directly accessible simple properties (such as the WorkingSet of a process) but a plaintext representation for anything deeper (such as a process’s

Threads collection). For information on how to control the depth of this export, type GetHelp ExportCliXml and see the explanation of the –Depth parameter.

After you import data saved by ExportCliXml, you again have access to the properties and values from the original data. PowerShell converts some objects back to their fully featured objects (such as System.DateTime objects), but for the most part does not retain functionality (for example, methods) from the original objects.