Skip to main content

Resources

Access Features of the Host’s User Interface

Problem

You want to interact with features in the user interface of the hosting application, but PowerShell doesn’t directly provide cmdlets for them.

Solution

To access features of the host’s user interface, use the $host.UI.RawUI variable: $host.UI.RawUI.WindowTitle = (GetLocation)

Discussion

PowerShell itself consists of two main components. The first is an engine that interprets commands, executes pipelines, and performs other similar actions. The second is the hosting application—the way that users interact with the PowerShell engine.

The default shell, PowerShell.exe, is a user interface based on the traditional Windows console. Other applications exist that host PowerShell in a graphical user interface. In fact, PowerShell makes it relatively simple for developers to build their own hosting applications, or even to embed the PowerShell engine features into their own application.

You (and your scripts) can always depend on the functionality available through the $host.UI variable, as that functionality remains the same for all hosts. Example 127 shows the features available to you in all hosts.

Example 127. Functionality available through the $host.UI property

PS >$host.UI | GetMember | Select Name,MemberType | FormatTable Auto

Name
MemberType

(...)

Prompt
Method

PromptForChoice
Method

PromptForCredential
Method

ReadLine
Method

ReadLineAsSecureString Method Write Method WriteDebugLine Method WriteErrorLine Method WriteLine Method WriteProgress Method WriteVerboseLine Method WriteWarningLine Method RawUI Property

If you (or your scripts) want to interact with portions of the user interface specific to the current host, PowerShell provides that access through the $host.UI.RawUI variable. Example 128 shows the features available to you in the PowerShell console host.

Example 128. Functionality available through the default console host

PS >$host.UI.RawUI | GetMember | >> Select Name,MemberType | FormatTable Auto >>

Name MemberType

(...) FlushInputBuffer Method GetBufferContents Method

Example 128. Functionality available through the default console host (continued)

GetHashCode
Method

GetType
Method

LengthInBufferCells
Method

NewBufferCellArray
Method

ReadKey
Method

ScrollBufferContents
Method

SetBufferContents
Method

BackgroundColor
Property

BufferSize
Property

CursorPosition
Property

CursorSize
Property

ForegroundColor
Property

KeyAvailable
Property

MaxPhysicalWindowSize
Property

MaxWindowSize
Property

WindowPosition
Property

WindowSize
Property

WindowTitle
Property

If you rely on the hostspecific features from $host.UI.RawUI, be aware that your script will require modifications (perhaps major) before it will run properly on other hosts.

Program: Set Properties of Remote Registry Keys

Discussion

Although PowerShell does not directly let you access and manipulate the registry of a remote computer, it still supports this by working with the .NET Framework. The functionality exposed by the .NET Framework is a bit more developeroriented than we want, so we can instead use a script to make it easier to work with.

Example 188 lets you set the value of a property on a given remote registry key. In order for this script to succeed, the target computer must have the remote registry service enabled and running.

Example 188. SetRemoteRegistryKeyProperty.ps1

############################################################################## ## ## SetRemoteRegistryKeyProperty.ps1 ## ## Set the value of a remote registry key property ## ## ie: ## ## PS >$registryPath = ## "HKLM:\software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" ## PS >SetRemoteRegistryKeyProperty LEEDESK $registryPath ` ## "ExecutionPolicy" "RemoteSigned" ## ##############################################################################

param( $computer = $(throw "Please specify a computer name."), $path = $(throw "Please specify a registry path"), $property = $(throw "Please specify a property name"), $propertyValue = $(throw "Please specify a property value") )

## Validate and extract out the registry key if($path match "^HKLM:\\(.*)") {

$baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey

("LocalMachine", $computer) } elseif($path match "^HKCU:\\(.*)") {

$baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey ("CurrentUser", $computer) }

Example 188. SetRemoteRegistryKeyProperty.ps1 (continued)

else { WriteError ("Please specify a fullyqualified registry path " + "(i.e.: HKLM:\Software) of the registry key to open.") return }

## Open the key and set its value $key = $baseKey.OpenSubKey($matches[1], $true) $key.SetValue($property, $propertyValue)

## Close the key and base keys $key.Close() $baseKey.Close()

Program: Learn Aliases for Common Windows Shell Commands

In interactive use, full cmdlet names (such as GetChildItem) are cumbersome and slow to type. Although aliases are much more efficient, it takes awhile to discover them. To learn aliases more easily, you can modify your prompt to remind you of the shorter version of any aliased commands that you use.

This involves two steps:

1. Add the program, GetAliasSuggestion.ps1, to your tools directory or other directory.

Example 19. GetAliasSuggestion.ps1

############################################################################## ## ## GetAliasSuggestion.ps1 ## ## Get an alias suggestion from the full text of the last command ## ## ie: ## ## PS > GetAliasSuggestion RemoveItemProperty ## Suggestion: An alias for RemoveItemProperty is rp ## ##############################################################################

param($lastCommand)

$helpMatches = @( )

## Get the alias suggestions foreach($alias in GetAlias) {

if($lastCommand match ("\b" + [System.Text.RegularExpressions.Regex]::Escape($alias.Definition) + "\b")) { $helpMatches += "Suggestion: An alias for $($alias.Definition) is $($alias.Name)" } }

$helpMatches

2. Prompt function in your profile. If you already have a prompt function, you only need to add the content from inside the prompt function of

A useful prompt to teach you aliases for common commands

function Prompt

{ ## Get the last item from the history $historyItem = GetHistory Count 1

## If there were any history items if($historyItem) {

## Get the training suggestion for that item $suggestions = @(GetAliasSuggestion $historyItem.CommandLine)

Example 110. A useful prompt to teach you aliases for common commands (continued)

## If there were any suggestions if($suggestions) {

## For each suggestion, write it to the screen foreach($aliasSuggestion in $suggestions) {

WriteHost "$aliasSuggestion" } WriteHost ""

} }

## Rest of prompt goes here "PS [$env:COMPUTERNAME] >" }

Sort an Array or List of Items

Problem

You want to sort the elements of an array or list.

Solution

To sort a list of items, use the SortObject cmdlet: PS >GetChildItem | SortObject Descending Length | Select Name,Length

Name
Length

ConvertTextObject.ps1
6868

ConnectWebService.ps1
4178

SelectFilteredObject.ps1
3252

GetPageUrls.ps1
2878

GetCharacteristics.ps1
2515

GetAnswer.ps1
1890

NewGenericObject.ps1
1490

InvokeCmdScript.ps1
1313

Discussion

The SortObject cmdlet provides a convenient way for you to sort items by a property that you specify. If you don’t specify a property, the SortObject cmdlet follows the sorting rules of those items if they define any.

In addition to sorting by a property in ascending or descending order, the SortObject cmdlet’s –Unique switch also allows you to remove duplicates from the sorted collection.

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

Program: Create a Filesystem Hard Link

Discussion

It is sometimes useful to refer to the same file by two different names or locations. You can’t solve this problem by copying the item, because modifications to one file do not automatically affect the other.

The solution to this is called a hard link, an item of a new name that points to the data of another file. The Windows operating system supports hard links, but only Windows Vista includes a utility that lets you create them.

Example 176 lets you create hard links without needing to install additional tools. It uses (and requires) the InvokeWindowsApi.ps1 script.

Example 176. NewFilesystemHardLink.ps1

############################################################################## ## ## NewFileSystemHardLink.ps1 ## ## Create a new hard link, which allows you to create a new name by which you ## can access an existing file. Windows only deletes the actual file once ## you delete all hard links that point to it. ## ## ie: ## ## PS >"Hello" > test.txt ## PS >dir test* | select name ## ## Name ## ## test.txt ## ## PS >NewFilesystemHardLink.ps1 test2.txt test.txt ## PS >type test2.txt ## Hello ## PS >dir test* | select name ## ## Name ## ## test.txt ## test2.txt ## ##############################################################################

param( ## The new filename you want to create [string] $filename,

## The existing file that you want the new name to point to [string] $existingFilename )

## Ensure that the provided names are absolute paths

$filename = $executionContext.SessionState.Path.` GetUnresolvedProviderPathFromPSPath($filename)

$existingFilename = ResolvePath $existingFilename

## Prepare the parameter types and parameters for the CreateHardLink function $parameterTypes = [string], [string], [IntPtr] $parameters = [string] $filename, [string] $existingFilename, [IntPtr]::Zero

## Call the CreateHardLink method in the Kernel32 DLL

$currentDirectory = SplitPath $myInvocation.MyCommand.Path

$invokeWindowsApiCommand = JoinPath $currentDirectory InvokeWindowsApi.ps1

$result = & $invokeWindowsApiCommand "kernel32" ` ([bool]) "CreateHardLink" $parameterTypes $parameters

Example 176. NewFilesystemHardLink.ps1 (continued)

## Provide an error message if the call fails if(not $result) {

$message = "Could not create hard link of $filename to " + "existing file $existingFilename" WriteError $message }

List Network Adapter Properties

Problem

You want to retrieve information about network adapters on a computer.

Solution

To retrieve information about network adapters on a computer, use the Win32_ NetworkAdapterConfiguration WMI class:

GetWmiObject Win32_NetworkAdapterConfiguration –Computer > To list only those with IP addresses assigned to them, use the WhereObject cmdlet to filter on the IpEnabled property:

PS >GetWmiObject Win32_NetworkAdapterConfiguration –Computer LEEDESK |

>> WhereObject { $_.IpEnabled }

>>

DHCPEnabled : True

IPAddress : {192.168.1.100}

DefaultIPGateway : {192.168.1.1}

DNSDomain : hsd1.wa.comcast.net.

ServiceName : USB_RNDIS

Description : Linksys WirelessG USB Network Adapter with SpeedBooste

r v2 Packet Scheduler Miniport

Index : 13

Discussion

The solution uses the Win32_NetworkAdapterConfiguration WMI class to retrieve information about network adapters on a given system. By default, PowerShell displays only the most important information about the network adapter but provides access to much more.

To see all information available, use the FormatList cmdlet, as shown in Example 247.

Example 247. Using the FormatList cmdlet to see detailed information about a network adapter

PS >$adapter = GetWmiObject Win32_NetworkAdapterConfiguration | >> WhereObject { $_.IpEnabled } >> PS >$adapter

Example 247. Using the FormatList cmdlet to see detailed information about a network adapter

DHCPEnabled : True IPAddress : {192.168.1.100} DefaultIPGateway : {192.168.1.1}

DNSDomain
: hsd1.wa.comcast.net.

ServiceName
: USB_RNDIS

Description
: Linksys WirelessG USB Network Adapter with SpeedBooste

r v2 Packet Scheduler Miniport

Index : 13

PS >$adapter | FormatList *

DHCPLeaseExpires Index Description

DHCPEnabled DHCPLeaseObtained DHCPServer DNSDomain DNSDomainSuffixSearchOrder DNSEnabledForWINSResolution DNSHostName DNSServerSearchOrder

: 20070521221927.000000420

: 13

: Linksys WirelessG USB Network Adapter with SpeedBooster v2 Packet Scheduler Minipor t

: True

: 20070520221927.000000420

: 192.168.1.1

: hsd1.wa.comcast.net.

:

: False

: LeeDesk

: {68.87.69.146, 68.87.85.98}

DomainDNSRegistrationEnabled : False

FullDNSRegistrationEnabled
: True

IPAddress
: {192.168.1.100}

IPConnectionMetric
: 25

IPEnabled
: True

IPFilterSecurityEnabled
: False

WINSEnableLMHostsLookup
: True

(...)

To retrieve specific properties, access as you would access properties on other PowerShell objects:

PS >$adapter.MacAddress 00:12:17:77:B4:EB

Download a Web Page from the Internet

Problem

You want to download a web page from the Internet and work with the content as a plain string.

Solution

Use the DownloadString() method from the .NET Framework’s System.Net.WebClient class to download a web page or plain text file into a string.

PS >$source = "http://blogs.msdn.com/powershell/rss.xml" PS > PS >$wc = NewObject System.Net.WebClient PS >$content = $wc.DownloadString($source)

Discussion

Although web services are becoming increasingly popular, they are still far less common than web pages that display useful data. Because of this, retrieving data from services on the Internet often comes by means of screen scraping: downloading the HTML of the web page and then carefully separating out the content you want from the vast majority of the content that you do not.

The technique of screen scraping has been around much longer than the Internet! As long as computer systems have generated output designed primarily for humans, screen scraping tools have risen to

make this output available to other computer programs.

Unfortunately, screen scraping is an errorprone way to extract content. If the web page authors change the underlying HTML, your code will usually stop working correctly. If the site’s HTML is written as valid XHTML, you may be able to use PowerShell’s built in XML support to more easily parse the content.

Despite its fragility, pure screen scraping is often the only alternative. In Example 91, you use this approach to easily fetch Encarta “Instant Answers” from MSN Search. If the script no longer works when you run it, I apologize—although it does demonstrate the perils of screen scraping.

Example 91. GetAnswer.ps1

############################################################################## ## GetAnswer.ps1 ## ## Use Encarta's Instant Answers to answer your question ## ## Example: ## GetAnswer "What is the population of China?" ############################################################################## param([string] $question = $( throw "Please ask a question."))

function Main

{ ## Load the System.Web.HttpUtility DLL, to let us URLEncode [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Web")

## Get the web page into a single string with newlines between ## the lines. $encoded = [System.Web.HttpUtility]::UrlEncode($question) $url = "http://search.live.com/results.aspx?q=$encoded" $text = (newobject System.Net.WebClient).DownloadString($url)

Example 91. GetAnswer.ps1 (continued)

## Get the answer with annotations $startIndex = $text.IndexOf('') $endIndex = $text.IndexOf('function YNC')

## If we found a result, then filter the result if(($startIndex ge 0) and ($endIndex ge 0)) {

$partialText = $text.Substring($startIndex, $endIndex $startIndex)

## Very fragile screen scraping here $pattern = '

Clear or Remove a File from PowerShell

Problem

You want to clear the content of a file, or remove that file altogether.

Solution

To clear the content from a file, use the ClearContent cmdlet. Use the RemoveItem cmdlet to remove that file altogether, as shown by Example 171.

Example 171. Clearing content from and removing a file

PS >GetContent test.txt Hello World PS >ClearContent test.txt PS >GetContent test.txt PS >GetItem test.txt

Directory: Microsoft.PowerShell.Core\FileSystem::C:\temp

Mode
LastWriteTime
Length Name

a

4/23/2007
8:05 PM
0 test.txt

PS >RemoveItem test.txt PS >GetItem test.txt GetItem : Cannot find path 'C:\temp\test.txt' because it does not exist. At line:1 char:9

+ GetItem test.txt

Discussion

The (aptly named) ClearContent and RemoveItem cmdlets clear the content from an item and remove an item, respectively. Although the solution demonstrates this only for files in the filesystem, they in fact apply to any PowerShell providers that support the concepts of “content” and “items.” Examples of other drives that support these content and item concepts are the Function:, Alias:, and Variable:. The HKLM:, HKCU:, and Env: drives do not support the concept of content, but do let you remove items with the RemoveItem cmdlet.

The RemoveItem cmdlet has a handful of standard aliases: ri, rm, rmdir, del, erase, and rd.

For more information about the RemoveItem or ClearContent cmdlets, type GetHelp RemoveItem or GetHelp ClearContent.

Windows PowerShell Enterprise Computer Management

When working with Windows systems across an enterprise, the question often arises: “How do I do in PowerShell?” In an administrator’s perfect world, anybody who designs a feature with management implications also supports (via PowerShell cmdlets) the tasks that manage that feature. Many management tasks have been around longer than PowerShell, though, so the answer can sometimes be,

“The same way you did it before PowerShell.”

That’s not to say that your life as an administrator doesn’t improve with the introduction of PowerShell, however. PrePowerShell administration tasks generally fall into one of several models: commandline utilities, Windows Management Instrumentation (WMI) interaction, registry manipulation, file manipulation, interaction with COM objects, or interaction with .NET objects.

PowerShell makes it easier to interact with all these task models, and therefore makes it easier to manage functionality that depends on them.

Work with Numbers As Binary in Windows PowerShell

Problem

You want to work with the individual bits of a number, or work with a number built by combining a series of flags.

Solution

To directly enter a hexadecimal number, use the 0x prefix:

PS >$hexNumber = 0x1234

PS >$hexNumber

4660 To convert a number to its binary representation, supply a base of 2 to the [Convert]::ToString() method:

PS >[Convert]::ToString(1234, 2)

10011010010 To convert a binary number into its decimal representation, supply a base of 2 to the [Convert]::ToInt32() method:

PS >[Convert]::ToInt32("10011010010", 2)

1234

To manage the individual bits of a number, use PowerShell’s binary operators. In this case, the Archive flag is just one of the many possible attributes that may be true of a given file:

PS >$archive = [System.IO.FileAttributes] "Archive" PS >attrib +a test.txt PS >GetChildItem | Where { $_.Attributes band $archive } | Select Name

Name

test.txt

PS >attrib a test.txt PS >GetChildItem | Where { $_.Attributes band $archive } | Select Name PS >

Discussion

In some system administration tasks, it is common to come across numbers that seem to mean nothing by themselves. The attributes of a file are a perfect example:

PS >(GetItem test.txt).Encrypt() PS >(GetItem test.txt).IsReadOnly = $true PS >[int] (GetItem test.txt force).Attributes 16417 PS >(GetItem test.txt force).IsReadOnly = $false PS >(GetItem test.txt).Decrypt() PS >[int] (GetItem test.txt).Attributes 32

What can the numbers 16417 and 32 possibly tell us about the file?

The answer to this comes from looking at the attributes in another light—as a set of features that can be either True or False. Take, for example, the possible attributes for an item in a directory shown by Example 63.

Example 63. Possible attributes of a file

PS >[Enum]::GetNames([System.IO.FileAttributes]) ReadOnly Hidden System Directory Archive Device Normal Temporary SparseFile ReparsePoint Compressed Offline NotContentIndexed Encrypted

If a file is ReadOnly, Archive, and Encrypted, then you might consider this as a succinct description of the attributes on that file:

ReadOnly = True Archive = True Encrypted = True

It just so happens that computers have an extremely concise way of representing sets of true and false values—a representation known as binary. To represent the attributes of a directory item as binary, you simply put them in a table. We give the item a “1” if the attribute applies to the item and a “0” otherwise (see Table 61).

Table 61. Attributes of a directory item

Attribute
True (1) or False (0)

Encrypted
1

NotContentIndexed
0

Offline
0

Compressed
0

ReparsePoint
0

SparseFile
0

Temporary
0

Normal
0

Device
0

Archive
1

Directory
0


0

System
0

Hidden
0

ReadOnly
1

If we treat those features as the individual binary digits in a number, that gives us the number 100000000100001. If we convert that number to its decimal form, it becomes clear where the number 16417 came from:

PS >[Convert]::ToInt32("100000000100001", 2) 16417

This technique sits at the core of many properties that you can express as a combination of features or flags. Rather than list the features in a table, though, documentation usually describes the number that would result from that feature being the only one active—such as FILE_ATTRIBUTE_REPARSEPOINT = 0x400 . Example 64 shows the various representations of these file attributes.

Example 64. Integer, hexadecimal, and binary representations of possible file attributes

PS >$attributes = [Enum]::GetValues([System.IO.FileAttributes]) PS >$attributes | SelectObject ` >> @{"Name"="Property"; >> "Expression"= { $_ } }, >> @{"Name"="Integer"; >> "Expression"= { [int] $_ } }, >> @{"Name"="Hexadecimal"; >> "Expression"= { [Convert]::ToString([int] $_, 16) } }, >> @{"Name"="Binary"; >> "Expression"= { [Convert]::ToString([int] $_, 2) } } | >> FormatTable auto >>

Example 64. Integer, hexadecimal, and binary representations of possible file attributes (continued)

Property Integer Hexadecimal Binary

ReadOnly
1 1
1

Hidden
2 2
10

System
4 4
100

Directory
16 10
10000

Archive
32 20
100000

Device
64 40
1000000

Normal
128 80
10000000

Temporary
256 100
100000000

SparseFile
512 200
1000000000

ReparsePoint
1024 400
10000000000

Compressed
2048 800
100000000000

Offline
4096 1000
1000000000000

NotContentIndexed
8192 2000
10000000000000

Encrypted
16384 4000
100000000000000

Knowing how that 16417 number was formed, you can now use the properties in meaningful ways. For example, PowerShell’s –band operator allows you to check if a certain bit has been set:

PS >$encrypted = 16384 PS >$attributes = (GetItem test.txt force).Attributes PS >($attributes band $encrypted) –eq $encrypted True PS >$compressed = 2048 PS >($attributes band $compressed) –eq $compressed False PS >

Although the example above uses the numeric values explicitly, it would be more common to enter the number by its name:

PS >$archive = [System.IO.FileAttributes] "Archive" PS >($attributes band $archive) –eq $archive True

Simplify Math with Administrative Constants in Windows PowerShell

Problem

You want to work with common administrative numbers (that is, kilobytes, megabytes, and gigabytes) without having to remember or calculate those numbers.

Solution

Use PowerShell’s administrative constants (KB, MB, and GB) to help work with these common numbers.

Calculate the download time (in seconds) of a 10.18 megabyte file over a connection that gets 215 kilobytes per second:

PS >10.18mb / 215kb 48.4852093023256

Discussion

PowerShell’s administrative constants are based on powers of two, since those are the kind most commonly used when working with computers. Each is 1,024 times bigger than the one before it:

1kb = 1024 1mb = 1024 * 1 kb 1gb = 1024 * 1 mb

Some (such as hard drive manufacturers) prefer to call numbers based on powers of two “kibibytes,” “mebibytes,” and “gibibytes.” They use the terms “kilobytes,” “megabytes,” and “gigabytes” to mean numbers that are 1,000 times bigger than the one before it—numbers based on powers of 10.

Although not represented by administrative constants, PowerShell still makes it easy to work with these numbers in powers of 10—for example, to figure out how big a “300 GB” hard drive is when reported by Windows:

PS >$kilobyte = [Math]::Pow(10,3) PS >$kilobyte 1000 PS >$megabyte = [Math]::Pow(10,6) PS >$megabyte 1000000 PS >$gigabyte = [Math]::Pow(10,9) PS >$gigabyte 1000000000 PS >(300 * $gigabyte) / 1GB 279.396772384644

Access a .NET SDK Library

Problem

You want to access the functionality exposed by a .NET DLL, but that DLL is packaged as part of a developeroriented Software Development Kit (SDK).

Solution

To create objects contained in a DLL, use the [System.Reflection.Assembly]:: LoadFile() method to load the DLL, and the NewObject cmdlet to create objects contained in it. Example 159 illustrates this technique.

Example 159. Interacting with classes from the SharpZipLib SDK DLL

[Reflection.Assembly]::LoadFile("d:\bin\ICSharpCode.SharpZipLib.dll") $namespace = "ICSharpCode.SharpZipLib.Zip.{0}"

$zipName = JoinPath (GetLocation) "PowerShell_TDG_Scripts.zip" $zipFile = NewObject ($namespace f "ZipOutputStream") ([IO.File]::Create($zipName))

foreach($file in dir *.ps1)

{

$zipEntry = NewObject ($namespace f "ZipEntry") $file.Name

$zipFile.PutNextEntry($zipEntry) }

$zipFile.Close()

Discussion

While C# and VB.Net developers are usually the consumers of SDKs created for the .NET Framework, PowerShell lets you access the SDK features just as easily. To do this, use the [Reflection.Assembly]::LoadFile() method to load the SDK assembly, and then work with the classes from that assembly as you would work with other classes in the .NET Framework.

Although PowerShell lets you access developeroriented SDKs easily, it can’t change the fact that these SDKs are developeroriented. SDKs and programming interfaces are rarely designed with the administra

tor in mind, so be prepared to work with programming models that require multiple steps to accomplish your task.

One thing you will notice when working with classes from an SDK is that it quickly becomes tiresome to specify their fully qualified type names. For example, ziprelated classes from the SharpZipLib all start with ICSharpCode.SharpZipLib.Zip. This is called the namespace of that class. Most programming languages solve this problem with a using statement that lets you specify a list of namespaces for that language to search when you type a plain class name such as ZipEntry. PowerShell lacks a using statement, but the solution demonstrates one of several ways to get the benefits of one.

Prepackaged SDKs aren’t the only DLLs you can load this way, either. An SDK library is simply a DLL that somebody wrote, compiled, packaged, and released. If you are comfortable with any of the .NET languages, you can also create your own DLL, compile it, and use it exactly the same way.

Take, for example, the simple math library given in Example 1510. It provides a static Sum method and an instance Product method.

Example 1510. A simple C# math library

namespace MyMathLib

{ public class Methods {

public Methods() { }

public static int Sum(int a, int b) { return a + b; }

public int Product(int a, int b) { return a * b; } } }

Example 1511 demonstrates everything required to get that working in your Power Shell system.

Example 1511. Compiling, loading, and using a simple C# library

PS >notepad MyMathLib.cs

PS >SetAlias csc $env:WINDIR\Microsoft.NET\Framework\v2.0.50727\csc.exe PS >csc /target:library MyMathLib.cs

Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42 for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727 Copyright (C) Microsoft Corporation 20012005. All rights reserved.

PS >[Reflection.Assembly]::LoadFile("c:\temp\MyMathLib.dll")

GAC
Version
Location

False
v2.0.50727
c:\temp\MyMathLib.dll

PS >[MyMathLib.Methods]::Sum(10, 2)

Example 1511. Compiling, loading, and using a simple C# library (continued)

PS >$mathInstance = NewObject MyMathLib.Methods PS >$mathInstance.Product(10, 2)

Create a User Account in PowerShell

Problem

You want to create a user account in a specific OU.

Solution

To create a user in a container, use the [adsi] type shortcut to bind to the OU in Active Directory, and then call the Create() method: $salesWest = [adsi] "LDAP://localhost:389/ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$user = $salesWest.Create("User", "CN=MyerKen") $user.Put("userPrincipalName", "Ken.Myer@fabrikam.com") $user.Put("displayName", "Ken Myer") $user.SetInfo()

Discussion

The solution creates a user under the Sales West organizational unit. It sets the userPrincipalName (a unique identifier for the user), as well as the user’s display name.

When you run this script against a real Active Directory deployment (as opposed to an ADAM instance), be sure to update the sAMAccountName property, or you’ll get an autogenerated default.

Adjust Script Flow Using Conditional Statements in Windows PowerShell

Problem

You want to control the conditions under which PowerShell executes commands or portions of your script.

Solution

Use PowerShell’s if, elseif, and else conditional statements to control the flow of execution in your script.

For example:

$temperature = 90

if($temperature le 0)

{

"Balmy Canadian Summer" } elseif($temperature le 32) {

"Freezing" } elseif($temperature le 50) {

"Cold" } elseif($temperature le 70) {

"Warm" } else {

"Hot" }

Discussion

Conditional statements include the following:

if statement Executes the script block that follows it if its condition evaluates to true

elseif statement Executes the script block that follows it if its condition evaluates to true, and none of the conditions in the if or elseif statements before it evaluate to true

else statement Executes the script block that follows it if none of the conditions in the if or elseif statements before it evaluate to true

For more information about these flow control statements, type GetHelp About_ Flow_Control.

Find the Location of Common System Paths in PowerShell

Problem

You want to know the location of common system paths and special folders, such as My Documents and Program Files.

Solution

To determine the location of common system paths and special folders, use the [Environment]::GetFolderPath() method: PS >[Environment]::GetFolderPath("System") C:\WINDOWS\system32

For paths not supported by this method (such as All Users Start Menu), use the WScript.Shell COM object: $shell = NewObject Com WScript.Shell $allStartMenu = $shell.SpecialFolders.Item("AllUsersStartMenu")

Discussion

The [Environment]::GetFolderPath() method lets you access the many common locations used in Windows. To use it, provide the short name for the location (such as System or Personal). Since you probably don’t have all these short names memorized, one way to see all these values is to use the [Enum]::GetValues() method, as shown in Example 142.

Example 142. Folders supported by the [Environment]::GetFolderPath() method

PS >[Enum]::GetValues([Environment+SpecialFolder]) Desktop Programs Personal Favorites Startup Recent SendTo StartMenu MyMusic DesktopDirectory MyComputer Templates ApplicationData LocalApplicationData InternetCache Cookies History CommonApplicationData

Example 142. Folders supported by the [Environment]::GetFolderPath() method (continued)

System ProgramFiles MyPictures CommonProgramFiles

Since this is such a common task for all enumerated constants, though, PowerShell actually provides the possible values in the error message if it is unable to convert your input:

PS >[Environment]::GetFolderPath("aouaoue") Cannot convert argument "0", with value: "aouaoue", for "GetFolderPath" to type "System.Environment+SpecialFolder": "Cannot convert value "aouaoue" to type "System.Environment+SpecialFolder" due to invalid enumeration values. Specify one of the following enumeration values and try again. The possible enumeration values are "Desktop, Programs, Personal, MyDocuments, Favorites, Startup, Recent, SendTo, StartMenu, MyMusic, DesktopDirectory, MyComputer, Templates, ApplicationData, LocalApplicationData, InternetCache, Cookies, History, CommonApplicationData, System, ProgramFiles, MyPictures, CommonProgramFiles"." At line:1 char:29

+ [Environment]::GetFolderPath( "aouaoue")

Although this method provides access to the mostused common system paths, it does not provide access to all of them. For the paths that the [Environment]:: GetFolderPath() method does not support, use the WScript.Shell COM object. The WScript.Shell COM object supports the following paths: AllUsersDesktop, AllUsersStartMenu, AllUsersPrograms, AllUsersStartup, Desktop, Favorites, Fonts, MyDocuments, NetHood, PrintHood, Programs, Recent, SendTo, StartMenu, Startup, and Templates.

It would be nice if you could use either the [Environment]::GetFolderPath() method or the WScript.Shell COM object, but each of them supports a significant number of paths that the other does not, as Example 143 illustrates.

Example 143. Differences between folders supported by [Environment]::GetFolderPath() and the Wscript.Shell COM object

PS >$shell = NewObject Com WScript.Shell PS >$shellPaths = $shell.SpecialFolders | SortObject PS > PS >$netFolders = [Enum]::GetValues([Environment+SpecialFolder]) PS >$netPaths = $netFolders | >> ForeachObject { [Environment]::GetFolderPath($_) } | SortObject >> PS >## See the shellonly paths PS >CompareObject $shellPaths $netPaths | >> WhereObject { $_.SideIndicator eq "=" } >>

Example 143. Differences between folders supported by [Environment]::GetFolderPath() and the Wscript.Shell COM object (continued)

InputObject SideIndicator

C:\Documents and Settings\All Users\Desktop = C:\Documents and Settings\All Users\Start Menu = C:\Documents and Settings\All Users\Start Menu\Programs = C:\Documents and Settings\All Users\Start Menu\Programs\... = C:\Documents and Settings\Lee\NetHood = C:\Documents and Settings\Lee\PrintHood = C:\Windows\Fonts =

PS >## See the .NETonly paths PS >CompareObject $shellPaths $netPaths | >> WhereObject { $_.SideIndicator eq "=>" } >>

InputObject SideIndicator

=> C:\Documents and Settings\All Users\Application Data => C:\Documents and Settings\Lee\Cookies => C:\Documents and Settings\Lee\Local Settings\Application... => C:\Documents and Settings\Lee\Local Settings\History => C:\Documents and Settings\Lee\Local Settings\Temporary I... => C:\Program Files => C:\Program Files\Common Files => C:\WINDOWS\system32 => d:\lee => D:\Lee\My Music => D:\Lee\My Pictures =>

Access Event Logs of a Remote Machine in Windows PowerShell

Problem

You want to access event log entries from a remote machine.

Solution

To access event logs on a remote machine, create a new System.Diagnostics. EventLog class with the log name and computer name. Then access its Entries property:

PS >$log = NewObject Diagnostics.EventLog "System","LEEDESK" PS >$log.Entries | GroupObject Source

Count Name Group

91 Print {LEEDESK, LEEDESK, LEEDESK, LEEDESK... 640 TermServDevices {LEEDESK, LEEDESK, LEEDESK, LEEDESK... 148 W32Time {LEEDESK, LEEDESK, LEEDESK, LEEDESK... 100 WMPNetworkSvc {LEEDESK, LEEDESK, LEEDESK, LEEDESK... 856 Service Control Manager {LEEDESK, LEEDESK, LEEDESK, LEEDESK... 123 Tcpip {LEEDESK, LEEDESK, LEEDESK, LEEDESK...

(...)

Discussion

The solution demonstrates one way to get access to event logs on a remote machine. In addition to retrieving the event log entries, the System.Diagnostics.EventLog class also lets you perform other operations on remote computers, such as creating event logs, removing event logs, writing event log entries, and more.

The System.Diagnostics.EventLog class supports this through additional parameters to the methods that manage event logs. For example, to get the event logs from a remote machine:

[Diagnostics.EventLog]::GetEventLogs("LEEDESK")

To create an event log or event source on a remote machine:

$newLog =

NewObject Diagnostics.EventSourceCreationData "PowerShellCookbook","ScriptEvents" $newLog.MachineName = "LEEDESK" [Diagnostics.EventLog]::CreateEventSource($newLog)

To write entries to an event log on a remote machine:

$log = NewObject Diagnostics.EventLog "ScriptEvents","LEEDESK" $log.Source = "PowerShellCookbook" $log.WriteEntry("Test event from a remote machine.")

Work with Each Item in a List or Windows PowerShell Command Output

Problem

You have a list of items and want to work with each item in that list.

Solution

Use the ForeachObject cmdlet (which has the standard aliases foreach and %)to work with each item in a list.

To apply a calculation to each item in a list, use the $_ variable as part of a calculation in the scriptblock parameter:

PS >1..10 | ForeachObject { $_ * 2 } 2 4 6 8 10 12 14 16 18 20

To run a program on each file in a directory, use the $_ variable as a parameter to the program in the script block parameter:

GetChildItem *.txt | ForeachObject { attrib –r $_ }

To access a method or property for each object in a list, access that method or property on the $_ variable in the script block parameter. In this example, you get the list of running processes called notepad, and then wait for each of them to exit:

$notepadProcesses = GetProcess notepad $notepadProcesses | ForeachObject { $_.WaitForExit() }

Discussion

Like the WhereObject cmdlet, the ForeachObject cmdlet runs the script block that you specify for each item in the input. Ascript block is a series of PowerShell commands enclosed by the { and } characters. For each item in the set of incoming objects, PowerShell assigns that item to the $_ variable, one element at a time. In the examples given by the solution, the $_ variable represents each file or process that the previous cmdlet generated.

This script block can contain a great deal of functionality, if desired. You can combine multiple tests, comparisons, and much more.

The first example in the solution demonstrates a neat way to generate ranges of numbers:

1..10

This is PowerShell’s array range syntax.

The ForeachObject cmdlet isn’t the only way to perform actions on items in a list. The PowerShell scripting language supports several other keywords, such as for,(a different) foreach, do, and while.

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

Program: Invoke a Script Block with Alternate Culture Settings

Given PowerShell’s diverse user community, scripts that you share will often be run on a system set to a language other than English. To ensure that your script runs properly in other languages, it is helpful to give it a test run in that culture. Example 126 lets you run the script block you provide in a culture of your choosing.

Example 126. UseCulture.ps1

############################################################################## ## ## UseCulture.ps1 ## ## Invoke a scriptblock under the given culture ## ## ie: ## ## PS >UseCulture frFR { [DateTime]::Parse("25/12/2007") } ## ## mardi 25 décembre 2007 00:00:00## ## ##############################################################################

Example 126. UseCulture.ps1 (continued)

param( [System.Globalization.CultureInfo] $culture = $(throw "Please specify a culture"), [ScriptBlock] $script = $(throw "Please specify a scriptblock") )

## A helper function to set the current culture function SetCulture([System.Globalization.CultureInfo] $culture) {

[System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture }

## Remember the original culture information $oldCulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture

## Restore the original culture information if ## the user's script encounters errors. trap { SetCulture $oldCulture }

## Set the current culture to the user's provided ## culture. SetCulture $culture

## Invoke the user's scriptblock & $script

## Restore the original culture information. SetCulture $oldCulture

Program: Get Properties of Remote Registry Keys

Discussion

Although PowerShell does not directly let you access and manipulate the registry of a remote computer, it still supports this by working with the .NET Framework. The functionality exposed by the .NET Framework is a bit more developeroriented than we want, so we can instead use a script to make it easier to work with.

Example 187 lets you get the properties (or a specific property) from a given remote registry key. In order for this script to succeed, the target computer must have the remote registry service enabled and running.

Example 187. GetRemoteRegistryKeyProperty.ps1

############################################################################## ## ## GetRemoteRegistryKeyProperty.ps1 ## ## Get the value of a remote registry key property ## ## ie: ## ## PS >$registryPath = ## "HKLM:\software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" ## PS >GetRemoteRegistryKeyProperty LEEDESK $registryPath "ExecutionPolicy" ##

Example 187. GetRemoteRegistryKeyProperty.ps1 (continued)

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

param( $computer = $(throw "Please specify a computer name."), $path = $(throw "Please specify a registry path"), $property = "*" )

## Validate and extract out the registry key if($path match "^HKLM:\\(.*)") {

$baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(

"LocalMachine", $computer) } elseif($path match "^HKCU:\\(.*)") {

$baseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(

"CurrentUser", $computer) } else {

WriteError ("Please specify a fullyqualified registry path " + "(i.e.: HKLM:\Software) of the registry key to open.") return }

## Open the key $key = $baseKey.OpenSubKey($matches[1]) $returnObject = NewObject PsObject

## Go through each of the properties in the key foreach($keyProperty in $key.GetValueNames()) {

## If the property matches the search term, add it as a ## property to the output if($keyProperty like $property) {

$returnObject | AddMember NoteProperty $keyProperty $key.GetValue($keyProperty) } }

## Return the resulting object $returnObject

## Close the key and base keys $key.Close() $baseKey.Close()

Manage Alerts

Problem

You want to retrieve and manage alerts in the current monitoring object.

Solution

To retrieve alerts on the current monitoring object, use the GetAlert cmdlet. To retrieve only active alerts, apply a filter to include only those with a ResolutionState of 0.

>GetAlert | WhereObject { $_.ResolutionState eq 0 } | SelectObject Description

Description

The process started at 2:15:56 AM failed to create System.Discovery.Data, no error The process started at 2:05:23 PM failed to create System.Discovery.Data. Errors MSExchangeIS service is stopped. This may be caused by missing patch KB 915786. The computer Ibiza.contoso.com was not pingable. The computer Sydney.contoso.com was not pingable.

To resolve an alert, pipe it to the ResolveAlert cmdlet. For example, to clean up the alert entries in bulk:

GetAlert | WhereObject { $_.ResolutionState eq 0 } | ResolveAlert

Discussion

For more information about the GetAlert cmdlet, type GetHelp GetAlert. For more information about the ResolveAlert cmdlet, type GetHelp ResolveAlert.

Customize the Windows Shell to Improve Your Productivity

Problem

You want to use the PowerShell console more efficiently for copying, pasting, history management, and scrolling.

Solution

Shell console windows and make many tasks easier.

Example 18. SetConsoleProperties.ps1

PushLocation SetLocation HKCU:\Console NewItem '.\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe' SetLocation '.\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe'

NewItemProperty . ColorTable00 type DWORD value 0x00562401 NewItemProperty . ColorTable07 type DWORD value 0x00f0edee NewItemProperty . FaceName type STRING value "Lucida Console" NewItemProperty . FontFamily type DWORD value 0x00000036 NewItemProperty . FontSize type DWORD value 0x000c0000 NewItemProperty . FontWeight type DWORD value 0x00000190 NewItemProperty . HistoryNoDup type DWORD value 0x00000000 NewItemProperty . QuickEdit type DWORD value 0x00000001 NewItemProperty . ScreenBufferSize type DWORD value 0x0bb80078 NewItemProperty . WindowSize type DWORD value 0x00320078 PopLocation

These commands customize the console color, font, history storage properties, QuickEdit mode, buffer size, and window size.

With these changes in place, you can also improve your productivity by learning some of the hotkeys for common tasks, as listed in Table 11. PowerShell uses the same input facilities as cmd.exe, and so brings with it all the input features that you are already familiar with—and some that you aren’t!

Table 11. Partial list of Windows PowerShell hotkeys

Hotkey
Meaning

Up arrow
Scan backward through your command history.

Down arrow
Scan forward through your command history.

PgUp
Display the first command in your command history.

PgDown
Display the last command in your command history.

Left arrow
Move cursor one character to the left on your command line.

Right arrow
Move cursor one character to the right on your command line.

Home
Move the cursor to the beginning of the command line.

End
Move the cursor to the end of the command line.

Control + Left arrow
Move the cursor one word to the left on your command line.

Control + Right arrow
Move the cursor one word to the right on your command line.

Discussion

When you launch PowerShell from the link on your Windows Start menu, it customizes several aspects of the console window:

  • Foreground and background color, to make the console more visually appealing
  • QuickEdit mode, to make copying and pasting with the mouse easier
  • Buffer size, to make PowerShell retain the output of more commands in your console history

By default, these customizations do not apply when you run PowerShell from the Start ➝ Run dialog. The commands given in the solution section improve the experience by applying these changes to all PowerShell windows that you open.

The hotkeys do, however, apply to all PowerShell windows (and any other application that uses Windows’ cooked input mode). The most common are given in the in the solution section, but “Common Customization Points”.