Skip to main content

Windows

Program: Simplify Most Where-Object Filters in Windows PowerShell

The WhereObject cmdlet is incredibly powerful, in that it allows you to filter your output based on arbitrary criteria. For extremely simple filters (such as filtering based only on a comparison to a single property), though, the syntax can get a little ungainly:

GetProcess | WhereObject { $_.Handles gt 1000 }

load all the syntax to the script itself:

GetProcess | CompareProperty Handles gt 1000 GetChildItem | CompareProperty PsIsContainer

With a shorter alias, this becomes even easier to type:

PS >gt;SetAlias wheres CompareProperty PS >GetChildItem | wheres Length gt 100

Example 23 implements this “simple where” functionality. Note that supplying a nonexisting operator as the $operator parameter will generate an error message.

Example 23. CompareProperty.ps1

############################################################################## ## CompareProperty.ps1 ## ## Compare the property you provide against the input supplied to the script. ## This provides the functionality of simple WhereObject comparisons without ## the syntax required for that cmdlet. ## ## Example: ## GetProcess | CompareProperty Handles gt 1000 ## dir | CompareProperty PsIsContainer ############################################################################## param($property, $operator = "eq", $matchText = "$true")

Begin { $expression = "`$_.$property $operator `"$matchText`"" } Process { if(InvokeExpression $expression) { $_ } }

Provide Progress Updates on Long-Running Tasks in PowerShell

Problem

You want to display status information to the user for longrunning tasks.

Solution

To provide status updates, use the WriteProgress cmdlet as shown in Example 125.

Example 125. Using the WriteProgress cmdlet to display status updates

$activity = "A longrunning operation"

$status = "Initializing" ## Initialize the longrunning operation for($counter = 0; $counter lt 100; $counter++) {

$currentOperation = "Initializing item $counter" WriteProgress $activity $status PercentComplete $counter ` CurrentOperation $currentOperation StartSleep m 20 }

$status = "Running" ## Initialize the longrunning operation for($counter = 0; $counter lt 100; $counter++) {

$currentOperation = "Running task $counter" WriteProgress $activity $status PercentComplete $counter ` CurrentOperation $currentOperation StartSleep m 20 }

Work with the Registry of a Remote Computer

Problem

You want to work with the registry keys and values of a remote computer.

Solution

To work with the registry of a remote computer, use the scripts provided in this chapter: GetRemoteRegistryChildItem, GetRemoteRegistryProperty, and SetRemoteRegistryProperty. These scripts require that the remote computer has the remote registry service enabled and running. Example 185 updates the PowerShell execution policy of a remote machine.

Example 185. Setting the PowerShell execution policy of a remote machine

PS >$registryPath = "HKLM:\Software\Microsoft\PowerShell\1" PS >GetRemoteRegistryChildItem LEEDESK $registryPath

SKC VC Name Property

0 1 1033 {Install} 0 5 PowerShellEngine {ApplicationBase, ConsoleHostAss... 2 0 PowerShellSnapIns {} 1 0 ShellIds {}

PS >GetRemoteRegistryChildItem LEEDESK $registryPath\ShellIds

SKC VC Name Property

0 2 Microsoft.PowerShell {Path, ExecutionPolicy}

PS > PS >$registryPath = "HKLM:\Software\Microsoft\PowerShell\1\" + >> "ShellIds\Microsoft.PowerShell" >>

Example 185. Setting the PowerShell execution policy of a remote machine (continued)

PS >GetRemoteRegistryKeyProperty LEEDESK $registryPath ExecutionPolicy

ExecutionPolicy

Unrestricted

PS >SetRemoteRegistryKeyProperty LEEDESK $registryPath ` >> "ExecutionPolicy" "RemoteSigned" >> PS >GetRemoteRegistryKeyProperty LEEDESK $registryPath ExecutionPolicy

ExecutionPolicy

RemoteSigned

Discussion

Although this specific task is perhaps better solved through PowerShell’s Group Policy support, it demonstrates a useful scenario that includes both remote registry exploration and modification.

For more information about the GetRemoteRegistryChildItem, GetRemoteRegistryProperty, and SetRemoteRegistryProperty scripts.

Enable or Disable Rules

Problem

You want to enable or disable rules not sealed in a management pack.

Solution

To retrieve a rule, use the GetRule: $rule = GetRule | WhereObject { $_.DisplayName –match "RuleName" }

Then, use the EnableRule or DisableRule cmdlet to enable or disable that rule, respectively:

$rule | EnableRule

$rule | DisableRule

Discussion

For more information about the GetRule cmdlet, type GetHelp GetRule. For more information about the DisableRule cmdlet, type GetHelp DisableRule. For more information about the EnableRule cmdlet, type GetHelp EnableRule.

Determine the Status of the Last Windows PowerShell Command

Problem

You want to get status information about the last command you executed, such as whether it succeeded.

Solution

Use one of the two variables PowerShell provides to determine the status of the last command you executed: the $lastExitCode variable and the $? variable.

$lastExitCode

Anumber that represents the exit code/error level of the last script or application that exited

$? (pronounced “dollar hook”)

A Boolean value that represents the success or failure of the last command

Discussion

The $lastExitCode PowerShell variable is similar to the %errorlevel% variable in DOS. It holds the exit code of the last application to exit. This lets you continue to interact with traditional executables (such as ping, findstr, and choice) that use exit codes as a primary communication mechanism. PowerShell also extends the meaning of this variable to include the exit codes of scripts, which can set their status using the exit.

Example 17. Interacting with the $lastExitCode and $? variables

PS >ping localhost

Pinging MyComputer [127.0.0.1] with 32 bytes of data:

Reply from 127.0.0.1: bytes=32 time1ms TTL=128 Reply from 127.0.0.1: bytes=32 time1ms TTL=128 Reply from 127.0.0.1: bytes=32 time1ms TTL=128 Reply from 127.0.0.1: bytes=32 time1ms TTL=128

Ping statistics for 127.0.0.1:

Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milliseconds:

Minimum = 0ms, Maximum = 0ms, Average = 0ms PS >$? True PS >$lastExitCode

Example 17. Interacting with the $lastExitCode and $? variables (continued)

0 PS >ping missinghost Ping request could not find host missinghost. Please check the name and try again. PS >$? False PS >$lastExitCode 1

The $? variable describes the exit status of the last application in a more general manner. PowerShell sets this variable to False on error conditions such as when:

  • An application exits with a nonzero exit code.
  • A cmdlet or script writes anything to its error stream.
  • A cmdlet or script encounters a terminating error or exception.

For commands that do not indicate an error condition, PowerShell sets the $? variable to True.

Create a Jagged or Multidimensional Array

Problem

You want to create an array of arrays, or an array of multiple dimensions.

Solution

To create a jagged multidimensional array (an array of arrays), use the @( ) array syntax:

PS >$jagged = @(

>> (1,2,3,4),

>> (5,6,7,8)

>> )

>>

PS >$jagged[0][1]

>>2

PS >$jagged[1][3]

>>8

To create a (nonjagged) multidimensional array, use the NewObject cmdlet:

PS >$multidimensional = NewObject "int32[,]" 2,4 PS >$multidimensional[0,1] = 2 PS >$multidimensional[1,3] = 8 PS > PS >$multidimensional[0,1] >>2 PS >$multidimensional[1,3] >>8

Discussion

Jagged and multidimensional arrays are useful for holding lists of lists/arrays of arrays. Jagged arrays are much easier to work with (and use less memory), while nonjagged multidimensional arrays are sometimes useful for dealing with large grids of data.

Since a jagged array is an array of arrays, creating an item in a jagged array follows the same rules as creating an item in a regular array. If any of the arrays are singleelement arrays, use the unary comma operator. For example, to create a jagged array with one nested array of one element:

PS >$oneByOneJagged = @( >> ,(,1) >> PS >$oneByOneJagged[0][0]

Get the ACL of a File or Directory in PowerShell

Problem

You want to retrieve the ACL of a file or directory.

Solution

To retrieve the ACL of a file, use the GetAcl cmdlet: PS >GetAcl example.txt

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

Path
Owner
Access

example.txt
LEEDESK\Lee
BUILTIN\Administrator...

Discussion

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

The GetAcl cmdlet returns an object that represents the security descriptor of the item and is specific to the provider that contains the item. In the filesystem, this returns a .NET System.Security.AccessControl.FileSecurity object that you can explore for further information. For example, Example 174 searches a directory for possible ACL misconfigurations by ensuring that each file contains an Administrators, Full Control ACL.

Example 174. GetAclMisconfiguration.ps1

############################################################################## ## ## GetAclMisconfiguration.ps1 ## ## Demonstration of functionality exposed by the GetAcl cmdlet. This script ## goes through all access rules in all files in the current directory, and ## ensures that the Administrator group has full control of that file. ## ##############################################################################

## Get all files in the current directory foreach($file in GetChildItem) {

## Retrieve the ACL from the current file $acl = GetAcl $file if(not $acl) {

continue }

$foundAdministratorAcl = $false

## Go through each access rule in that ACL foreach($accessRule in $acl.Access) {

## If we find the Administrator, Full Control access rule, ## then set the $foundAdministratorAcl variable if(($accessRule.IdentityReference like "*Administrator*") and

($accessRule.FileSystemRights eq "FullControl")) { $foundAdministratorAcl = $true } }

## If we didn't find the administrator ACL, output a message if(not $foundAdministratorAcl)

Example 174. GetAclMisconfiguration.ps1 (continued)

{ "Found possible ACL Misconfiguration: $file" } }

Renew a DHCP Lease

Problem

You want to renew the DHCP lease for a connection on a computer.

Solution

To renew DHCP leases, use the ipconfig application. To renew the lease on all connections:

PS >ipconfig /renew

To renew the lease on a specific connection:

PS >ipconfig /renew "Wireless Network Connection 4"

Discussion

The standard ipconfig application works well to manage network configuration options on a local machine. To renew the lease on a remote computer, you have two options.

Use the Win32_NetworkAdapterConfiguration WMI class

To renew the lease on a remote computer, use the Win32_ NetworkAdapterConfiguration WMI class. The WMI class requires that you know the description of the network adapter, so first obtain that by reviewing the output of GetWmiObject Win32_NetworkAdapterConfiguration –Computer :

PS >GetWmiObject Win32_NetworkAdapterConfiguration –Computer LEEDESK

(...) 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 (...) Index : 13 (...)

Knowing which adapter you want to renew, call its RenewDHCPLease() method:

$description = "Linksys WirelessG USB"

$adapter = GetWmiObject Win32_NetworkAdapterConfiguration –Computer LEEDESK |

WhereObject { $_.Description –match $description}

$adapter.RenewDHCPLease()

Run ipconfig on the remote computer

PS >InvokeRemoteExpression \\LEEDESK { ipconfig /renew }

Use Excel to Manage Command Output

Problem

You want to use Excel to manipulate or visualize the output of a command.

Solution

Use PowerShell’s ExportCsv cmdlet to save the output of a command in a CSV file, and then load that CSV in Excel. If you have Excel associated with .CSV files, the InvokeItem cmdlet launches Excel when you provide it with a .CSV file as an argument.

Example 87 demonstrates how to generate a CSV containing the disk usage for sub directories of the current directory.

Example 87. Using Excel to visualize disk usage on the system

PS >$filename = "c:\temp\diskusage.csv" PS > PS >$output = GetChildItem | WhereObject { $_.PsIsContainer } | >> SelectObject Name, >> @{ Name="Size"; >> Expression={ ($_ | GetChildItem Recurse | >> MeasureObject Sum Length).Sum + 0 } } >>

Example 87. Using Excel to visualize disk usage on the system (continued)

PS >$output | ExportCsv $filename PS > PS >InvokeItem $filename

Discussion

Although used only as a demonstration, Example 87 packs quite a bit into just a few lines.

The first GetChildItem line gets a list of all the files in the current directory and uses the WhereObject cmdlet to restrict those to directories. For each of those directories, you use the SelectObject cmdlet to pick out the Name and Size of that directory.

Directories don’t have a Size property though. To get that, we use SelectObject’s hashtable syntax to generate a calculated property. This calculated property (as defined by the Expression script block) uses the GetChildItem and MeasureObject cmdlets to add up the Length of all files in the given directory.

Program: Search the Certificate Store

Discussion

One useful feature of the certificate provider is that it provides a –CodeSign parameter that lets you search for certificates in the certificate store that support code signing. Code signing certificates are not the only kind of certificates, however; other frequently used certificate types are Encrypting File System, Client Authentication, and more.

Example 166 lets you search the certificate provider for certificates that support a given Enhanced Key Usage (EKU).

Example 166. SearchCertificateStore.ps1

############################################################################## ## ## SearchCertificateStore.ps1 ## ## Search the certificate provider for certificates that match the specified ## Enhanced Key Usage (EKU.) ## ## ie: ## ## PS >SearchCertificateStore "Encrypting File System" ## ##############################################################################

param( $ekuName = $(throw "Please specify the friendly name of an " + "Enhanced Key Usage (such as 'Code Signing'") )

Example 166. SearchCertificateStore.ps1 (continued)

## Go through every certificate in the current user's "My" store foreach($cert in GetChildItem cert:\CurrentUser\My) {

## For each of those, go through its extensions foreach($extension in $cert.Extensions) {

## For each extension, go through its Enhanced Key Usages foreach($certEku in $extension.EnhancedKeyUsages) {

## If the friendly name matches, output that certificate if($certEku.FriendlyName eq $ekuName) {

$cert } } } }