Skip to main content

Resources

Program: Create Instances of Generic Objects .NET Framework

When you work with the .NET Framework, you’ll often run across classes that have the primary responsibility of managing other objects. For example, the System. Collections.ArrayList class lets you manage a dynamic list of objects. You can add objects to an ArrayList, remove objects from it, sort the objects inside, and more. These objects can be any type of object—String objects, integers, DateTime objects, and many more. However, working with classes that support arbitrary objects can sometimes be a little awkward. One example is type safety: if you accidentally add a String to a list of integers, you might not find out until your program fails.

Although the issue becomes largely moot when working only inside PowerShell, a more common complaint in strongly typed languages (such as C#) is that you have to remind the environment (through explicit casts) about the type of your object when you work with it again:

// This is C# code

System.Collections.ArrayList list =

new System.Collections.ArrayList();

list.Add("Hello World");

string result = (String) list[0];

To address these problems, the .NET Framework introduced a feature called generic types: classes that support arbitrary types of objects, but allow you to specify which type of object. In this case, a collection of strings:

// This is C# code System.Collections.ObjectModel.Collection list = new System.Collections.ObjectModel.Collection(); list.Add("Hello World");

string result = list[0]; Although the NewObject cmdlet is powerful, it doesn’t yet handle creating generic types very elegantly. For a simple generic type, you can use the syntax that the .NET Framework uses under the hood:

$coll = NewObject 'System.Collections.ObjectModel.Collection`1[System.String]'

However, that begins to fall apart if you want to use types defined outside the main mscorlib assembly, or want to create complex generic types (for example, ones that refer to other generic types).

Example 33. NewGenericObject.ps1

############################################################################## ## ## NewGenericObject.ps1 ## ## Creates an object of a generic type: ## ## Usage:

##

##
# Simple generic collection

##
NewGenericObject System.Collections.ObjectModel.Collection System.Int32

##

##
# Generic dictionary with two types

##
NewGenericObject System.Collections.Generic.Dictionary `

##
System.String,System.Int32

##

##
# Generic list as the second type to a generic dictionary

##
$secondType = NewGenericObject System.Collections.Generic.List Int32

##
NewGenericObject System.Collections.Dictionary `

##
System.String,$secondType.GetType()

##

##
# Generic type with a nondefault constructor

##
NewGenericObject System.Collections.Generic.LinkedListNode `

##
System.String "Hi"

##

##############################################################################

Collect Detailed Traces of a Script or Command

Problem

You want to access detailed debugging or diagnostic information for the execution of a script or command.

Solution

To trace a script as it executes, use the Trace parameter of the SetPsDebug cmdlet.

To view detailed trace output for the PowerShell engine and its cmdlets, use the TraceCommand cmdlet.

Discussion

The SetPsDebug cmdlet lets you configure the amount of debugging detail that PowerShell provides during the execution of a script. By setting the Trace parameter, PowerShell lets you see the lines of script as PowerShell executes them.

When you want to investigate issues in the way that your code interacts with PowerShell, or with PowerShell commands, use the TraceCommand cmdlet. The TraceCommand cmdlet provides a huge amount of detail, intended mainly for indepth problem analysis.

For example, to gain some insight into why PowerShell can’t seem to find your script:

TraceCommand CommandDiscovery PsHost { ScriptInTheCurrentDirectory.ps1 }

The TraceCommand cmdlet takes a trace source (for example, CommandDiscovery), a destination (usually –PsHost or –File), and a script block to trace. The output of this command shows that PowerShell never actually searches the current directory for your script, so you need to be explicit: .\ScriptInTheCurrentDirectory.ps1.

For more information about the TraceCommand cmdlet, type GetHelp TraceCommand. To learn what trace sources are available, see the command GetTraceSource.

List All Event Logs

Problem

You want to determine which event logs exist on a system.

Solution

To list event logs on a system, use the –List parameter of the GetEventLog cmdlet: PS >GetEventLog List

Max(K) Retain OverflowAction Entries Name

512 0 OverwriteAsNeeded 2,157 ADAM (Test)

512 7 OverwriteOlder 2,090 Application

512 7 OverwriteOlder 0 Internet Explorer

8,192 45 OverwriteOlder 0 Media Center

512 7 OverwriteOlder 0 ScriptEvents

512 7 OverwriteOlder 2,368 System

15,360 0 OverwriteAsNeeded 0 Windows PowerShell

Discussion

The –List parameter of the GetEventLog cmdlet generates a list of the event logs registered on the system. Like the output of nearly all PowerShell commands, these event logs are fully featured .NET objects—in this case, objects of the .NET System. Diagnostics.EventLog type. For information on how to use these objects to write entries to an event log.

Although the heading of the GetEventLog output shows a table heading called Name, the actual property you need to use in WhereObject (and similar commands) is Log.

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

Manage the Error Output of Windows PowerShell Commands

Problem

You want to display detailed information about errors that come from commands.

Solution

To list all errors (up to $MaximumErrorCount) that have occurred in this session, access the $error array:

$error

To list the last error that occurred in this session, access the first element in the $error array:

$error[0]

To list detailed information about an error, pipe the error into the FormatList cmdlet with the Force parameter:

$currentError = $error[0]

$currentError | FormatList Force

To list detailed information about the command that caused an error, access its InvocationInfo property:

$currentError = $error[0]

$currentError.InvocationInfo To display errors in a more succinct categorybased view, change the $errorView variable to "CategoryView":

$errorView = "CategoryView" To clear the list of errors collected by PowerShell so far, call the Clear( ) method on the $error variable:

$error.Clear( )

Discussion

Errors are a simple fact of life in the administrative world. Not all errors mean disaster, though. Because of this, PowerShell separates errors into two categories: nonterminating and terminating.

Nonterminating errors are the most common type of error. They indicate that the cmdlet, script, function, or pipeline encountered an error that it was able to recover from or was able to continue past. An example of a nonterminating error comes from the CopyItem cmdlet. If it fails to copy a file from one location to another, it can still proceed with the rest of the files specified.

Aterminating error, on the other hand, indicates a deeper, more fundamental error in the operation. An example of this can again come from the CopyItem cmdlet when you specify invalid commandline parameters.

For more information on how to handle both nonterminating and terminating errors, see Chapter 13, Tracing and Error Management.

Sort a Hashtable by Key or Value in Windows PowerShell

Problem

You have a hashtable of keys and values, and want to get the list of values that result from sorting the keys in order.

Solution

To sort a hashtable, use the GetEnumerator() method on the hashtable to gain access to its individual elements. Then use the SortObject cmdlet to sort by Name or Value.

foreach($item in $myHashtable.GetEnumerator() | Sort Name)

{

$item.Value

}

Discussion

Since the primary focus of a hashtable is to simply map keys to values, you should not depend on it to retain any ordering whatsoever—such as the order you added the items, the sorted order of the keys, or the sorted order of the values.

This becomes clear in Example 113.

Example 113. A demonstration of hashtable items not retaining their order

PS >$myHashtable = @{} PS >$myHashtable["Hello"] = 3 PS >$myHashtable["Ali"] = 2 PS >$myHashtable["Alien"] = 4 PS >$myHashtable["Duck"] = 1 PS >$myHashtable["Hectic"] = 11

PS >$myHashtable

Name
Value

Hectic
11

Duck
1

Alien
4

Hello
3

Ali
2

However, the hashtable object supports a GetEnumerator() method that lets you deal with the individual hashtable entries—all of which have a Name and Value property. Once you have those, we can sort by them as easily as we can sort any other PowerShell data. Example 114 demonstrates this technique.

Example 114. Sorting a hashtable by name and value

PS >$myHashtable.GetEnumerator() | Sort Name

Name Value

Ali 2 Alien 4 Duck 1 Hectic 11 Hello 3

PS >$myHashtable.GetEnumerator() | Sort Value

Name Value

Duck 1 Ali 2 Hello 3 Alien 4 Hectic 11

Remove a Registry Key

Problem

You want to remove a registry key and all its properties.

Solution

To remove a registry key, use the RemoveItem cmdlet: PS >dir

Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\ Microsoft\Windows\CurrentVersion\Run

SKC VC Name Property

0 0 Spyware {}

PS >RemoveItem Spyware

Discussion

The registry provider lets you remove items and containers with the RemoveItem cmdlet. The RemoveItem cmdlet has the standard aliases rm, rmdir, del, erase, and rd.

As always, use caution when changing information in the registry. Deleting or changing the wrong item can easily render your system unbootable.

As in the filesystem, the RemoveItem cmdlet lets you specify multiple files through its Path, Include, Exclude, and Filter parameters.

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

Manage Transport Rules

Problem

You want to manage transport rules applied to incoming or outgoing mail.

Solution

To create transport rules on the server, use the NewTransportRule cmdlet, as shown in Example 251.

Example 251. Creating a new transport rule

$from = GetTransportRulePredicate FromScope $from.Scope = "NotInOrganization"

$attachmentSize = GetTransportRulePredicate AttachmentSizeOver $attachmentSize.Size = 0

$action = GetTransportRuleAction ApplyDisclaimer $action.Text = "Warning: Only open attachments you were already expecting."

NewTransportRule Name "Attachment Warning" ` Conditions $from,$attachmentSize Action $action Enabled:$true

Discussion

The NewTransportRule cmdlet adds transport rules on the server. A transport rule is a collection of predicates and actions—conditions and actions that apply to the rule.

In the example given by the solution, we add a rule that warns internal users about the dangers of unexpected attachments received from the outside world. For more information about the NewTransportRule cmdlet, type GetHelp NewTransportRule.

Find a Windows PowerShell Command to Accomplish a Task

Problem

You want to accomplish a task in PowerShell but don’t know the command or cmdlet to accomplish that task.

Solution

Use the GetCommand cmdlet to search for and investigate commands.

To get the summary information about a specific command, specify the command name as an argument:

GetCommand CommandName To get the detailed information about a specific command, pipe the output of GetCommand to the FormatList cmdlet: GetCommand CommandName | FormatList To search for all commands with a name that contains text, surround the text with asterisk characters:

GetCommand *text* To search for all commands that use the Get verb, supply Get to the Verb parameter: GetCommand Verb Get

To search for all commands that act on a service, supply Service to the Noun parameter:

GetCommand Noun Service

Discussion

One of the benefits that PowerShell provides administrators is the consistency of its command names. All PowerShell commands (called cmdlets) follow a regular VerbNoun pattern. For example: GetProcess, GetEventLog, and SetLocation. The verbs come from a relatively small set of standard verbs and describe what action the cmdlet takes. The nouns are spe cific to the cmdlet and describe what the cmdlet acts on.

Knowing this philosophy, you can easily learn to work with groups of cmdlets. If you want to start a service on the local machine, the standard verb for that is Start.A good guess would be to first try StartService (which in this case would be correct), but typing GetCommand Verb Start would also be an effective way to see what things you can start. Going the other way, you can see what actions are supported on services by typing GetCommand Noun Service.

The GetCommand cmdlet is one of the three commands you will use most commonly as you explore Windows PowerShell. The other two commands are GetHelp and GetMember.

There is one important point when it comes to looking for a PowerShell command to accomplish a task. Many times, that PowerShell command does not exist, because the task is best accomplished the same way it always was—shutdown.exe to reboot a machine, netstat.exe to list protocol statistics and current TCP/IP network connections, and many more.

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

Place Common Functions in a Library in Windows PowerShell

Problem

You’ve developed a useful set of functions and want to share them between multiple scripts.

Solution

First, place these common function definitions by themselves in a script, with a name that starts with Library. While the Library prefix is not required, it is a useful naming convention. Example 104 demonstrates this approach.

Example 104. A library of temperature functions

## LibraryTemperature.ps1 ## Functions that manipulate and convert temperatures

## Convert Fahrenheit to Celsius function ConvertFahrenheitToCelsius([double] $fahrenheit)

Example 104. A library of temperature functions (continued)

{ $celsius = $fahrenheit 32 $celsius = $celsius / 1.8 $celsius

}

Next, dotsource that library from any scripts that need to use those functions, as shown by Example 105.

Example 105. A script that uses a library

param([double] $fahrenheit)

$scriptDirectory = SplitPath $myInvocation.MyCommand.Path . (JoinPath $scriptDirectory LibraryTemperature.ps1)

$celsius = ConvertFahrenheitToCelsius $fahrenheit

## Output the answer "$fahrenheit degrees Fahrenheit is $celsius degrees Celsius."

Discussion

Although mostly used for libraries, you can dotsource any script or function. When you dotsource a script or function, PowerShell acts as though the calling script itself had included the commands from that script or function.

Monitor a File for Changes

Problem

You want to monitor the end of a file for new content.

Solution

To monitor the end of a file for new content, use the –Wait parameter of the GetContent cmdlet.

GetContent log.txt Wait

Discussion

The –Wait parameter on the GetContent cmdlet acts much like the traditional Unix tail command with the –follow parameter. If you provide the –Wait parameter, the GetContent cmdlet reads the content of the file but doesn’t exit. When a program appends new content to the end of the file, the GetContent cmdlet returns that content and continues to wait.

Unlike the Unix tail command, the GetContent cmdlet does not support a feature to let you start reading from the end of a file. If you need to monitor the end of an extremely large file, a specialized file moni

toring utility is a valid option.

For more information about the GetContent cmdlet, type GetHelp GetContent. For more information about the –Wait parameter, type GetHelp FileSystem.

Manage Scheduled Tasks on a Computer

Problem

You want to schedule a task on a computer.

Solution

To manage scheduled tasks, use the schtasks.exe application. To view the list of scheduled tasks:

PS >schtasks

TaskName Next Run Time Status ==================================== ======================== ============= Defrag C 03:00:00, 5/21/2007 User_Feed_Synchronization{CA4D6D9C 18:34:00, 5/20/2007 User_Feed_Synchronization{CA4D6D9C 18:34:00, 5/20/2007

To schedule a task to defragment C: every day at 3:00 a.m.:

schtasks /create /tn "Defrag C" /sc DAILY ` /st 03:00:00 /tr "defrag c:" /ru Administrator

To remove a scheduled task by name:

schtasks /delete /tn "Defrag C"

Discussion

The example in the solution tells the system to defragment C: every day at 3:00 a.m.. It runs this command under the Administrator account, since the defrag.exe command requires administrative privileges. In addition to scheduling tasks on the local computer, the schtasks.exe application also allows you to schedule tasks on remote computers.

On Windows Vista, the schtasks.exe application has been enhanced to support event triggers, conditions, and additional settings.

For more information about the schtasks.exe application, type schtasks /?.

How to Search and Replace Text in a File in Windows PowerShell

Problem

You want to search for text in a file and replace that text with something new.

Solution

To search and replace text in a file, first store the content of the file in a variable, and then store the replaced text back in that file as shown in Example 74.

Example 74. Replacing text in a file

PS >$filename = "file.txt" PS >$match = "source text" PS >$replacement = "replacement text" PS > PS >$content = GetContent $filename PS >$content This is some source text that we want to replace. One of the things you may need to be careful careful about with Source Text is when it spans multiple lines, and may have different Source Text capitalization. PS > PS >$content = $content creplace $match,$replacement PS >$content This is some replacement text that we want to replace. One of the things you may need to be careful careful about with Source Text is when it spans multiple lines, and may have different Source Text capitalization. PS >$content | SetContent $filename

Discussion

Using PowerShell to search and replace text in a file (or many files!) is one of the best examples of using a tool to automate a repetitive task. What could literally take months by hand can be shortened to a few minutes (or hours, at most).

Notice that the solution uses the –creplace operator to replace text in a casesensitive manner. This is almost always what you will want to do, as the replacement text uses the exact capitalization that you pro

vide. If the text you want to replace is capitalized in several different ways (as in the term “Source Text” from the solution), then search and replace several times with the different possible capitalizations.

Example 74 illustrates what is perhaps the simplest (but actually most common) scenario:

  • You work with an ASCII text file.
  • You replace some literal text with a literal text replacement.
  • You don’t worry that the text match might span multiple lines.
  • Your text file is relatively small.

If some of those assumptions don’t hold true, then this discussion shows you how to tailor the way you search and replace within this file.

Work with files encoded in Unicode or another (OEM) code page

By default, the SetContent cmdlet assumes that you want the output file to contain plain ASCII text. If you work with a file in another encoding (for example, Unicode or an OEM code page such as Cyrillic), use the –Encoding parameter of the OutFile cmdlet to specify that:

$content | OutFile –Encoding Unicode $filename $content | OutFile –Encoding OEM $filename

Replace text using a pattern instead of plain text

Although it is most common to replace one literal string with another literal string, you might want to replace text according to a pattern in some advanced scenarios. One example might be swapping first name and last name. PowerShell supports this type of replacement through its support of regular expressions in its replacement operator:

PS >$content = GetContent names.txt PS >$content John Doe Mary Smith PS >$content replace '(.*) (.*)','$2, $1' Doe, John Smith, Mary

Replace text that spans multiple lines

The GetContent cmdlet used in the solution retrieves a list of lines from the file. When you use the –replace operator against this array, it replaces your text in each of those lines individually. If your match spans multiple lines, as shown between lines 3 and 4 in Example 74, the –replace operator will be unaware of the match and will not perform the replacement.

If you want to replace text that spans multiple lines, then it becomes necessary to stop treating the input text as a collection of lines. Once you stop treating the input as a collection of lines, it is also important to use a replacement expression that can ignore line breaks, as shown in Example 75.

Example 75. Replacing text across multiple lines in a file

$filename = GetItem file.txt $singleLine = [System.IO.File]::ReadAllText($filename.FullName) $content = $singleLine creplace "(?s)Source(\s*)Text",'Replacement$1Text'

The first and second lines of Example 75 read the entire content of the file as a sin gle string. It does this by calling the [System.IO.File]::ReadAllText() method from the .NET Framework, since the GetContent cmdlet splits the content of the file into individual lines.

The third line of this solution replaces the text by using a regular expression pattern. The section, Source(\s*)Text, scans for the word Source followed optionally by some whitespace, followed by the word Text. Since the whitespace portion of the regular expression has parentheses around it, we want to remember exactly what that whitespace was. By default, regular expressions do not let newline characters count as whitespace, so the first portion of the regular expression uses the singleline option (?s) to allow newline characters to count as whitespace. The replacement portion of the –replace operator replaces that match with Replacement, followed by the exact whitespace from the match that we captured ($1), followed by Text.

Replace text in large files

The approaches used so far store the entire contents of the file in memory as they replace the text in them. Once we’ve made the replacements in memory, we write the updated content back to disk. This works well when replacing text in small, medium, and even moderately large files. For extremely large files (for example, more than several hundred megabytes), using this much memory may burden your system and slow down your script. To solve that problem, you can work on the files linebyline, rather than with the entire file at once.

Since you’re working with the file linebyline, it will still be in use when you try to write replacement text back into it. You can avoid this problem if you write the replacement text into a temporary file until you’ve finished working with the main file. Once you’ve finished scanning through our file, you can delete it and replace it with the temporary file.

$filename = "file.txt" $temporaryFile = [System.IO.Path]::GetTempFileName()

$match = "source text" $replacement = "replacement text"

GetContent $filename | ForeachObject { $_ creplace $match,$replacement | AddContent $temporaryFile }

RemoveItem $filename MoveItem $temporaryFile $filename

Verify the Digital Signature of a PowerShell Script

Problem

You want to verify the digital signature of a PowerShell script or formatting file.

Solution

To validate the signature of a script or formatting file, use the GetAuthenticodeSignature cmdlet:

PS >GetAuthenticodeSignature .\test.ps1

Directory: C:\temp

SignerCertificate Status Path

FD48FAA9281A657DBD089B5A008FAFE61D3B32FD Valid test.ps1

Discussion

The GetAuthenticodeSignature cmdlet gets the Authenticode signature from a file. This can be a PowerShell script or formatting file, but the cmdlet also supports DLLs and other Windows standard executable file types.

By default, PowerShell displays the signature in a format that summarizes the certificate and its status. For more information about the signature, use the FormatList cmdlet, as shown in Example 162.

Example 162. PowerShell displaying detailed information about an Authenticode signature

PS >GetAuthenticodeSignature .\test.ps1 | FormatList

SignerCertificate : [Subject] CN=PowerShell User

[Issuer] CN=PowerShell Local Certificate Root

[Serial Number] 454D75B8A18FBDB445D8FCEC4942085C

[Not Before] 4/22/2007 12:32:37 AM

[Not After] 12/31/2039 3:59:59 PM

[Thumbprint] FD48FAA9281A657DBD089B5A008FAFE61D3B32FD

TimeStamperCertificate : Status : Valid StatusMessage : Signature verified. Path : C:\temp\test.ps1

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

Find the Owner of a Group in Windows PowerShell

Problem

You want to get the owner of a security or distribution group.

Solution

To determine the owner of a group, use the [adsi] type shortcut to bind to the group in Active Directory, and then retrieve the ManagedBy property: $group = [adsi] "LDAP://localhost:389/cn=Management,ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$group.ManagedBy

Discussion

The solution retrieves the owner of the Management group from the Sales West OU. To do this, it accesses the ManagedBy property of that group. This property exists only when populated by the administrator group, but it is a best practice to do so.

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 a String for Text or a Pattern in Windows PowerShell

Problem

You want to determine if a string contains another string, or want to find the position of a string within another string.

Solution

PowerShell provides several options to help you search a string for text.

Use the –like operator to determine whether a string matches a given DOSlike wildcard:

PS >"Hello World" –like "*llo W*"

True Use the –match operator to determine whether a string matches a given regular expression:

PS >"Hello World" –match '.*l[lz]o W.*$' True

Use the Contains() method to determine whether a string contains a specific string:

PS >"Hello World".Contains("World")

True

Use the IndexOf() method to determine the location of one string within another:

PS >"Hello World".IndexOf("World")

6

Discussion

Since PowerShell strings are fully featured .NET objects, they support many stringoriented operations directly. The Contains() and IndexOf() methods are two examples of the many features that the String class supports.

Although they use similar characters, simple wildcards and regular expressions serve significantly different purposes. Wildcards are much more simple than regular expressions, and because of that, more constrained. While you can summarize the rules for wildcards in just four bullet points, entire books have been written to help teach and illuminate the use of regular expressions.

Acommon use of regular expressions is to search for a string that spans multiple lines. By default, regular expressions do not search across lines, but you can use the singleline (?s) option to instruct them

to do so:

PS >"Hello `n World" match "Hello.*World" False PS >"Hello `n World" match "(?s)Hello.*World" True

Wildcards lend themselves to simple matches, while regular expressions lend themselves to more complex matches.

One difficulty sometimes arises when you try to store the result of a PowerShell command in a string, as shown in Example 53.

Example 53. Attempting to store output of a PowerShell command in a string

PS >GetHelp GetChildItem

NAME GetChildItem

SYNOPSIS Gets the items and child items in one or more specified locations.

(...)

PS >$helpContent = GetHelp GetChildItem PS >$helpContent match "location" False

The –match operator searches a string for the pattern you specify but seems to fail in this case. This is because all PowerShell commands generate objects. If you don’t store that output in another variable or pass it to another command, PowerShell converts to a text representation before it displays it to you. In Example 53, $helpContent is a fully featured object, not just its string representation:

PS >$helpContent.Name

GetChildItem

To work with the textbased representation of a PowerShell command, you can explicitly send it through the OutString cmdlet. The OutString cmdlet converts its input into the textbased form you are used to seeing on the screen:

PS >$helpContent = GetHelp GetChildItem | OutString

PS >$helpContent match "location"

True

Program: Determine Properties Available to WMI Filters

When you want to access a specific WMI instance with PowerShell’s [Wmi] type shortcut, you might at first struggle to determine what properties WMI lets you search on. These properties are called key properties on the class. Example 151 gets all the properties you may use in a WMI filter for a given class.

Example 151. GetWmiClassKeyProperty.ps1

############################################################################## ## ## GetWmiClassKeyProperty.ps1 ## ## Get all of the properties that you may use in a WMI filter for a given class. ## ## ie: ## ## PS >GetWmiClassKeyProperty Win32_Process ## ##############################################################################

param( [WmiClass] $wmiClass )

## WMI classes have properties foreach($currentProperty in $wmiClass.PsBase.Properties) {

## WMI properties have qualifiers to explain more about them foreach($qualifier in $currentProperty.Qualifiers) {

## If it has a 'Key' qualifier, then you may use it in a filter if($qualifier.Name eq "Key") {

$currentProperty.Name } } }

List All Running PowerShell Services

Problem

You want to see which services are running on the system.

Solution

To list all running services, use the GetService cmdlet: PS >GetService

Status
Name
DisplayName

Running
ADAM_Test
Test

Stopped
Alerter
Alerter

Running
ALG
Application Layer Gateway Service

Stopped
AppMgmt
Application Management

Stopped
aspnet_state
ASP.NET State Service

Running
AudioSrv
Windows Audio

Running
BITS
Background Intelligent Transfer Ser...

Running
Browser
Computer Browser

(...)
 

Discussion

The GetService cmdlet retrieves information about all services running on the system. Because these are rich .NET objects (of the type System.ServiceProcess. ServiceController), you can apply advanced filters and operations to make managing services straightforward.

For example, to find all running services:

PS >GetService | WhereObject { $_.Status eq "Running" }

Status Name DisplayName

Running ADAM_Test Test Running ALG Application Layer Gateway Service Running AudioSrv Windows Audio Running BITS Background Intelligent Transfer Ser... Running Browser Computer Browser Running COMSysApp COM+ System Application Running CryptSvc Cryptographic Services

Or, to sort services by the number of services that depend on them:

PS >GetService | SortObject Descending { $_.DependentServices.Count }

Status Name DisplayName

Running RpcSs Remote Procedure Call (RPC) Running PlugPlay Plug and Play Running lanmanworkstation Workstation Running SSDPSRV SSDP Discovery Service Running TapiSrv Telephony (...)

Since PowerShell returns fullfidelity .NET objects that represent system services, these tasks and more become incredibly simple due to the rich amount of information that PowerShell returns for each service. For more information about the GetService cmdlet, type GetHelp GetService.

The GetService cmdlet displays most (but not all) information about running services. For additional information (such as the service’s startup mode), use the GetWmiObject cmdlet:

$service = GetWmiObject Win32_Service | WhereObject { $_.Name eq "AudioSrv" } $service.StartMode

Create an Instance of a .NET Object

Problem

You want to create an instance of a .NET object to interact with its methods and properties.

Solution

Use the NewObject cmdlet to create an instance of an object.

To create an instance of an object using its default constructor, use the NewObject cmdlet with the class name as its only parameter:

PS >$generator = NewObject System.Random PS >$generator.NextDouble() 0.853699042859347

To create an instance of an object that takes parameters for its constructor, supply those parameters to the NewObject cmdlet. In some instances, the class may exist in a separate library not loaded in PowerShell by default, such as the System.Windows. Forms assembly. In that case, you must first load the assembly that contains the class:

[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") $image = NewObject System.Drawing.Bitmap source.gif $image.Save("source_converted.jpg","JPEG")

To create an object and use it at the same time (without saving it for later), wrap the call to NewObject in parentheses:

PS >(NewObject Net.WebClient).DownloadString("http://live.com")

Discussion

Many cmdlets (such as GetProcess and GetChildItem) generate live .NET objects that represent tangible processes, files, and directories. However, PowerShell supports much more of the .NET Framework than just the objects that its cmdlets produce.

These additional areas of the .NET Framework supply a huge amount of functionality that you can use in your scripts and general system administration tasks.

When it comes to using most of these classes, the first step is often to create an instance of the class, store that instance in a variable, and then work with the methods and properties on that instance. To create an instance of a class, you use the NewObject cmdlet. The first parameter to the NewObject cmdlet is the type name, and the second parameter is the list of arguments to the constructor, if it takes any. The NewObject cmdlet supports PowerShell’s type shortcuts, so you never have to use the fully qualified type name.

Since the second parameter to the NewObject cmdlet is an array of parameters to the type’s constructor, you might encounter difficulty when trying to specify a parameter that itself is a list. Assuming $byte is an array of bytes:

PS >$memoryStream = NewObject System.IO.MemoryStream $bytes NewObject : Cannot find an overload for ".ctor" and the argument count: "11". At line:1 char:27

+ $memoryStream = NewObject System.IO.MemoryStream $bytes

To solve this, provide an array that contains an array:

PS >$parameters = ,$bytes PS >$memoryStream = NewObject System.IO.MemoryStream $parameters

or

PS >$memoryStream = NewObject System.IO.MemoryStream @(,$bytes)

Load types from another assembly

PowerShell makes most common types available by default. However, many are available only after you load the library (called the assembly) that defines them. The MSDN documentation for a class includes the assembly that defines it.

To load an assembly, use the methods provided by the System.Reflection.Assembly class:

PS >[Reflection.Assembly]::LoadWithPartialName("System.Web")

GAC
Version
Location

True

v2.0.50727
C:\WINDOWS\assembly\GAC_32\(…)\System.Web.dll

PS >[Web.HttpUtility]::UrlEncode("http://search.msn.com") http%3a%2f%2fsearch.msn.com

The LoadWithPartialName method is unsuitable for scripts that you want to share with others or use in a production environment. It loads the most current version of the assembly, which may not be the same

as the version you used to develop your script. To load an assembly in the safest way possible, use its fully qualified name with the [Reflection.Assembly]::Load() method.

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

Debug a Script in Windows PowerShell

Problem

You want to diagnose failures or unexpected behavior in a script interactively.

Solution

To generate debugging statements from your script, Use the WriteDebug cmdlet. If you want to step through a region carefully, surround it with SetPsDebug –Step calls. To explore the environment at a specific point of execution, add a line that calls $host.EnterNestedPrompt().

Discussion

By default, PowerShell allows you to assign data to variables you haven’t yet created (thereby creating those variables). It also allows you to retrieve data from variables that don’t exist—which usually happens by accident and almost always causes bugs. To help save you from getting stung by this problem, PowerShell provides a strict mode that generates an error if you attempt to access a nonexisting variable. Example 132 demonstrates this mode.

Example 132. PowerShell operating in strict mode

PS >$testVariable = "Hello" PS >$tsetVariable += " World" PS >$testVariable Hello PS >RemoveItem Variable:\tsetvariable PS >SetPsDebug Strict PS >$testVariable = "Hello" PS >$tsetVariable += " World" The variable $tsetVariable cannot be retrieved because it has not been set

yet. At line:1 char:14

+ $tsetVariable += " World"

For the sake of your script debugging health and sanity, strict mode should be one of the first additions you make to your PowerShell profile.

When it comes to interactive debugging (as opposed to bug prevention), PowerShell supports several of the most useful debugging features that you might be accustomed to: tracing (through the SetPsDebug –Trace statement), stepping (through the SetPsDebug –Step statement), and environment inspection (through the $host. EnterNestedPrompt() call).

As a demonstration of these techniques, consider Example 133.

Example 133. A complex script that interacts with PowerShell’s debugging features

############################################################################## ## ## InvokeComplexScript.ps1 ## ## Demonstrates the functionality of PowerShell's debugging support. ## ##############################################################################

WriteHost "Calculating lots of complex information"

$runningTotal = 0 $runningTotal += [Math]::Pow(5 * 5 + 10, 2)

WriteDebug "Current value: $runningTotal"

SetPsDebug Trace 1 $dirCount = @(GetChildItem $env:WINDIR).Count

SetPsDebug Trace 2 $runningTotal = 10 $runningTotal /= 2

SetPsDebug Step $runningTotal *= 3 $runningTotal /= 2

$host.EnterNestedPrompt()

SetPsDebug off

As you try to determine why this script isn’t working as you expect, a debugging session might look like Example 134.

Example 134. Debugging a complex script

PS >$debugPreference = "Continue" PS >InvokeComplexScript.ps1 Calculating lots of complex information DEBUG: Current value: 1225

Example 134. Debugging a complex script (continued)

DEBUG: 17+ $dirCount = @(GetChildItem $env:WINDIR).Count DEBUG: 17+ $dirCount = @(GetChildItem $env:WINDIR).Count DEBUG: 19+ SetPsDebug Trace 2 DEBUG: 20+ $runningTotal = 10 DEBUG: ! SET $runningTotal = '1215'. DEBUG: 21+ $runningTotal /= 2 DEBUG: ! SET $runningTotal = '607.5'. DEBUG: 23+ SetPsDebug Step

Continue with this operation? 24+ $runningTotal *= 3

[Y]

Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 24+ $runningTotal *= 3 DEBUG: ! SET $runningTotal = '1822.5'.

Continue with this operation? 25+ $runningTotal /= 2

[Y]

Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 25+ $runningTotal /= 2 DEBUG: ! SET $runningTotal = '911.25'.

Continue with this operation? 27+ $host.EnterNestedPrompt()

[Y]

Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 27+ $host.EnterNestedPrompt() DEBUG: ! CALL method 'System.Void EnterNestedPrompt()' PS >$dirCount

PS >$dirCount + $runningTotal 1207.25 PS >exit

Continue with this operation? 29+ SetPsDebug off

[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 29+ SetPsDebug off

While not built into a graphical user interface, PowerShell’s interactive debugging features are bound to help you diagnose and resolve problems quickly.

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

Event Logs in Windows PowerShell

Event logs form the core of most monitoring and diagnosis on Windows. To support this activity, PowerShell offers the GetEventLog cmdlet to let you query and work with event log data on a system. In addition to PowerShell’s builtin GetEventLog cmdlet, its support for the .NET Framework means that you can access event logs on remote computers, add entries to event logs, and even create and delete event logs.