Skip to main content

Windows

Display the Properties of an Item As a Table in Windows PowerShell

Problem

You have a set of items (for example, error records, directory items, or .NET objects), and you want to display summary information about them in a table format.

Solution

To display summary information about a set of items, pass those items to the FormatTable cmdlet. This is the default type of formatting for sets of items in PowerShell and provides several useful features.

To use PowerShell’s default formatting, pipe the output of a cmdlet (such as the GetProcess cmdlet) to the FormatTable cmdlet:

GetProcess | FormatTable

To display specific properties (such as Name and WorkingSet,) in the table formatting, supply those property names as parameters to the FormatTable cmdlet:

GetProcess | FormatTable Name,WS

To instruct PowerShell to format the table in the most readable manner, supply the –Auto flag to the FormatTable cmdlet. PowerShell defines “WS” as an alias of the WorkingSet for processes:

GetProcess | FormatTable Name,WS Auto

To define a custom column definition (such as a process’s Working Set in megabytes), supply a custom formatting expression to the FormatTable cmdlet:

$fields = "Name",@{Label = "WS (MB)"; Expression = {$_.WS / 1mb}; Align = "Right"} GetProcess | FormatTable $fields Auto

Discussion

The FormatTable cmdlet is one of the three PowerShell formatting cmdlets. These cmdlets include FormatTable, FormatList, and FormatWide. The FormatTable cmdlet takes input and displays information about that input as a table. By default, PowerShell takes the list of properties to display from the *.format.ps1xml files in PowerShell’s installation directory. You can display all properties of the items if you type FormatTable *, although this is rarely a useful view.

The Auto parameter to FormatTable is a helpful way to automatically format the table to use screen space as efficiently as possible. It does come at a cost, however. To figure out the best table layout, PowerShell needs to examine each item in the incoming set of items. For small sets of items, this doesn’t make much difference, but for large sets (such as a recursive directory listing) it does. Without the Auto parameter, the FormatTable cmdlet can display items as soon as it receives them. With the Auto flag, the cmdlet can only display results after it receives all the input.

Perhaps the most interesting feature of the FormatTable cmdlet is illustrated by the last example—the ability to define completely custom table columns. You define a custom table column similarly to the way that you define a custom column list. Rather than specify an existing property of the items, you provide a hashtable. That hashtable includes up to three keys: the column’s label, a formatting expression, and alignment. The FormatTable cmdlet shows the label as the column header and uses your expression to generate data for that column. The label must be a string, the expression must be a script block, and the alignment must be either "Left", "Center",or "Right". In the expression script block, the $_ variable represents the current item being formatted.

The expression shown in the last example takes the working set of the current item and divides it by 1 megabyte (1 MB).

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

Create a Hashtable or Associative Array

Problem

You have a collection of items that you want to access through a label that you provide.

Solution

To define a mapping between labels and items, use a hashtable (associative array):

PS >$myHashtable = @{} PS > PS >$myHashtable = @{ Key1 = "Value1"; "Key 2" = 1,2,3 } PS >$myHashtable["New Item"] = 5

PS >

PS >$myHashTable

Name
Value

Key 2
{1, 2, 3}

New Item
5

Key1
Value1

Discussion

Hashtables are much like arrays that allow you to access items by whatever label you want—not just through their index in the array. Because of that freedom, they form the keystone of a huge number of scripting techniques. Since they allow you to map names to values, they form the natural basis for lookup tables such as ZIP codes and area codes. Since they allow you to map names to fully featured objects and script blocks, they can often take the place of custom objects. Since you can map rich objects to other rich objects, they can even form the basis of more advanced data structures such as caches and object graphs.

This label and value mapping also proves helpful in interacting with cmdlets that support advanced configuration parameters, such as the calculated property parameters available on the FormatTable and SelectObject cmdlets.

Create a Registry Key Value

Problem

You want to add a new key value to an existing registry key.

Solution

To add a value to a registry key, use the NewItemProperty cmdlet. Example 182 adds MyProgram.exe to the list of programs that start when the current user logs in.

Example 182. Creating new properties on a registry key

PS >NewItemProperty . Name MyProgram Value c:\temp\MyProgram.exe

PSPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Softw are\Microsoft\Windows\CurrentVersion\Run PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Softw

are\Microsoft\Windows\CurrentVersion PSChildName : Run PSDrive : HKCU PSProvider : Microsoft.PowerShell.Core\Registry MyProgram : c:\temp\MyProgram.exe

PS >GetItemProperty .

PSPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_U SER\Software\Microsoft\Windows\CurrentVersion\Run PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_U

SER\Software\Microsoft\Windows\CurrentVersion PSChildName : Run PSDrive : HKCU PSProvider : Microsoft.PowerShell.Core\Registry FolderShare : "C:\Program Files\FolderShare\FolderShare.exe" /ba

ckground TaskSwitchXP : d:\lee\tools\TaskSwitchXP.exe

Example 182. Creating new properties on a registry key (continued)

ctfmon.exe
: C:\WINDOWS\system32\ctfmon.exe

Ditto
: C:\Program Files\Ditto\Ditto.exe

QuickTime Task
: "C:\Program Files\QuickTime Alternative\qttask.exe

" atboottime

H/PC Connection Agent : "C:\Program Files\Microsoft ActiveSync\wcescomm.ex

e" MyProgram : c:\temp\MyProgram.exe

Discussion

In the registry provider, PowerShell treats registry keys as items and key values as properties of those items. To create a key property, use the NewItemProperty cmdlet.

For more information about the NewItemProperty cmdlet, type GetHelp NewItemProperty. For more information about the registry provider, type GetHelp Registry.

Manage Distribution Groups

Problem

You want to get and modify information about distribution groups from the Exchange Management Shell.

Solution

To retrieve information about a distribution group, or distribution groups, use the GetDistributionGroup cmdlet:

$group = GetDistributionGroup "Stock Traders"

$group | FormatList *

To modify information about a group, use the SetDistributionGroup cmdlet. This example updates the Stock Traders distribution group to accept messages only from other members of the Stock Traders distribution group:

$group | SetDistributionGroup –AcceptMessagesOnlyFromDLMembers "Stock Traders" To add a user to a distribution group, use the AddDistributionGroupMember cmdlet:

$group | AddDistributionGroupMember –Member *preeda* To list members of a distribution group, use the GetDistributionGroupMember cmdlet:

$group | GetDistributionGroupMember

Discussion

For more information about the GetDistributionGroup cmdlet, type GetHelp GetDistributionGroup. For more information about the SetDistributionGroup cmdlet, type GetHelp SetDistributionGroup. For more information about the AddDistributionGroupMember cmdlet, type GetHelp AddDistributionGroupMember. For more information about the GetDistributionGroupMember cmdlet, type GetHelp GetDistributionGroupMember.

How to Customize Windows Shell, Profile, and Prompt

Problem

You want to customize PowerShell’s interactive experience with a personalized prompt, aliases, and more.

Solution

When you want to customize aspects of PowerShell, place those customizations in your personal profile script. PowerShell provides easy access to this profile script by storing its location in the $profile variable.

By default, PowerShell’s security policies prevent scripts (including your profile) from running. Once you begin writing scripts, though, you should configure this policy to something less restrictive.

To create a new profile (and overwrite one if it already exists):

NewItem type file force $profile

To edit your profile:

notepad $profile

To see your profile file:

GetChildItem $profile

Once you create a profile script, you can add a function called Prompt that returns a string. PowerShell displays the output of this function as your commandline prompt.

function Prompt

{

"PS [$env:COMPUTERNAME] >"

}

This example prompt displays your computer name, and look like: PS [LEEDESK]>

You may also find it helpful to add aliases to your profile. Aliases let you to refer to common commands by a name that you choose. Personal profile scripts let you automatically define aliases, functions, variables, or any other customizations that you might set interactively from the PowerShell prompt. Aliases are among the most common customizations, as they let you refer to PowerShell commands (and your own scripts) by a name that is easier to type.

If you want to define an alias for a command but also need to modify the parameters to that command, then define a function instead.

For example:

SetAlias new NewObject SetAlias iexplore 'C:\Program Files\Internet Explorer\iexplore.exe'

Your changes will become effective once you save your profile and restart PowerShell. To reload your profile immediately, run the command:

. $profile

Functions are also very common customizations, with the most popular of those being the Prompt function.

Discussion

Although the Prompt function returns a simple string, you can also use the function for more complex tasks. For example, many users update their console window title (by changing the $host.UI.RawUI.WindowTitle variable) or use the WriteHost cmdlet to output the prompt in color. If your prompt function handles the screen output itself, it still needs to return a string (for example, a single space) to prevent PowerShell from using its default. If you don’t want this extra space to appear in your prompt, add an extra space at the end of your WriteHost command and return the backspace ("`b") character.

Example 12. An example PowerShell prompt

function Prompt

{ $id = 1 $historyItem = GetHistory Count 1 if($historyItem) {

$id = $historyItem.Id + 1 }

WriteHost ForegroundColor DarkGray "`n[$(GetLocation)]" WriteHost NoNewLine "PS:$id > " $host.UI.RawUI.WindowTitle = "$(GetLocation)"

"`b" }

In addition to showing the current location, this prompt also shows the ID for that command in your history. This lets you locate and invoke past commands with relative ease:

[C:\] PS:73 >5 * 5 25

[C:\] PS:74 >1 + 1 2

[C:\] PS:75 >InvokeHistory 73 5 * 5

[C:\] PS:76 > Although the profile referenced by $profile is the one you will almost always want to use, PowerShell actually supports four separate profile scripts.

Return Data from a Script, Function, or Script Block in Windows PowerShell

Problem

You want your script or function to return data to whatever called it.

Solution

To return data from a script or function, write that data to the output pipeline:

## GetTomorrow.ps1 ## Get the date that represents tomorrow

function GetDate

{

GetDate

}

$tomorrow = (GetDate).AddDays(1) $tomorrow

Discussion

In PowerShell, any data that your function or script generates gets sent to the output pipeline, unless something captures that output. The GetDate function generates data (a date) and does not capture it, so that becomes the output of the function. The portion of the script that calls the GetDate function captures that output and then manipulates it.

Finally, the script writes the $tomorrow variable to the pipeline without capturing it, so that becomes the return value of the script itself.

Some .NET methods—such as the System.Collections.ArrayList class produce output, even though you may not expect them to. To prevent them from sending data to the output pipeline, either capture the data

or cast it to [void]: PS >$collection = NewObject System.Collections.ArrayList PS >$collection.Add("Hello") 0 PS >[void] $collection.Add("Hello")

Even with this “pipeline output becomes the return valuephilosophy, PowerShell continues to support the traditional return keyword as a way to return from a function or script. If you specify anything after the keyword (such as return "Hello"), PowerShell treats that as a "Hello" statement followed by a return statement.

If you want to make your intention clear to other readers of your script, you can use the WriteOutput cmdlet to explicitly send data down the pipeline. Both produce the same result, so this is only a mat

ter of preference.

If you write a collection (such as an array or ArrayList) to the output pipeline, PowerShell in fact writes each element of that collection to the pipeline. To keep the collection intact as it travels down the pipeline, prefix it with a comma when you return it. This returns a collection (that will be unraveled) with one element: the collection you wanted to keep intact.

function WritesObjects

{ $arrayList = NewObject System.Collections.ArrayList [void] $arrayList.Add("Hello") [void] $arrayList.Add("World")

$arrayList }

function WritesArrayList

{ $arrayList = NewObject System.Collections.ArrayList [void] $arrayList.Add("Hello") [void] $arrayList.Add("World")

,$arrayList }

$objectOutput = WritesObjects

# The following command would generate an error # $objectOutput.Add("Extra")

$arrayListOutput = WritesArrayList $arrayListOutput.Add("Extra")

Although relatively uncommon in PowerShell’s world of fully structured data, you may sometimes want to use an exit code to indicate the success or failure of your script. For this, PowerShell offers the exit keyword.

Determine the Current Location

Problem

You want to determine the current location from a script or command.

Solution

To retrieve the current location, use the GetLocation cmdlet. The GetLocation cmdlet provides the Drive and Path as two common properties:

$currentLocation = (GetLocation).Path

As a shortform for (GetLocation).Path, use the $pwd automatic variable.

Discussion

The GetLocation cmdlet returns information about the current location. From the information it returns, you can access the current drive, provider, and path.

This current location affects PowerShell commands and programs that you launch from PowerShell. It does not apply when you interact with the .NET Framework, however. If you need to call a .NET method that interacts with the filesystem, always be sure to provide fully qualified paths:

[System.Reflection.Assembly]::LoadFile("d:\documents\path_to_library.dll") If you are sure that the file exists, the ResolvePath cmdlet lets you translate a relative path to an absolute path:

$filePath = (ResolvePath library.dll).Path If the file does not exist, use the JoinPath cmdlet in combination with the GetLocation cmdlet to specify the file:

$filePath = JoinPath (GetLocation) library.dll

Another alternative that combines the functionality of both approaches is a bit more advanced but also lets you specify relative locations. It comes from methods in the PowerShell $executionContext variable, which provides functionality normally used by cmdlet and provider authors:

$executionContext.SessionState.Path.` GetUnresolvedProviderPathFromPSPath("..\library.dll")

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

Uninstall an Application from PowerShell

Problem

You want to uninstall a specific software application.

Solution

To uninstall an application, use the GetInstalledSoftware script to retrieve the command that uninstalls the software. Since the UninstallString uses batch file syntax, use cmd.exe to launch the uninstaller:

PS > $software = GetInstalledSoftware UnwantedProgram PS > cmd /c $software.UninstallString

Alternatively, use the Win32_Product WMI class for an unattended installation:

$application = GetWmiObject Win32_Product filter "Name='UnwantedProgram'" $application.Uninstall()

Discussion

The UninstallString provided by applications starts the interactive experience you would see if you were to uninstall the application through the Add/Remove Programs entry in the Control Panel. If you need to remove the software in an unattended manner, you have two options: use the “quiet mode” of the application’s uninstaller (for example, the /quiet switch to msiexec.exe), or use the software removal functionality of the Win32_Product WMI class as demonstrated in the solution.

Create a Temporary File in Windows PowerShell

Problem

You want to create a file for temporary purposes and want to be sure that the file does not already exist.

Solution

Use the [System.IO.Path]::GetTempFilename() method from the .NET Framework to create a temporary file:

$filename = [System.IO.Path]::GetTempFileName() (... use the file ...) RemoveItem Force $filename

Discussion

It is common to want to create a file for temporary purposes.

Often, people create this temporary file wherever they can think of: in C:\, the script’s current location, or any number of other places. Although this may work on the author’s system, it rarely works well elsewhere. For example, if the user does not use their Administrator account for daytoday tasks, your script will not have access to C:\ and will fail.

Another difficulty comes from trying to create a unique name for the temporary file. If your script just hardcodes a name (no matter how many random characters it has), it will fail if you run two copies at the same time. You might even craft a script smart enough to search for a filename that does not exist, create it, and then use it. Unfortunately, this could still break if another copy of your script creates that file after you see that it is missing—but before you actually create the file.

Finally, there are several security vulnerabilities that your script might introduce should it write its temporary files to a location that other users can read or write.

Luckily, the authors of the .NET Framework provided the [System.IO.Path]:: GetTempFilename() method to resolve these problems for you. It creates a unique filename in a reliable location in a secure manner. The method returns a filename, which you can then use as you want.

Remember to delete this file when your script no longer needs it; otherwise, your script will waste disk space and cause needless clutter on your users’ systems. Remember: your scripts should solve the adminis

trator’s problems, not cause them!

By default, the GetTempFilename() method returns a file with a .tmp extension. For most purposes, the file extension does not matter, and this works well. In the rare instances when you need to create a file with a specific extension, the [System.IO. Path]::ChangeExtension() method lets you change the extension of that temporary file. The following example creates a new temporary file that uses the .cs file extension:

$filename = [System.IO.Path]::GetTempFileName() $newname = [System.IO.Path]::ChangeExtension($filename, ".cs") MoveItem $filename $newname (... use the file ...) RemoveItem $newname

Manage PowerShell Security in an Enterprise

Problem

You want to control PowerShell’s security features in an enterprise setting.

Solution

To manage PowerShell’s security features enterprisewide:

  • Apply PowerShell’s Group Policy templates to control PowerShell’s execution policy through Group Policy.
  • Deploy Microsoft Certificate Services to automatically generate Authenticode codesigning certificates for domain accounts.
  • Apply software restriction policies to prevent PowerShell from trusting specific script publishers.

Discussion

Apply PowerShell’s Group Policy templates

The administrative templates for Windows PowerShell let you override the machine’s local execution policy preference at both the machine and peruser level. To obtain the PowerShell administrative templates, visit http://www.microsoft.com/ downloads and search for “Administrative templates for Windows PowerShell.”

Although Group Policy settings override local preferences, PowerShell’s execution policy should not be considered a security measure that protects the system from the user. It is a security measure that

helps prevent untrusted scripts from running on the system. As mentioned in the introduction, PowerShell is only a vehicle that allows users to do what they already have the Windows permissions to do.

Once you install the administrative templates for Windows PowerShell, launch the Group Policy Object Editor MMC snapin. Rightclick Administrative Templates and then select Add/Remove Administrative Templates. You will find the administrative template in the installation location you chose when you installed the administrative templates for Windows PowerShell. Once added, the Group Policy Editor MMC snapin provides PowerShell as option under its Administrative Templates node.

The default state is Not Configured. In this state, PowerShell takes its execution pol icy from the machine’s local preference. If you change the state to one of the Enabled options (or Disabled), PowerShell uses this configuration instead of the machine’s local preference.

PowerShell respects these Group Policy settings no matter what. This includes settings that the machine’s administrator may consider to reduce security—such as an Unrestricted group policy overriding an

AllSigned local preference.

Peruser Group Policy settings override the machine’s local preference, while permachine Group Policy settings override peruser settings.

Deploy Microsoft Certificate services

Although outside the scope of this book, Microsoft Certificate Services lets you automatically deploy codesigning certificates to any or all domain users. This provides a significant benefit, as it helps protect users from accidental or malicious script tampering.

For an introduction to this topic, visit http://technet.microsoft.com and search for “Enterprise Design for Certificate Services.”

Apply software restriction policies

While not common, you may sometimes want to prevent PowerShell from running scripts signed by specific publishers. If the script would normally be subject to signature verification (for example, it is a remote script, or PowerShell’s execution policy is set to AllSigned), PowerShell lets you configure this through certificate rules in the computer’s software restriction policies.

PowerShell does not support software restriction policy path rules.

To configure these certificate rules, launch the Local Security Policy MMC snapin listed in the Administrative Tools group of the Start menu. Expand the Software Restriction Policies node, rightclick Additional Rules, and then select New Certificate Rule.

Browse to the certificate that represents the publisher you want to block, and then click OK to block that publisher.

You can also create certificate policy that allows only certificates from a centrally administered whitelist. To do this, select either Allow only all administrators to manage Trusted Publishers or Allow only enterprise administrators to manage Trusted Publishers from the Trusted Publishers Management dialog.