Skip to main content

Windows

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”.