Skip to main content

Windows

List the PowerShell Users in an Organizational Unit

Problem

You want to list all the users in an OU.

Solution

To list the users in an OU, use the [adsi] type shortcut to bind to the OU in Active Directory. Create a new System.DirectoryServices.DirectorySearcher for that OU, and then set its Filter property to (objectClass=User). Finally, call the searcher’s FindAll() method to perform the search.

$sales = [adsi] "LDAP://localhost:389/ou=Sales,dc=Fabrikam,dc=COM"

$searcher = NewObject System.DirectoryServices.DirectorySearcher $sales $searcher.Filter = '(objectClass=User)' $searcher.FindOne()

Discussion

The solution lists all users in the Sales OU. It does this through the System. DirectoryServices.DirectorySearcher class from the .NET Framework, which lets you query Active Directory. The Filter property specifies an LDAP filter string.

By default, a DirectorySearcher searches the given container and all containers below it. Set the SearchScope property to change this behavior. Avalue of Base searches only the current container, while a value

of OneLevel searches only the immediate children.

Calculations and Math in Windows PowerShell

Math is an important feature in any scripting language. Math support in a language includes addition, subtraction, multiplication, and division of course, but extends further into more advanced mathematical operations. So, it should not surprise you that PowerShell provides a strong suite of mathematical and calculationoriented features.

Since PowerShell provides full access to its scripting language from the command line, though, this keeps a powerful and useful commandline calculator always at your fingertips!

In addition to its support for traditional mathematical operations, PowerShell also caters to system administrators by working natively with concepts such as megabytes and gigabytes, simple statistics (such as sum and average), and conversions between bases.

Access Windows Performance Counters

Problem

You want to access system performance counter information from PowerShell.

Solution

To retrieve information about a specific performance counter, use the System. Diagnostics.PerformanceCounter class from the .NET Framework, as shown in Example 156.

Example 156. Accessing performance counter data through the System.Diagnostics. PeformanceCounter class

PS >$arguments = "System","System Up Time" PS >$counter = NewObject System.Diagnostics.PerformanceCounter $arguments PS > PS >[void] $counter.NextValue() PS >NewObject TimeSpan 0,0,0,$counter.NextValue()

Days
: 0

Hours
: 18

Minutes
: 51

Seconds
: 17

Milliseconds
: 0

Ticks
: 678770000000

TotalDays
: 0.785613425925926

TotalHours
: 18.8547222222222

TotalMinutes
: 1131.28333333333

TotalSeconds
: 67877

TotalMilliseconds : 67877000

Alternatively, WMI’s Win32_Perf* set of classes support many of the most common performance counters:

GetWmiObject Win32_PerfFormattedData_Tcpip_NetworkInterface

Discussion

The System.Diagnostics.PerformanceCounter class from the .NET Framework provides access to the different performance counters you might want to access on a Windows system. Example 156 illustrates working with a performance counter from a specific category. In addition, the constructor for the PerformanceCounter class also lets you specify instance names, and even a machine name for the performance counter you want to retrieve.

The first time you access a performance counter, the NextValue() method returns 0. At that point, the system begins to sample the performance information and returns a current value the next time you call the NextValue() method.

Get the Properties of an Organizational Unit from Windows PowerShell

Problem

You want to get and list the properties of a specific OU.

Solution

To list the properties of an OU, use the [adsi] type shortcut to bind to the OU in Active Directory, and then pass the OU to the FormatList cmdlet: $organizationalUnit = [adsi] "LDAP://localhost:389/ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$organizationalUnit | FormatList *

Discussion

The solution retrieves the Sales West OU. By default, the FormatList cmdlet shows only the distinguished name of the group, so we type FormatList * to display all properties.

If you know which property you want the value of, you can specify it by name:

PS >$organizationalUnit.wWWHomePage http://fabrikam.com/sales/west

Unlike OUs, some types of Active Directory objects don’t let you retrieve their properties by name this way. Instead, you must call the Get() method to retrieve specific properties:

PS >$organizationalUnit.Get("name") West

Add Custom Methods and Properties to Objects

Problem

You have an object and want to add your own custom properties or methods (members) to that object.

Solution

Use the AddMember cmdlet to add custom members to an object.

Discussion

The AddMember cmdlet is extremely useful in helping you add custom members to individual objects. For example, imagine that you want to create a report from the files in the current directory, and that report should include each file’s owner. The Owner property is not standard on the objects that GetChildItem produces, but you could write a small script to add them, as shown in

Example 34. A script that adds custom properties to its output of file objects

############################################################################## ## GetOwnerReport.ps1 ## ## Gets a list of files in the current directory, but with their owner added ## to the resulting objects. ## ## Example: ## GetOwnerReport ## GetOwnerReport | FormatTable Name,LastWriteTime,Owner ##############################################################################

$files = GetChildItem foreach($file in $files) {

$owner = (GetAcl $file).Owner

$file | AddMember NoteProperty Owner $owner

$file }

Although it is most common to add static information (such as a NoteProperty), the AddMember cmdlet supports several other property and method types—including AliasProperty, ScriptProperty, CodeProperty, CodeMethod, and ScriptMethod. For a more detailed description of these other property types, see “Working with the .NET Framework”, as well as the help documentation for the AddMember cmdlet.

Although the AddMember cmdlet lets you to customize specific objects, it does not let you to customize all objects of that type.

Calculated properties

Calculated properties are another useful way to add information to output objects. If your script or command uses a FormatTable or SelectObject command to generate its output, you can create additional properties by providing an expression that generates their value. For example:

GetChildItem | SelectObject Name, @{Name="Size (MB)"; Expression={ "{0,8:0.00}" f ($_.Length / 1MB) } }

In this command, we get the list of files in the directory. We use the SelectObject command to retrieve its name and a calculated property called Size (MB). This calculated property returns the size of the file in megabytes, rather than the default (which is bytes).

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

For more information about adding calculated properties, type GetHelp SelectObject or GetHelp FormatTable.

Program: Investigate the InvocationInfo Variable

When experimenting with the information available through the $myInvocation variable, it is helpful to see how this information changes between scripts, functions, and script blocks. For a useful deep dive into the resources provided by the $myInvocation variable, review the output of Example 141.

Example 141. GetInvocationInfo.ps1

############################################################################## ## ## GetInvocationInfo.ps1 ## ## Display the information provided by the $myInvocation variable ## ############################################################################## param([switch] $preventExpansion)

## Define a helper function, so that we can see how $myInvocation changes ## when it is called, and when it is dotsourced function HelperFunction {

" MyInvocation from function:" ""*50 $myInvocation

" Command from function:" ""*50 $myInvocation.MyCommand

}

## Define a script block, so that we can see how $myInvocation changes ## when it is called, and when it is dotsourced $myScriptBlock = {

" MyInvocation from script block:" ""*50 $myInvocation

" Command from script block:" ""*50 $myInvocation.MyCommand

}

## Define a helper alias SetAlias gii GetInvocationInfo

Example 141. GetInvocationInfo.ps1 (continued)

## Illustrate how $myInvocation.Line returns the entire line that the ## user typed. "You invoked this script by typing: " + $myInvocation.Line

## Show the information that $myInvocation returns from a script "MyInvocation from script:" ""*50 $myInvocation

"Command from script:" ""*50 $myInvocation.MyCommand

## If we were called with the PreventExpansion switch, don't go ## any further if($preventExpansion) {

return }

## Show the information that $myInvocation returns from a function "Calling HelperFunction" ""*50 HelperFunction

## Show the information that $myInvocation returns from a dotsourced ## function "DotSourcing HelperFunction" ""*50 . HelperFunction

## Show the information that $myInvocation returns from an aliased script "Calling aliased script" ""*50 gii PreventExpansion

## Show the information that $myInvocation returns from a script block "Calling script block" ""*50 & $myScriptBlock

## Show the information that $myInvocation returns from a dotsourced ## script block "DotSourcing script block" ""*50 . $myScriptBlock

## Show the information that $myInvocation returns from an aliased script "Calling aliased script" ""*50 gii –PreventExpansion

Back Up an Event Log

Problem

You want to store the information in an event log in a file for storage or later review.

Solution

To store event log entries in a file, use the GetEventLog cmdlet to retrieve the entries in the event log, and then pipe them to the ExportCliXml cmdlet to store them in a file.

GetEventLog System | ExportCliXml c:\temp\SystemLogBackup.clixml

Discussion

Once you’ve exported the events from an event log, you can archive them, or use the ImportCliXml cmdlet to review them on any machine that has PowerShell installed:

PS >$archivedLogs = ImportCliXml c:\temp\SystemLogBackup.clixml

PS >$archivedLogs | Group Source

Count Name
Group

856 Service Control Manager
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

640 TermServDevices
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

91 Print
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

100 WMPNetworkSvc
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

123 Tcpip
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

(...)

For more information about the GetEventLog cmdlet, type GetHelp GetEventLog. For more information about the ExportCliXml and ImportCliXml cmdlets, type GetHelp ExportCliXml and GetHelp ImportCliXml, respectively.

Filter Items in a List or Windows PowerShell Command Output

Problem

You want to filter the items in a list or command output.

Solution

Use the WhereObject cmdlet (which has the standard aliases, where and ?) to select items in a list (or command output) that match a condition you provide.

To list all running processes that have "search" in their name, use the like operator to compare against the process’s Name property:

GetProcess | WhereObject { $_.Name like "*Search*" }

To list all directories in the current location, test the PsIsContainer property:

GetChildItem | WhereObject { $_.PsIsContainer }

To list all stopped services, use the eq operator to compare against the service’s Status property:

GetService | WhereObject { $_.Status eq "Stopped" }

Discussion

For each item in its input (which is the output of the previous command), the WhereObject cmdlet evaluates that input against the script block that you specify. If the script block returns True, then the WhereObject cmdlet passes the object along. Otherwise, it does not. Ascript block is a series of PowerShell commands enclosed by the { and } characters. You can write any PowerShell commands inside the script block. In the script block, the $_ variable represents the current input object. For each item in the incoming set of objects, PowerShell assigns that item to the $_ variable, and then runs your script block. In the preceding examples, this incoming object represents the process, file, or service that the previous cmdlet generated.

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

For simple filtering, the syntax of the WhereObject cmdlet may sometimes seem overbearing.

For complex filtering (for example, the type you would normally rely on a mouse to do with files in an Explorer window), writing the script block to express your intent may be difficult or even infeasible.

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

Display Messages and Output to the User in PowerShell

Problem

You want to display messages and other information to the user.

Solution

To ensure that the output actually reaches the screen, call the WriteHost (or OutHost) cmdlet:

PS >function GetDirectorySize >> { >> $size = (GetChildItem | MeasureObject Sum Length).Sum >> WriteHost ("Directory size: {0:N0} bytes" f $size) >> } >> PS >GetDirectorySize Directory size: 46,581 bytes PS >$size = GetDirectorySize Directory size: 46,581 bytes

If you want a message to help you (or the user) diagnose and debug your script, use the WriteDebug cmdlet. If you want a message to provide detailed tracetype output, use the WriteVerbose cmdlet, as shown in Example 122.

Example 122. A function that provides debug and verbose output

PS >function GetDirectorySize >> { >> WriteDebug "Current Directory: $(GetLocation)" >> >> WriteVerbose "Getting size" >> $size = (GetChildItem | MeasureObject Sum Length).Sum >> WriteVerbose "Got size: $size" >> >> WriteHost ("Directory size: {0:N0} bytes" f $size) >> } >> PS >$DebugPreference = "Continue" PS >GetDirectorySize DEBUG: Current Directory: D:\lee\OReilly\Scripts\Programs Directory size: 46,581 bytes PS >$DebugPreference = "SilentlyContinue" PS >$VerbosePreference = "Continue" PS >GetDirectorySize VERBOSE: Getting size VERBOSE: Got size: 46581 Directory size: 46,581 bytes PS >$VerbosePreference = "SilentlyContinue"

Discussion

Most scripts that you write will output richly structured data, such as the actual count of bytes in a directory. That way, other scripts can use the output of that script as a building block for their functionality.

When you do want to provide output specifically to the user, use the WriteHost, WriteDebug, and WriteVerbose cmdlets.

However, be aware that this type of output bypasses normal file redirection, and is therefore difficult for the user to capture. In the case of the WriteHost cmdlet, use it only when your script already generates other structured data that the user would want to capture in a file or variable.

Most script authors eventually run into the problem illustrated by Example 123 when their script tries to output formatted data to the user.

Example 123. An error message caused by formatting statements

PS >## Get the list of items in a directory, sorted by length PS >function GetChildItemSortedByLength($path = (GetLocation)) >> { >> GetChildItem $path | FormatTable | Sort Length >> }

Example 123. An error message caused by formatting statements (continued)

>> PS >GetChildItemSortedByLength outlineoutput : Object of type "Microsoft.PowerShell.Commands.Internal.Fo rmat.FormatEntryData" is not legal or not in the correct sequence. This is likely caused by a userspecified "format*" command which is conflicting with the default formatting.

This happens because the Format* cmdlets actually generate formatting information for the OutHost cmdlet to consume. The OutHost cmdlet (which PowerShell adds automatically to the end of your pipelines) then uses this information to generate formatted output. To resolve this problem, always ensure that formatting commands are the last commands in your pipeline, as shown in Example 124.

Example 124. A function that does not generate formatting errors

PS >## Get the list of items in a directory, sorted by length PS >function GetChildItemSortedByLength($path = (GetLocation)) >> { >> ## Problematic version >> ## GetChildItem $path | FormatTable | Sort Length >> >> ## Fixed version >> GetChildItem $path | Sort Length | FormatTable >> } >> PS >GetChildItemSortedByLength

(...)

Mode
LastWriteTime
Length Name

a
3/11/2007
3:21 PM
59 LibraryProperties.ps1

a
3/6/2007
10:27 AM
150 GetTomorrow.ps1

a
3/4/2007
3:10 PM
194 ConvertFromFahrenheitWithout

Function.ps1

a
3/4/2007
4:40 PM
257 LibraryTemperature.ps1

a
3/4/2007
4:57 PM
281 ConvertFromFahrenheitWithLib

rary.ps1

a
3/4/2007
3:14 PM
337 ConvertFromFahrenheitWithFunc

tion.ps1

(...)

When it comes to producing output for the user, a common reason is to provide progress messages. PowerShell actually supports this in a much richer way, through its WriteProgress cmdlet. 

Set the ACL of a Registry Key

Problem

You want to change the ACL of a registry key.

Solution

To set the ACL on a registry key, use the SetAcl cmdlet. This example grants an account write access to a registry key under HKLM:\Software. This is especially useful for programs that write to administratoronly regions of the registry, which prevents them from running under a nonadministrator account.

cd HKLM:\Software\MyProgram $acl = GetAcl . $arguments = "LEEDESK\Lee","FullControl","Allow" $accessRule = NewObject System.Security.AccessControl.RegistryAccessRule $arguments $acl.SetAccessRule($accessRule) $acl | SetAcl .

Discussion

The SetAcl cmdlet sets the security descriptor of an item. This cmdlet doesn’t only work against the registry, however. Any provider (for example, the filesystem provider) that supports the concept of security descriptors also supports the SetAcl cmdlet.

The SetAcl cmdlet requires that you provide it with an ACL to apply to the item. While it is possible to construct the ACL from scratch, it is usually easiest to retrieve it from the item beforehand (as demonstrated in the solution). To retrieve the ACL, use the GetAcl cmdlet. Once you’ve modified the access control rules on the ACL, simply pipe them to the SetAcl cmdlet to make them permanent.

In the solution, the $arguments list that we provide to the RegistryAccessRule constructor explicitly sets an Allow rule on the Lee account of the LEEDESK computer for FullControl permission.

Although the SetAcl command is powerful, you may already be familiar with commandline tools that offer similar functionality (such as SubInAcl.exe). You can of course continue to use these tools from PowerShell.

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