Skip to main content

Resources

Write a Function in Windows PowerShell

Problem

You have commands in your script that you want to call multiple times, or a section of your script that you consider to be a “helper” for the main purpose of your script.

Solution

Place this common code in a function, and then call that function instead. For example, this Celsius conversion code in a script:

param([double] $fahrenheit)

## Convert it to Celsius $celsius = $fahrenheit 32 $celsius = $celsius / 1.8

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

could be placed in a function (itself in a script):

param([double] $fahrenheit)

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

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

}

$celsius = ConvertFahrenheitToCelsius $fahrenheit

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

Although using a function arguably makes this specific script longer and more difficult to understand, the technique is extremely valuable (and used) in almost all nontrivial scripts.

Discussion

Once you define a function, any command after that definition can use it. This means that you must define your function before any part of your script that uses it. You might find this unwieldy if your script defines many functions, as the function definitions obscure the main logic portion of your script.

Acommon question that comes from those accustomed to batch scripting in cmd.exe is, “What is the PowerShell equivalent of a GOTO?” In situations where the GOTO is used to call subroutines or other iso

lated helper parts of the batch file, use a PowerShell function to accomplish that task. If the GOTO is used as a way to loop over something, PowerShell’s looping mechanisms are more appropriate.

In PowerShell, calling a function is designed to feel just like calling a cmdlet or a script. As a user, you should not have to know whether a little helper routine was written as a cmdlet, script, or function. When you call a function, simply add the parameters after the function name, with spaces separating each one (as shown in the solution). This is in contrast to the way that you call functions in many programming languages (such as C#), where you use parentheses after the function name and commas between each parameter.

Also, notice that the return value from a function is anything that it writes to the output pipeline (such as $celsius in the solution). You can write return $celsius if you want, but it is unnecessary.

Manage Files That Include Special Characters in PowerShell

Problem

You want to use a cmdlet that supports wildcarding but provide a filename that includes wildcard characters.

Solution

To prevent PowerShell from treating those characters as wildcard characters, use the cmdlet’s –LiteralPath (or similarly named) parameter if it defines one: GetChildItem LiteralPath '[My File].txt'

Discussion

One consequence of PowerShell’s advanced wildcard support is that the square brackets used to specify character ranges sometimes conflict with actual filenames. Consider the following example:

PS >GetChildItem | SelectObject Name

Name

[My File].txt

PS >GetChildItem '[My File].txt' | SelectObject Name PS >GetChildItem LiteralPath '[My File].txt' | SelectObject Name

Name

[My File].txt

The first command clearly demonstrates that we have a file called [My File].txt. When we try to retrieve it (passing its name to the GetChildItem cmdlet), we see no results. Since square brackets are wildcard characters in PowerShell (like * and ?), the text we provided turns into a search expression rather than a filename.

The –LiteralPath parameter (or a similarly named parameter in other cmdlets) tells PowerShell that the filename is named exactly—not a wildcard search term.

In addition to wildcard matching, filenames may sometimes run afoul of another topic—PowerShell escape sequences. For example, the backtick character (`)in PowerShell means the start of an escape sequence, such as `t (tab), `n (newline), or `a (alarm). To prevent PowerShell from interpreting a backtick as an escape sequence, surround that string in single quotes instead of double quotes.

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

For more information about PowerShell’s special characters, type GetHelp About_ Special_Characters.

Open or Close Ports in the Windows Firewall

Problem

You want to open or close ports in the Windows Firewall.

Solution

To open or close ports in the Windows Firewall, use the LocalPolicy. CurrentProfile.GloballyOpenPorts collection of the HNetCfg.FwMgr COM object.

To add a port, create a HNetCfg.FWOpenPort COM object to represent the port, and then add it to the GloballyOpenPorts collection:

$PROTOCOL_TCP = 6

$firewall = NewObject com HNetCfg.FwMgr

$port = NewObject com HNetCfg.FWOpenPort

$port.Name = "Webserver at 8080"

$port.Port = 8080

$port.Protocol = $PROTOCOL_TCP

$firewall.LocalPolicy.CurrentProfile.GloballyOpenPorts.Add($port) To close a port, remove it from the GloballyOpenPorts collection:

$PROTOCOL_TCP = 6

$firewall.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(8080, $PROTOCOL_TCP)

Discussion

The HNetCfg.FwMgr COM object provides programmatic access to the Windows Firewall in Windows XP SP2 and later. The LocalPolicy.CurrentProfile property provides the majority of its functionality.

For more information about managing the Windows Firewall through its COM API, visit http://msdn.microsoft.com and search for “Using Windows Firewall API.” The documentation provides examples in VBScript but gives a useful overview of the functionality available.

If you are unfamiliar with the VBScriptspecific portions of the documentation, the Microsoft Script Center provides a useful guide to help you convert from VBScript to PowerShell. You can find that document at http://www.microsoft.com/technet/ scriptcenter/topics/winpsh/convert/default.mspx.

How to Parse and Manage Text-Based Logfiles in Windows PowerShell

Problem

You want to parse and analyze a textbased logfile using PowerShell’s standard object management commands.

Solution

Use the ConvertTextObject script to work with textbased logfiles. With your assistance, it con verts steams of text into streams of objects, which you can then easily work with using PowerShell’s standard commands.

The ConvertTextObject script primarily takes two arguments:

  1. A regular expression that describes how to break the incoming text into groups
  2. A list of property names that the script then assigns to those text groups

As an example, you can use patch logs from the Windows directory. These logs track the patch installation details from updates applied to the machine (except for Windows Vista). One detail included in these logfiles are the names and versions of the files modified by that specific patch, as shown in Example 71.

Example 71. Getting a list of files modified by hotfixes

PS >cd $env:WINDIR PS >$parseExpression = "(.*): Destination:(.*) \((.*)\)" PS >$files = dir kb*.log Exclude *uninst.log PS >$logContent = $files | GetContent | SelectString $parseExpression PS >$logContent

(...)

  1. Destination:C:\WINNT\system32\shell32.dll (6.0.3790.205)
  2. Destination:C:\WINNT\system32\wininet.dll (6.0.3790.218)
  3. Destination:C:\WINNT\system32\urlmon.dll (6.0.3790.218)
  4. Destination:C:\WINNT\system32\shlwapi.dll (6.0.3790.212)
  5. Destination:C:\WINNT\system32\shdocvw.dll (6.0.3790.214)
  6. Destination:C:\WINNT\system32\digest.dll (6.0.3790.0)
  7. Destination:C:\WINNT\system32\browseui.dll (6.0.3790.218) (...)

Like most logfiles, the format of the text is very regular but hard to manage. In this example, you have:

A number (the number of seconds since the patch started) The text, “: Destination:” The file being patched An open parenthesis The version of the file being patched A close parenthesis

You don’t care about any of the text, but the time, file, and file version are useful properties to track:

$properties = "Time","File","FileVersion" So now, you use the ConvertTextObject script to convert the text output into a stream of objects:

PS >$logObjects = $logContent | >> ConvertTextObject ParseExpression $parseExpression PropertyName $properties >>

We can now easily query those objects using PowerShell’s builtin commands. For example, you can find the files most commonly affected by patches and service packs, as shown by Example 72.

Example 72. Finding files most commonly affected by hotfixes

PS >$logObjects | GroupObject file | SortObject Descending Count | >> SelectObject Count,Name | FormatTable Auto >>

Count Name

152 C:\WINNT\system32\shdocvw.dll 147 C:\WINNT\system32\shlwapi.dll

Example 72. Finding files most commonly affected by hotfixes (continued)

128 C:\WINNT\system32\wininet.dll

116 C:\WINNT\system32\shell32.dll

92 C:\WINNT\system32\rpcss.dll

92 C:\WINNT\system32\olecli32.dll

92 C:\WINNT\system32\ole32.dll

84 C:\WINNT\system32\urlmon.dll (...)

Using this technique, you can work with most textbased logfiles.

Discussion

In Example 72, you got all the information you needed by splitting the input text into groups of simple strings. The time offset, file, and version information served their purposes as is. In addition to the features used by Example 72, however, the ConvertTextObject script also supports a parameter that lets you control the data types of those properties. If one of the properties should be treated as a number or a DateTime, you may get incorrect results if you work with that property as a string. For more information about this functionality, see the description of the –PropertyType parameter in the ConvertTextObject script.

Although most logfiles have entries designed to fit within a single line, some span multiple lines. When a logfile contains entries that span multiple lines, it includes some sort of special marker to separate log entries from each other. Take, for example:

PS >GetContent AddressBook.txt Name: Chrissy Phone: 5551212

Name: John

Phone: 5551213

The key to working with this type of logfile comes from two places. The first is the –Delimiter parameter of the GetContent cmdlet, which makes it split the file based on that delimiter instead of newlines. The second is to write a ParseExpression Regular Expression that ignores the newline characters that remain in each record.

PS >$records = gc AddressBook.txt Delimiter "" PS >$parseExpression = "(?s)Name: (\S*).*Phone: (\S*).*" PS >$records | ConvertTextObject ParseExpression $parseExpression

Property1 Property2

Chrissy 5551212

John 5551213 The parse expression in this example uses the single line option (?s) so that the (.*) portion of the regular expression accepts newline characters as well.

For extremely large logfiles, handwritten parsing tools may not meet your needs. In those situations, specialized log management tools can prove helpful. One example is Microsoft’s free Log Parser (http://www.logparser.com ). Another common alternative is to import the log entries to a SQL database, and then perform ad hoc queries on database tables, instead.

Sign a PowerShell Script or Formatting File

Problem

You want to sign a PowerShell script so that it may be run on systems that have their execution policy set to require signed scripts.

Solution

To sign the script with your standard codesigning certificate, use the SetAuthenticodeSignature cmdlet:

$cert = @(GetChildItem cert:\CurrentUser\My CodeSigning)[0] SetAuthenticodeSignature file.ps1 $cert

Alternatively, you may also use other traditional applications (such as signtool.exe) to sign PowerShell .ps1 and .ps1xml files.

Discussion

Signing a script or formatting file provides you and your customers with two primary benefits: publisher identification and file integrity. When you sign a script or formatting file, PowerShell appends your digital signature to the end of that file. This signature verifies that the file came from you and also ensures that nobody can tamper with the content in the file without detection. If you try to load a file that has been tampered with, PowerShell provides the following error message:

File C:\temp\test.ps1 cannot be loaded. The contents of file C:\temp\test.ps1 may have been tampered because the hash of the file does not match the hash stored in the digital signature. The script will not execute on the system. Please see "gethelp about_signing" for more details.. At line:1 char:10

+ .\test.ps1

When it comes to the signing of scripts and formatting files, PowerShell participates in the standard Windows Authenticode infrastructure. Because of that, techniques you may already know for signing files and working with their signatures continue to work with PowerShell scripts and formatting files. While the SetAuthenticodeSignature cmdlet is primarily designed to support scripts and formatting files, it also supports DLLs and other standard Windows executable file types.

To sign a file, the SetAuthenticodeSignature cmdlet requires that you provide it with a valid codesigning certificate. Most certification authorities provide Authenticode codesigning certificates for a fee. By using an Authenticode codesigning certificate from a reputable certification authority (such as VeriSign or Thawte), you can be sure that all users will be able to verify the signature on your script. Some online services offer extremely cheap codesigning certificates, but be aware that many machines may be unable to verify the digital signatures created by those certificates.

You can still gain many of the benefits of code signing on your own computers by generating your own codesigning certificate. While other computers will not be able to recognize the signature, it still provides tamperprotection on your own computer.

The –TimeStampServer parameter lets you sign your script or formatting file in a way that makes the signature on your script or formatting file valid even after your codesigning certificate expires.

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

Modify Properties of a Windows PowerShell User Account

Problem

You want to modify properties of a specific user account.

Solution

To modify a user account, use the [adsi] type shortcut to bind to the user in Active Directory, and then call the Put() method to modify properties. Finally, call the SetInfo() method to apply the changes.

$user = [adsi] "LDAP://localhost:389/cn=MyerKen,ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$user.Put("Title", "Sr. Exec. Overlord") $user.SetInfo()

Discussion

The solution retrieves the MyerKen user from the Sales West OU. It then sets the user’s title to Sr. Exec. Overlord and applies those changes to Active Directory.

Place Special Characters in a String in PowerShell

Problem

You want to place special characters (such as tab and newline) in a string variable.

Solution

In an expanding string, use PowerShell’s escape sequences to include special characters such as tab and newline.

PS >$myString = "Report for Today`n" PS >$myString Report for Today

Discussion

Aliteral string uses single quotes around its text, while an expanding string uses double quotes around its text.

In a literal string, all the text between the single quotes becomes part of your string. In an expanding string, PowerShell expands variable names (such as $ENV: SystemRoot) and escape sequences (such as `n) with their values (such as the SystemRoot environment variable and the newline character).

Unlike many languages that use a backslash character (\) for escape sequences, PowerShell uses a backtick (`) character. This stems from its focus on system administration, where backslashes are ubiquitous

in path names.

Insert Dynamic Information in a String in Windows PowerShell

Problem

You want to place dynamic information (such as the value of another variable) in a string.

Solution

In an expanding string, include the name of a variable in the string to insert the value of that variable.

PS >$header = "Report for Today" PS >$myString = "$header`n" PS >$myString Report for Today

To include information more complex than just the value of a variable, enclose it in a subexpression:

PS >$header = "Report for Today" PS >$myString = "$header`n$('' * $header.Length)" PS >$myString Report for Today

Discussion

Variable substitution in an expanding string is a simple enough concept, but subexpressions deserve a little clarification.

A subexpression is the dollar sign character, followed by a PowerShell command (or set of commands) contained in parentheses:

$(subexpression)

When PowerShell sees a subexpression in an expanding string, it evaluates the subexpression and places the result in the expanding string. In the solution, the expression '' * $header.Length tells PowerShell to make a line of dashes $header.Length long.

Another way to place dynamic information inside a string is to use PowerShell’s string formatting operator, which is based on the rules of the .NET string formatting:

PS >$header = "Report for Today" PS >$myString = "{0}`n{1}" f $header,('' * $header.Length)

PS >$myString Report for Today

For more information about PowerShell’s escape characters, type GetHelp About_Escape_Character or type GetHelp About_Special_ Character.

Interact with PowerShell’s Global Environment

Problem

You want to store information in the PowerShell environment so that other scripts have access to it.

Solution

To make a variable available to the entire PowerShell session, use a $GLOBAL: prefix when you store information in that variable:

## Create the web service cache, if it doesn't already exist

if(not (TestPath Variable:\Lee.Holmes.WebServiceCache))

{

${GLOBAL:Lee.Holmes.WebServiceCache} = @{}

}

If the main purpose of your script is to provide permanent functions and variables for its caller, treat that script as a library and have the caller dotsource the script:

PS >. LibraryDirectory PS >GetDirectorySize Directory size: 53,420 bytes

Discussion

The primary guidance when it comes to storing information in the session’s global environment to avoid it when possible. Scripts that store information in the global scope are prone to breaking other scripts and prone to being broken by other scripts.

It is a common practice in batch file programming, but script parameters and return values usually provide a much cleaner alternative.

If you do find yourself needing to write variables to the global scope, make sure that you create them with a name unique enough to prevent collisions with other scripts, as illustrated in the solution. Good options for naming prefixes are the script name, author’s name, or company name.

Stop a Windows PowerShell Process

Problem

You want to stop (or kill) a process on the system.

Solution

To stop a process, use the StopProcess cmdlet, as shown in Example 212.

Example 212. Stopping a process using the StopProcess cmdlet

PS >notepad PS >GetProcess Notepad

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

42 3 1276 3916 32 0.09 3520 notepad

PS >StopProcess ProcessName notepad PS >GetProcess Notepad

Example 212. Stopping a process using the StopProcess cmdlet (continued)

GetProcess : Cannot find a process with the name 'Notepad'. Verify the process name and call the cmdlet again. At line:1 char:12

+ GetProcess Notepad

Discussion

While the parameters of the StopProcess cmdlet are useful in their own right, PowerShell’s pipeline model lets you be even more precise. The StopProcess cmdlet stops any processes that you pipeline into it, so an advanced process set generated by GetProcess automatically turns into an advanced process set for the StopProcess cmdlet to operate on:

PS >GetProcess | WhereObject { $_.WorkingSet lt 10mb } | >> SortObject Descending Name | StopProcess WhatIf >> What if: Performing operation "StopProcess" on Target "svchost (1368)". What if: Performing operation "StopProcess" on Target "sqlwriter (1772)". What if: Performing operation "StopProcess" on Target "qttask (3672)". What if: Performing operation "StopProcess" on Target "Ditto (2892)". What if: Performing operation "StopProcess" on Target "ctfmon (3904)". What if: Performing operation "StopProcess" on Target "csrss (848)". What if: Performing operation "StopProcess" on Target "BrmfRsmg (1560)". What if: Performing operation "StopProcess" on Target "AutoHotkey (3460)". What if: Performing operation "StopProcess" on Target "alg (1084)".

Notice that this example uses the –WhatIf flag on the StopProcess cmdlet. This flag lets you see what would happen if you were to run the command but doesn’t actually perform the action.

For more information about the StopProcess cmdlet, type GetHelp StopProcess. For more information about the WhereObject cmdlet, type GetHelp WhereObject.

Access Environment Variables in Windows PowerShell

Problem

You want to use an environment variable (such as the system path, or current user’s name) in your script or interactive session.

Solution

PowerShell offers several ways to access environment variables. To list all environment variables, list the children of the env drive:

GetChildItem env: To get an environment variable using a more concise syntax, precede its name with

$env:

$env:variablename

i.e.: $env:username

To get an environment variable using its Provider path, supply env: or Environment:: to the GetChildItem cmdlet:

GetChildItem env:variablename

GetChildItem Environment::variablename

Discussion

PowerShell provides access to environment variables through its environment provider. Providers let you work with data stores (such as the registry, environment variables, and aliases) much as you would access the filesystem.

By default, PowerShell creates a drive (called env) that works with the environment provider to let you access environment variables. The environment provider lets you access items in the env: drive as you would any other drive: dir env:\variablename or dir env:variablename. If you want to access the provider directly (rather than go through its drive), you can also type dir Environment::variablename.

However, the most common (and easiest) way to work with environment variables is by typing $env:variablename. This works with any provider but is most typically used with environment variables.

This is because the environment provider shares something in common with several other providers—namely support for the *Content set of core cmdlets

Example 31. Working with content on different providers

PS >"hello world" > test PS >GetContent c:test hello world PS >GetContent variable:ErrorActionPreference Continue PS >GetContent function:more param([string[]]$paths); if(($paths ne $null) and ($paths.length ne 0)) { ...

GetContent $local:file | OutHost p } } else { $input | OutHost ... PS >GetContent env:systemroot C:\WINDOWS

For providers that support the content cmdlets, PowerShell lets you interact with this content through a special variable syntax

Example 32. Using PowerShell’s special variable syntax to access content

PS >$function:more param([string[]]$paths); if(($paths ne $null) and ($paths.length ne 0)) { …

GetContent $local:file | OutHost p } } else { $input | OutHost … PS >$variable:ErrorActionPreference Continue PS >$c:test hello world PS >$env:systemroot C:\WINDOWS

This variable syntax for content management lets you to both get and set content:

PS >$function:more = { $input | less.exe } PS >$function:more $input | less.exe

Now, when it comes to accessing complex provider paths using this method, you’ll quickly run into naming issues (even if the underlying file exists):

PS >$c:\temp\test.txt Unexpected token '\temp\test.txt' in expression or statement. At line:1 char:17

+ $c:\temp\test.txt

The solution to that lies in PowerShell’s escaping support for complex variable names. To define a complex variable name, enclose it in braces:

PS >${1234123!@#$!@#$12$!@#$@!} = "Crazy Variable!" PS >${1234123!@#$!@#$12$!@#$@!} Crazy Variable! PS >dir variable:\1*

Name
Value

1234123!@#$!@#$12$!@#$@!
Crazy Variable!

… and the content equivalent (assuming that the file exists):

PS >${c:\temp\test.txt} hello world Since environment variable names do not contain special characters, this GetContent variable syntax is the best (and easiest) way to access environment variables.

View the Errors Generated by a Command

Problem

You want to view the errors generated in the current session.

Solution

To access the list of errors generated so far, use the $error variable, as shown by Example 131.

Example 131. Viewing errors contained in the $error variable

PS >1/0 Attempted to divide by zero. At line:1 char:3

+ 1/0 PS >$error[0] | FormatList Force

ErrorRecord
: Attempted to divide by zero.

StackTrace
:
at System.Management.Automation.Parser.ExpressionNode.A

(...)

Message
: Attempted to divide by zero.

Data
: {}

InnerException : System.DivideByZeroException: Attempted to divide by zero. at System.Management.Automation.ParserOps.polyDiv(Execu val, Object rval) TargetSite : System.Collections.ObjectModel.Collection`1[System.Managem

ctions.IEnumerable) HelpLink : Source : System.Management.Automation

Discussion

The PowerShell $error variable always holds the list of errors generated so far in the current shell session. This list includes both terminating and nonterminating errors.

By default, PowerShell displays error records in a customized view. If you want to view an error in a table or list (through the FormatTable or FormatList cmdlets), you must also specify the –Force option to override this customized view.

If you want to display errors in a more compact manner, PowerShell supports an additional view called CategoryView that you set through the $errorView preference variable:

PS >GetChildItem IDoNotExist GetChildItem : Cannot find path 'C:\IDoNotExist' because it does not exist. At line:1 char:4

+ GetChildItem IDoNotExist PS >$errorView = "CategoryView" PS >GetChildItem IDoNotExist ObjectNotFound: (C:\IDoNotExist:String) [GetChildItem], ItemNotFoundExcep tion

To clear the list of errors, call the Clear() method on the $error list:

PS >$error.Count 2 PS >$error.Clear() PS >$error.Count 0

Compare the Output of Two Commands

Problem

You want to compare the output of two commands.

Solution

To compare the output of two commands, store the output of each command in variables, and then use the CompareObject cmdlet to compare those variables:

PS >notepad PS >$processes = GetProcess PS >StopProcess ProcessName Notepad PS >$newProcesses = GetProcess PS >CompareObject $processes $newProcesses

InputObject SideIndicator

System.Diagnostics.Process (notepad) =

Discussion

The solution shows how to determine which processes have exited between the two calls to GetProcess. The SideIndicator of = tells us that the process was present in the left collection ($processes) but not in the right ($newProcesses).

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

Add Information to the End of a File in Windows PowerShell

Problem

You want to redirect the output of a pipeline into a file but add the information to the end of that file.

Solution

To redirect the output of a command into a file, use either the Append parameter of the OutFile cmdlet, or one of the appending redirection operators. Both support options to append text to the end of a file.

OutFile:

GetChildItem | OutFile Append files.txt

Redirection operators:

GetChildItem >> files.txt

Discussion

The OutFile cmdlet and redirection operators share a lot in common—and for the most part, you can use either.

Find Items in an Array That Match a Value

Problem

You have an array and want to find all elements that match a given item or term— either exactly, by pattern, or by regular expression.

Solution

To find all elements that match an item, use the –eq, like, and –match comparison operators:

PS >$array = "Item 1","Item 2","Item 3","Item 1","Item 12" PS >$array eq "Item 1" Item 1 Item 1 PS >$array like "*1*" Item 1 Item 1 Item 12 PS >$array match "Item .." Item 12

Discussion

The eq, like, and match operators are useful ways to find elements in a collection that match your given term. The –eq operator returns all elements that are equal to your term, the –like operator returns all elements that match the wildcard given in your pattern, and the –match operator returns all elements that match the regular expression given in your pattern.

Navigate the Registry

Problem

You want to navigate and explore the Windows Registry.

Solution

Use the SetLocation just as you would navigate the filesystem to navigate the registry:

PS >SetLocation HKCU: PS >SetLocation \Software\Microsoft\Windows\CurrentVersion\Run PS >GetLocation

Path

HKCU:\Software\Microsoft\Windows\CurrentVersion\Run

Discussion

PowerShell lets you navigate the Windows Registry in exactly the same way that you navigate the filesystem, certificate drives, and other navigationbased providers. Like these other providers, the registry provider supports the SetLocation cmdlet (with the standard aliases of sl, cd, and chdir), PushLocation (with the standard alias pushd), PopLocation (with the standard alias popd), and more.

Automate Wizard-Guided Tasks

Problem

You want to automate tasks you normally complete through one of the wizards in the Exchange Management Console.

Solution

To automate a wizardguided task, complete the wizard in the Exchange Management Shell, and then save the script it displays for future reference.

Discussion

Since the Exchange Management Console user interface uses PowerShell cmdlets to accomplish all its actions, every wizard displays the PowerShell script that you could run to accomplish the same task.

Launch the Exchange Management Console, and then click Recipient Configuration

➝ Mailbox. In the Actions pane, click New Mailbox. Select User Mailbox ➝ Existing User ➝ Preeda Ola. Type preeda as an alias, and then complete the wizard. The final step provides the PowerShell command that you can use next time.

Windows PowerShell

Above all else, the design of Windows PowerShell places priority on its use as an efficient and powerful interactive shell. Even its scripting language plays a critical role in this effort, as it too heavily favors interactive use.

What surprises most people when they first launch PowerShell is its similarity to the command prompt that has long existed as part of Windows. Familiar tools continue to run. Familiar commands continue to run. Even familiar hotkeys are the same. Supporting this familiar user interface, though, is a powerful engine that lets you accomplish once cumbersome administrative and scripting tasks with ease.

These help topics introduces PowerShell from the perspective of its interactive shell.

Write a Script in PowerShell

Problem

You want to store your commands in a script, so that you can share them or reuse them later.

Solution

To write a PowerShell script, create a plaintext file with your editor of choice. Add your PowerShell commands to that script (the same PowerShell commands you use from the interactive shell) and then save it with a .ps1 extension.

Discussion

One of the most important things to remember about PowerShell is that running scripts and working at the command line are essentially equivalent operations. If you see it in a script, you can type it or paste it at the command line. If you typed it on the command line, you can paste it into a text file and call it a script.

Once you write your script, PowerShell lets you call it in the same way that you call other programs and existing tools. Running a script does the same thing as running all the commands in that script.

PowerShell introduces a few features related to running scripts and tools that may at first confuse you if you aren’t aware of them. For more information about how to call scripts and existing tools.

The first time you try to run a script in PowerShell, PowerShell provides the error message:

File c:\tools\myFirstScript.ps1 cannot be loaded because the execution of scri pts is disabled on this system. Please see "gethelp about_signing" for more d etails. At line:1 char:12

+ myFirstScript

Since relatively few computer users write scripts, PowerShell’s default security policies prevent scripts from running. Once you begin writing scripts, though, you should configure this policy to something less restrictive.

When it comes to the filename of your script, picking a descriptive name is the best way to guarantee that you will always remember what that script does—or at least have a good idea. This is an issue that PowerShell tackles elegantly, by naming every cmdlet in the VerbNoun pattern: a command that performs an action (verb)onan item (noun). As an example of the usefulness of this philosophy, consider the names of typical Windows commands given in Example 101:

Example 101. The names of some standard Windows commands

PS >dir $env:WINDIR\System32\*.exe | SelectObject Name

Name

accwiz.exe actmovie.exe ahui.exe alg.exe append.exe arp.exe asr_fmt.exe asr_ldm.exe asr_pfu.exe at.exe atmadm.exe attrib.exe (...)

Compare this to the names of some standard Windows PowerShell cmdlets given in Example 102.

Example 102. The names of some standard Windows PowerShell cmdlets

PS >GetCommand | SelectObject Name

Name

AddContent AddHistory AddMember AddPSSnapin ClearContent ClearItem ClearItemProperty ClearVariable CompareObject ConvertFromSecureString ConvertPath ConvertToHtml (...)

As an additional way to improve discovery, PowerShell takes this even further with the philosophy (and explicit goal) that “you can manage 80 percent of your system with less than 50 verbs.” As you learn the standard verbs for a concept (such as Get as the standard verb of Read, Open, and so on), you can often guess the verb of a command as the first step in discovering it.

When you name your script (especially if you intend to share it), make every effort to pick a name that follows these conventions.

Find Files That Match a Pattern in PowerShell

Problem

You want to get a list of files that match a specific pattern.

Solution

Use the GetChildItem cmdlet for both simple and advanced wildcard support:

    • To find all items in the current directory that match a PowerShell wildcard, sup
    • ply that wildcard to the GetChildItem cmdlet: GetChildItem *.txt
    • To find all items in the current directory that match a providerspecific filter, sup
    • ply that filter to the –Filter parameter: GetChildItem –Filter *~2*
    • To find all items in the current directory that do not match a PowerShell wild
    • card, supply that wildcard to the –Exclude parameter: GetChildItem Exclude *.txt
  • To find all items in subdirectories that match a PowerShell wildcard, use

the –Include and –Recurse parameters: GetChildItem –Include *.txt –Recurse

• To find all items in subdirectories that match a providerspecific filter, use the

–Filter and –Recurse parameters: GetChildItem –Filter *.txt –Recurse

• To find all items in subdirectories that do not match a PowerShell wildcard, use the –Exclude and –Recurse parameters:

GetChildItem –Exclude *.txt –Recurse Use the WhereObject cmdlet for advanced regular expression support:

    • To find all items with a filename that matches a regular expression, use the
    • WhereObject cmdlet to compare the Name property to the regular expression: GetChildItem | WhereObject { $_.Name match '^KB[09]+\.log$' }
  • To find all items with a directory name that matches a regular expression, use the WhereObject cmdlet to compare the DirectoryName property to the regular expression:

GetChildItem –Recurse | WhereObject { $_.DirectoryName match 'Release' }

• To find all items with a directory name or filename that matches a regular expression, use the WhereObject cmdlet to compare the FullName property to the regular expression:

GetChildItem –Recurse | WhereObject { $_.FullName match 'temp' }

Discussion

The GetChildItem cmdlet supports wildcarding through three parameters:

Path The Path parameter is the first (and default) parameter. While you can enter simple paths such as ., C:\ or D:\Documents, you can also supply paths that include wildcards—such as *, *.txt, [az]???.log, or even C:\win*\*.N[af]?\ F*\v2*\csc.exe.

Include/Exclude The –Include and –Exclude parameters act as a filter on wildcarding that happens on the Path parameter. If you specify the –Recurse parameter, the –Include and –Exclude wildcards apply to all items returned.

The most common mistake with the –Include parameter comes when you use it against a path with no wildcards. For example, this doesn’t seem to produce the expected results:

GetChildItem $env:WINDIR Include *.log

That command produces no results, as you have not supplied an item wildcard to the path. Instead, the correct command is:

GetChildItem $env:WINDIR\* Include *.log

Filter The –Filter parameter lets you filter results based on the providerspecific filtering language of the provider from which you retrieve items. Since PowerShell’s wildcarding support closely mimics filesystem wildcards, and most people use the –Filter parameter only on the filesystem, this seems like a redundant (and equivalent) parameter. ASQL provider, however, would use SQL syntax in its –Filter parameter. Likewise, an Active Directory provider would use LDAP paths in its –Filter parameter.

Although it may not be obvious, the filesystem provider’s filtering language is not exactly the same as the PowerShell wildcard syntax. For example, the Filter parameter matches against the short filenames, too:

PS >GetChildItem | SelectObject Name

Name

A Long File Name With Spaces Also.txt A Long File Name With Spaces.txt

PS >GetChildItem *1* | SelectObject Name PS >GetChildItem Filter *1* | SelectObject Name

Name

A Long File Name With Spaces.txt

On the other hand, PowerShell’s wildcard syntax supports far more than the filesystem’s native filtering language. For more information about the PowerShell’s wildcard syntax, type GetHelp About_WildCard.

When you want to perform filtering even more advanced than what PowerShell’s wildcarding syntax offers, the WhereObject cmdlet provides infinite possibilities. For example, to exclude certain directories from a search:

GetChildItem Rec | WhereObject { $_.DirectoryName notmatch "Debug" }

or, to list all directories:

GetChildItem | WhereObject { $_.PsIsContainer }

Since the syntax of the WhereObject cmdlet can sometimes be burdensome for simple queries, the CompareProperty script provides an attractive alternative:

GetChildItem Rec | CompareProperty DirectoryName notmatch Debug For a filter that is difficult (or impossible) to specify programmatically, the SelectFilteredObject script lets you interactively filter the output.

Because of PowerShell’s pipeline model, an advanced file set generated by GetChildItem automatically turns into an advanced file set for other cmdlets to operate on:

PS >GetChildItem Rec | WhereObject { $_.Length gt 20mb } | >> SortObject Descending Length | SelectFilteredObject | >> RemoveItem WhatIf >> What if: Performing operation "Remove File" on Target "C:\temp\backup092300 .zip". What if: Performing operation "Remove File" on Target "C:\temp\sptricking_ iT2.zip". What if: Performing operation "Remove File" on Target "C:\temp\slime.mov". What if: Performing operation "Remove File" on Target "C:\temp\helloworld. mov".

For more information about the GetChildItem cmdlet, type GetHelp GetChildItem. For more information about the WhereObject cmdlet, type GetHelp WhereObject.

Enable or Disable the Windows Firewall

Problem

You want to enable or disable the Windows Firewall.

Solution

To manage the Windows Firewall, use the LocalPolicy.CurrentProfile. FirewallEnabled property of the HNetCfg.FwMgr COM object:

PS >$firewall = NewObject com HNetCfg.FwMgr PS >$firewall.LocalPolicy.CurrentProfile.FirewallEnabled = $true PS >$firewall.LocalPolicy.CurrentProfile.FirewallEnabled True

Discussion

The HNetCfg.FwMgr COM object provides programmatic access to the Windows Firewall in Windows XP SP2 and later. The LocalPolicy.CurrentProfile property provides the majority of its functionality.

For more information about managing the Windows Firewall through its COM API, visit http://msdn.microsoft.com and search for “Using Windows Firewall API.” The documentation provides examples in VBScript but gives a useful overview of the functionality available.

If you are unfamiliar with the VBScriptspecific portions of the documentation, the Microsoft Script Center provides a useful guide to help you convert from VBScript to PowerShell. You can find that document at: http://www.microsoft.com/technet/ scriptcenter/topics/winpsh/convert/default.mspx.