Skip to main content

Resources

Create an Organizational Unit in Windows PowerShell

Problem

You want to create an organizational unit (OU) in Active Directory.

Solution

To create an organizational unit in a container, use the [adsi] type shortcut to bind to a part of the Active Directory, and then call the Create() method.

$domain = [adsi] "LDAP://localhost:389/dc=Fabrikam,dc=COM" $salesOrg = $domain.Create("OrganizationalUnit", "OU=Sales") $salesOrg.Put("Description", "Sales Headquarters, SF") $salesOrg.Put("wwwHomePage", "http://fabrikam.com/sales") $salesOrg.SetInfo()

Discussion

The solution shows an example of creating a Sales organizational unit (OU) at the root of the organization. You can use the same syntax to create OUs under other OUs as well. Example 231 demonstrates how to create an East and West sales division.

Example 231. Creating East and West sales divisions

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

$east = $sales.Create("OrganizationalUnit", "OU=East") $east.Put("wwwHomePage", "http://fabrikam.com/sales/east") $east.SetInfo()

$west = $sales.Create("OrganizationalUnit", "OU=West") $west.Put("wwwHomePage", "http://fabrikam.com/sales/west") $west.SetInfo()

Get Detailed Documentation About Types and Objects in .NET Framework

Problem

You have a type of object and want to know detailed information about the methods and properties it supports.

Solution

The documentation for the .NET Framework (available on http://msdn.microsoft. com ) is the best way to get detailed documentation about the methods and properties supported by an object. That exploration generally comes in two stages:

1. Find the type of the object.

To determine the type of an object, you can use either the type name shown by the GetMember cmdlet, or call the GetType() method of an object (if you have an instance of it):

PS >$date = GetDate PS >$date.GetType().ToString() System.DateTime

2. Enter that type name into the search box at http://msdn.microsoft.com .

Discussion

When the GetMember cmdlet does not provide the information you need, the MSDN documentation for a type is a great alternative. It provides much more detailed information than the help offered by the GetMember cmdlet—usually including detailed descriptions, related information, and even code samples. MSDN documentation focuses on developers using these types through a language such as C#, though, so you may find interpreting the information for use in PowerShell to be a little difficult at first.

Typically, the documentation for a class first starts with a general overview, and then provides a hyperlink to the members of the class—the list of methods and properties it supports.

To get to the documentation for the members quickly, search for them more explicitly by adding the term “members” to your MSDN search term:

typename members

Documentation for the members of a class lists its methods and properties, as does the output of the GetMember cmdlet. The S icon represents static methods and properties. Click the member name for more information about that method or property.

Public constructors

This section lists the constructors of the type. You use a constructor when you create the type through the NewObject cmdlet. When you click on a constructor, the documentation provides all the different ways that you can create that object, including the parameter list that you will use with the NewObject cmdlet.

Public fields/public properties

This section lists the names of the fields and properties of an object. The S icon represents a static field or property. When you click on a field or property, the documentation also provides the type returned by this field or property.

For example, you might see the following in the definition for System.DateTime.Now:

C#

public static DateTime Now { get; } Public means that the Now property is public—that everybody can access it. Static means that the property is static. DateTime means that the property returns a DateTime object when you call it. Get; means that you can get information from this property but cannot set the information. Many properties support a Set; as well (such as the IsReadOnly property on System.IO.FileInfo), which means that you can change its value.

Public methods

This section lists the names of the methods of an object. The S icon represents a static method. When you click on a method, the documentation provides all the different ways that you can call that method, including the parameter list that you will use to call that method in PowerShell.

For example, you might see the following in the definition for System.DateTime. AddDays():

C# public DateTime AddDays ( double value ) Public means that the AddDays method is public—that everybody can access it. DateTime means that the method returns a DateTime object when you call it. The text, double value, means that this method requires a parameter (of type double). In this case, that parameter determines the number of days to add to the DateTime object on which you call the method.

Access Information About Your Command’s Invocation

Problem

You want to learn about how the user invoked your script, function, or script block.

Solution

To access information about how the user invoked your command, use the $myInvocation variable:

"You invoked this script by typing: " + $myInvocation.Line

Discussion

The $myInvocation variable provides a great deal of information about the current script, function, or script block—and the context in which it was invoked:

MyCommand

Information about the command (script, function, or script block) itself.

ScriptLineNumber

The line number in the script that called this command.

ScriptName

When in a function or script block, the name of the script that called this command.

Line

The verbatim text used in the line of script (or command line) that called this command.

InvocationName

The name that the user supplied to invoke this command. This will be different from the information given by MyCommand if the user has defined an alias for the command.

PipelineLength

The number of commands in the pipeline that invoked this command.

PipelinePosition

The position of this command in the pipeline that invoked this command.

One important point about working with the $myInvocation variable is that it changes depending on the type of command from which you call it. If you access this information from a function, it provides information specific to that function—not the script from which it was called. Since scripts, functions, and script blocks are fairly unique, information in the $myInvocation.MyCommand variable changes slightly between the different command types.

Scripts

Definition and Path

The full path to the currently running script

Name

The name of the currently running script

CommandType Always ExternalScript

Functions

Definition and ScriptBlock

The source code of the currently running function

Options The options (None, ReadOnly, Constant, Private, AllScope) that apply to the currently running function

Name

The name of the currently running function

CommandType Always Function

Script blocks

Definition and ScriptBlock

The source code of the currently running script block

Name

Empty

CommandType Always Script

Find Event Log Entries by Their Frequency

Problem

You want to find the event log entries that occur most frequently.

Solution

To find event log entries by frequency, use the GetEventLog cmdlet to retrieve the entries in the event log, and then pipe them to the GroupObject cmdlet to group them by their message.

PS >GetEventLog System | GroupObject Message

Count Name Group

23 The Background Intelli... {LEEDESK, LEEDESK, LEEDESK, LEEDESK... 23 The Background Intelli... {LEEDESK, LEEDESK, LEEDESK, LEEDESK...

3 The Logical Disk Manag... {LEEDESK, LEEDESK, LEEDESK}

3 The Logical Disk Manag... {LEEDESK, LEEDESK, LEEDESK}

3 The Logical Disk Manag... {LEEDESK, LEEDESK, LEEDESK} 161 Driver Microsoft XPS D... {LEEDESK, LEEDESK, LEEDESK, LEEDESK... (...)

Discussion

The GroupObject cmdlet is a useful way to determine which events occur most frequently on your system. It also provides a useful way to summarize the information in the event log.

If you want to learn more information about the items in a specific group, use the WhereObject cmdlet. Since we used the Message property in the GroupObject cmdlet, we need to filter on Message in the WhereObject cmdlet. For example, to learn more about the entries relating to the Microsoft XPS Driver (from the scenario in the solution):

PS >GetEventLog System | >> WhereObject { $_.Message like "Driver Microsoft XPS*" } >>

Index Time
Type Source
EventID Message

2917 May 06 09:13
Erro TermServDevices
1111 Driver Microsoft...

2883 May 05 10:40
Erro TermServDevices
1111 Driver Microsoft...

2877 May 05 08:10
Erro TermServDevices
1111 Driver Microsoft...

(...)

If grouping by message doesn’t provide useful information, you can group by any other property—such as source:

PS >GetEventLog Application | GroupObject Source

Count Name
Group

4 Application
{LEEDESK, LEEDESK, LEEDESK, LEEDESK}

191 Media Center Scheduler
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

1082 MSSQL$SQLEXPRESS
{LEEDESK, LEEDESK, LEEDESK, LEEDESK...

(...)

If you’ve listed the items in an event log or searched it for entries that have a message with specific text, you often want to get more details about a specific event log entry.

By default, PowerShell’s default table formatting displays a summary of event log entries. If you are retrieving a specific entry, however, you are probably interested in seeing more details about the entry. In this case, use the FormatList cmdlet to format these entries in a more detailed list view, as shown in Example 205.

Example 205. A detailed list view of an event log entry

PS >GetEventLog System | WhereObject { $_.Index –eq 2917 } | >> FormatList >>

Index : 2917 EntryType : Error EventID : 1111 Message : Driver Microsoft XPS Document Writer required for pri

nter Microsoft XPS Document Writer is unknown. Contac t the administrator to install the driver before you log in again.

Category : (0) CategoryNumber : 0 ReplacementStrings : {Microsoft XPS Document Writer, Microsoft XPS Documen

t Writer} Source : TermServDevices TimeGenerated : 5/6/2007 9:13:31 AM TimeWritten : 5/6/2007 9:13:31 AM UserName :

For more information about the GetEventLog cmdlet, type GetHelp GetEventLog. For more information about the GroupObject cmdlet, type GetHelp GroupObject.

Pipelines in Windows PowerShell

One of the fundamental concepts in a shell is called the pipeline. It also forms the basis of one of the most significant advances that PowerShell brings to the table. A pipeline is a big name for a simple concept—a series of commands where the output of one becomes the input of the next. Apipeline in a shell is much like an assembly line in a factory: it successively refines something as it passes between the stages.

Example 21. A PowerShell pipeline

GetProcess | WhereObject { $_.WorkingSet gt 500kb } | SortObject Descending Name

In PowerShell, you separate each stage in the pipeline with the pipe (|) character.

GetProcess cmdlet generates objects that represent actual processes on the system. These process objects contain information about the process’s name, memory usage, process id, and more. The WhereObject cmdlet, then, gets to work directly with those processes, testing easily for those that use more than 500 kb of memory. It passes those along, allowing the SortObject cmdlet to also work directly with those processes, sorting them by name in descending order. This brief example illustrates a significant advancement in the power of pipelines: PowerShell passes fullfidelity objects along the pipeline, not their text representations.

In contrast, all other shells pass data as plain text between the stages. Extracting meaningful information from plaintext output turns the authoring of pipelines into a black art. Expressing the previous example in a traditional Unixbased shell is exceedingly difficult and nearly impossible in cmd.exe.

Traditional textbased shells make writing pipelines so difficult because they require you to deeply understand the peculiarities of output formatting for each command in the pipeline,

Example 22. A traditional textbased pipeline

lee@trinity:~$ ps F | awk '{ if($5 > 500) print }' | sort r k 64,70

UID
PID
PPID
C
SZ
RSS PSR STIME TTY
TIME CMD

lee
8175
7967
0
965
1036
0 21:51 pts/0
00:00:00 ps F

lee
7967
7966
0
1173
2104
0 21:38 pts/0
00:00:00 bash

In this example, you have to know that, for every line, group number five represents the memory usage. You have to know another language (that of the awk tool) to filter by that column. Finally, you have to know the column range that contains the process name (columns 64 to 70 on this system) and then provide that to the sort command. And that’s just a simple example.

An objectbased pipeline opens up enormous possibilities, making system administration both immensely more simple and more powerful.

Program: Display a Menu to the User

It is often useful to read input from the user but restrict it to a list of choices that you specify. The following script lets you access PowerShell’s prompting functionality in a manner that is friendlier than what PowerShell exposes by default. It returns a number that represents the position of their choice from the list of options you provide.

PowerShell's prompting requires that you include an accelerator key (the & before a letter in the option description) to define the keypress that represents that option. Since you don't always control the list of options (for example, a list of possible directories), Example 121 automatically generates sensible accelerator characters for any descriptions that lack them.

Example 121. ReadHostWithPrompt.ps1

############################################################################## ## ## ReadHostWithPrompt.ps1 ## ## Read user input, with choices restricted to the list of options you ## provide. ## ## ie: ## ## PS >$caption = "Please specify a task" ## PS >$message = "Specify a task to run" ## PS >$option = "&Clean Temporary Files","&Defragment Hard Drive" ## PS >$helptext = "Clean the temporary files from the computer", ## >> "Run the defragment task" ## >> ## PS >$default = 1 ## PS >ReadHostWithPrompt $caption $message $option $helptext $default ##

Example 121. ReadHostWithPrompt.ps1 (continued)

## Please specify a task ## Specify a task to run ## [C] Clean Temporary Files [D] Defragment Hard Drive [?] Help ## (default is "D"):? ## C Clean the temporary files from the computer ## D Run the defragment task ## [C] Clean Temporary Files [D] Defragment Hard Drive [?] Help ## (default is "D"):C ## 0 ## ##############################################################################

param( $caption = $null, $message = $null, $option = $(throw "Please specify some options."), $helpText = $null, $default = 0 )

## Create the list of choices [Management.Automation.Host.ChoiceDescription[]] $choices = @()

## Create a list of possible key accelerators for their options $accelerators = NewObject System.Collections.ArrayList

## First, add a the list of numbers as possible choices $startNumber = [int][char] '0' $endNumber = [int][char] '9' foreach($number in $startNumber..$endNumber) {

[void] $accelerators.Add([char] $number) }

## Then, a list of characters as possible choices $startLetter = [int][char] 'A' $endLetter = [int][char] 'Z' foreach($letter in $startLetter .. $endLetter) {

[void] $accelerators.Add([char] $letter) }

## Go through each of the options, and add them to the choice collection for($counter = 0; $counter lt $option.Length; $counter++) {

$optionText = $option[$counter]

## If they didn't provide an accelerator, generate new option ## text for them if($optionText notmatch '&') {

$optionText = "&{0} {1}" f $accelerators[0],$optionText }

Example 121. ReadHostWithPrompt.ps1 (continued)

## Now, remove their option character from the list of possibilities $acceleratorIndex = $optionText.IndexOf('&') $optionCharacter = $optionText[$acceleratorIndex + 1] $accelerators.Remove($optionCharacter)

## Create the choice $choice = NewObject Management.Automation.Host.ChoiceDescription $optionText if($helpText and $helpText[$counter]) {

$choice.HelpMessage = $helpText[$counter] }

## Add the choice to the list of possible choices $choices += $choice }

## Prompt for the choice, returning the item the user selected $host.UI.PromptForChoice($caption, $message, $choices, $default)

Get the ACL of a Registry Key

Problem

You want to retrieve the ACL of a registry key.

Solution

To retrieve the ACL of a registry key, use the GetAcl cmdlet: PS >GetAcl HKLM:\Software

Path Owner Microsoft.PowerShell.... BUILTIN\Administrators
Access CREATOR OWNER Allow
...

Discussion
 

“Get the ACL of a File or Directory,” the GetAcl cmdlet retrieves 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 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 registry provider, this returns a .NET System.Security.AccessControl.RegistrySecurity object that you can explore for further information.

Schedule a Maintenance Window

Problem

You want to place a server in maintenance mode to prevent it from generating incorrect alerts.

Solution

To schedule a maintenance window on a computer, use the NewMaintenanceWindow cmdlet:

$computer = GetAgent | WhereObject { $_.Name match "Denver" }

$computer.HostComputer | NewMaintenanceWindow `

StartTime (GetDate) `

EndTime (GetDate).AddMinutes(5) `

Comment "Security updates"

To retrieve information about that maintenance window, use the GetMaintenanceWindow cmdlet:

>$computer.HostComputer | GetMaintenanceWindow

MonitoringObjectId : a542ffe891a284a637b00555b15513bd

StartTime : 5/22/2007 9:27:23 AM

ScheduledEndTime : 5/22/2007 9:32:23 AM

EndTime :

Reason : PlannedOther

Comments : Security updates

User : CONTOSO\Administrator

LastModified : 5/22/2007 9:27:23 AM

ManagementGroup : MMS

ManagementGroupId : 846c09747fd058f28020400f125beb67

To stop maintenance mode, use the SetMaintenanceWindow cmdlet to end the maintenance window immediately:

$computer.HostComputer | SetMaintenanceWindow EndTime (GetDate)

Discussion

For more information about the NewMaintenanceWindow cmdlet, type GetHelp NewMaintenanceWindow. For more information about the GetMaintenanceWindow cmdlet, type GetHelp GetMaintenanceWindow. For more information about the SetMaintenanceWindow cmdlet, type GetHelp SetMaintenanceWindow.

Program: Retain Changes to Environment Variables Set by a Batch File in Windows PowerShell

When a batch file modifies an environment variable, cmd.exe retains this change even after the script exits. This often causes problems, as one batch file can accidentally pollute the environment of another. That said, batch file authors sometimes intentionally change the global environment to customize the path and other aspects of the environment to suit a specific task.

However, environment variables are private details of a process and disappear when that process exits. This makes the environment customization scripts mentioned above stop working when you run them from PowerShell—just as they fail to work when you run them from another cmd.exe (for example, cmd.exe /c MyScript.cmd).

retain their changes even after cmd.exe exits. It accomplishes this by storing the environment variables in a text file once the batch file completes, and then setting all those environment variables again in your PowerShell session.

To run this script, type InvokeCmdScript Scriptname.cmd or InvokeCmdScript Scriptname.bat—whichever extension the batch files uses.

If this is the first time you’ve run a script in PowerShell, you will need to configure your Execution Policy.

Notice that this script uses the full names for cmdlets: GetContent, ForeachObject, SetContent, and RemoveItem. This makes the script readable and is ideal for scripts that somebody else will read. It is by no means required, though. For quick scripts and interactive use, shorter aliases (such as gc, %, sc, and ri) can make you more productive.

Example 16. InvokeCmdScript.ps1

############################################################################## ## ## InvokeCmdScript.ps1 ## ## Invoke the specified batch file (and parameters), but also propagate any ## environment variable changes back to the PowerShell environment that ## called it. ## ## i.e., for an already existing 'foothatsetstheFOOenvvariable.cmd': ## ## PS > type foothatsetstheFOOenvvariable.cmd ## @set FOO=%* ## echo FOO set to %FOO%. ## ## PS > $env:FOO ## ## PS > InvokeCmdScript "foothatsetstheFOOenvvariable.cmd" Test ## ## C:\Temp>echo FOO set to Test. ## FOO set to Test. ## ## PS > $env:FOO ## Test ## ##############################################################################

param([string] $script, [string] $parameters)

Example 16. InvokeCmdScript.ps1 (continued)

$tempFile = [IO.Path]::GetTempFileName( )

## Store the output of cmd.exe. We also ask cmd.exe to output ## the environment table after the batch file completes cmd /c " `"$script`" $parameters && set > `"$tempFile`" "

## Go through the environment variables in the temp file. ## For each of them, set the variable in our local environment. GetContent $tempFile | ForeachObject {

if($_ match "^(.*?)=(.*)$") { SetContent "env:\$($matches[1])" $matches[2] } }

RemoveItem $tempFile

Write a Pipeline-Oriented Function in Windows PowerShell

Problem

Your function primarily takes its input from the pipeline, and you want it to perform the same steps for each element of that input.

Solution

To write a pipelineoriented function, define your function using the filter keyword, rather than the function keyword. PowerShell makes the current pipeline object available as the $_ variable.

filter GetPropertyValue($property)

{

$_.$property

}

Discussion

Afilter is the equivalent of a function that uses the cmdletstyle keywords and has all its code inside the process section.

The solution demonstrates an extremely useful filter: one that returns the value of a property for each item in a pipeline:

PS >GetProcess | GetPropertyValue Name audiodg avgamsvr avgemc avgrssvc avgrssvc avgupsvc (...)

Lists, Arrays, and Hashtables

Most scripts deal with more than one thing—lists of servers, lists of files, lookup codes, and more. To enable this, PowerShell supports many features to help you through both its language features and utility cmdlets.

PowerShell makes working with arrays and lists much like working with other data types: you can easily create an array or list and then add or remove elements from it. You can just as easily sort it, search it, or combine it with another array. When you want to store a mapping between one piece of data and another, a hashtable solves that need perfectly.

Rename a File or Directory in PowerShell

Problem

You want to rename a file or directory.

Solution

To rename an item in a provider, use the RenameItem cmdlet: PS > RenameItem example.txt example2.txt

Discussion

The RenameItem cmdlet changes the name of an item. While that may seem like pointing out the obvious, a common mistake is:

PS >RenameItem c:\temp\example.txt c:\temp\example2.txt RenameItem : Cannot rename because the target specified is not a path. At line:1 char:12

+ RenameItem c:\temp\example.txt c:\temp\example2.txt

In this situation, PowerShell provides a (not very helpful) error message because we specified a path for the new item, rather than just its name.

One thing that some shells allow you to do is rename multiple files at the same time. In those shells, the command looks like this:

ren *.gif *.jpg

PowerShell does not support this syntax, but provides even more power through its –replace operator. As a simple example, we can emulate the preceding command: GetChildItem *.gif | RenameItem NewName { $_.Name replace '.gif$','.jpg' }

This syntax provides an immense amount of power. Consider removing underscores from filenames and replacing them with spaces:

GetChildItem *_* | RenameItem NewName { $_.Name replace '_',' ' } or restructuring files in a directory with the naming convention of Report_Project_ Quarter.txt:

PS >GetChildItem | Select Name

Name

Report_Project1_Q3.txt Report_Project1_Q4.txt Report_Project2_Q1.txt

You might want to change that to Quarter_Project.txt with an advanced replacement pattern:

PS >GetChildItem | >> RenameItem NewName { $_.Name replace '.*_(.*)_(.*)\.txt','$2_$1.txt' } >> PS >GetChildItem | Select Name

Name

Q1_Project2.txt Q3_Project1.txt Q4_Project1.txt

Like the other *Item cmdlets, the RenameItem doesn’t work only against the filesystem. Any providers that support the concept of items automatically support this cmdlet as well. For more information about the RenameItem cmdlet, type GetHelp RenameItem.

Determine Whether a Hotfix Is Installed in Windows PowerShell

Problem

You want to determine whether a specific hotfix is installed on a system.

Solution

To retrieve a list of hotfixes applied to the system, use the Win32_ QuickfixEngineering WMI class: PS >GetWmiObject Win32_QuickfixEngineering Filter "HotFixID='KB925228'"

Description : Windows PowerShell(TM) 1.0 FixComments : HotFixID : KB925228 Install Date : InstalledBy : InstalledOn : Name : ServicePackInEffect : SP3 Status :

To determine whether a specific fix is applied, use the TestHotfixInstallation script provided in Example 245:

PS >TestHotfixInstallation KB925228 LEEDESK True PS >TestHotfixInstallation KB92522228 LEEDESK False

Discussion

Example 245 lets you determine whether a hotfix is installed on a specific system. It uses the Win32_QuickfixEngineering WMI class to retrieve this information.

Example 245. TestHotfixInstallation.ps1

############################################################################## ## ## TestHotfixInstallation.ps1 ## ## Determine if a hotfix is installed on a computer ## ## ie: ## ## PS >TestHotfixInstallation KB925228 LEEDESK ## True ## ##############################################################################

param( $hotfix = $(throw "Please specify a hotfix ID"), $computer = "." )

## Create the WMI query to determine if the hotfix is installed $filter = "HotFixID='$hotfix'" $results = GetWmiObject Win32_QuickfixEngineering `

Filter $filter Computer $computer

## Return the results as a boolean, which tells us if the hotfix is installed [bool] $results

Store the Output of a Command in a CSV File

Problem

You want to store the output of a command in a CSV file for later processing. This is helpful when you want to export the data for later processing outside PowerShell.

Solution

Use PowerShell’s ExportCsv cmdlet to save the output of a command into a CSV file. For example, to create an inventory of the patches applied to a system by KB number (on preVista systems):

cd $env:WINDIR GetChildItem KB*.log | ExportCsv c:\temp\patch_log.csv

You can then review this patch log in a tool such as Excel, mail it to others, or do whatever else you might want to do with a CSV file.

Discussion

The CSV file format is one of the most common formats for exchanging semistructured data between programs and systems.

PowerShell’s ExportCsv cmdlet provides an easy way to export data from the PowerShell environment, while still allowing you to keep a fair amount of your data’s structure. When PowerShell exports your data to the CSV, it creates a row for each object that you provide. For each row, PowerShell creates columns in the CSV that represent the values of your object’s properties.

One thing to keep in mind is that the CSV file format supports only plain strings for property values. If a property on your object isn’t actually a string, PowerShell converts it to a string for you. Having PowerShell convert rich property values (such as integers) to strings, however, does mean that a certain amount of information is not preserved. If your ultimate goal is to load this unmodified data again in PowerShell, the ExportCliXml cmdlet provides a much better alternative.

Securely Store Credentials on Disk in Windows PowerShell

Problem

Your script performs an operation that requires credentials, but you don’t want it to require user interaction when it runs.

Solution

To securely store the credential’s password to disk so that your script can load it automatically, use the ConvertFromSecureString and ConvertToSecureString cmdlets.

Save the credential’s password to disk

The first step for storing a password on disk is usually a manual one. Given a credential that you’ve stored in the $credential variable, you can safely export its password to password.txt using the following command:

PS >$credential.Password | ConvertFromSecureString | SetContent c:\temp\password.txt

Recreate the credential from the password stored on disk

In the script that you want to run automatically, add the following commands:

$password = GetContent c:\temp\password.txt | ConvertToSecureString

$credential = NewObject System.Management.Automation.PsCredential `

"CachedUser",$password

These commands create a new credential object (for the CachedUser user) and store that object in the $credential variable.

Discussion

When reading the solution, you might at first be wary of storing a password on disk. While it is natural (and prudent) to be cautious of littering your hard drive with sensitive information, the ConvertFromSecureString cmdlet encrypts this data using Windows’ standard Data Protection API. This ensures that only your user account can properly decrypt its contents.

While keeping a password secure is an important security feature, you may sometimes want to store a password (or other sensitive information) on disk so that other accounts have access to it anyway. This is often the case with scripts run by service accounts or scripts designed to be transferred between computers. The ConvertFromSecureString and ConvertToSecureString cmdlets support this by letting you to specify an encryption key.

When used with a hardcoded encryption key, this technique no longer acts as a security measure. If a user can access to the content of your automated script, they have access to the encryption key. If the user

has access to the encryption key, they have access to the data you were trying to protect.

Although the solution stores the password in a specific named file, it is more common to store the file in a more generic location—such as the directory that contains the script, or the directory that contains your profile.

To load password.txt from the same location as your profile, use the following command:

$passwordFile = JoinPath (SplitPath $profile) password.txt $password = GetContent $passwordFile | ConvertToSecureString

For more information about the ConvertToSecureString and ConvertFromSecureString cmdlets, type GetHelp ConvertToSecureString or GetHelp ConvertFromSecureString.

List a Windows PowerShell User’s Group Membership

Problem

You want to list the groups to which a user belongs.

Solution

To list a user’s group membership, use the [adsi] type shortcut to bind to the user in Active Directory, and then access the MemberOf property:

$user =

[adsi] "LDAP://localhost:389/cn=MyerKen,ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$user.MemberOf

Discussion

The solution lists all groups in which the MyerKen user is a member. Since Active Directory stores this information as a user property, this is simply a specific case of retrieving information about the user.

How to Format a Date for Output in Windows PowerShell

Problem

You want to control the way that PowerShell displays or formats a date.

Solution

To control the format of a date, use one of the following options:

• The GetDate cmdlet’s –Format parameter:

PS >GetDate Date "05/09/1998 1:23 PM" Format "ddMMyyyy @ hh:mm:ss" 09051998 @ 01:23:00

• PowerShell’s string formatting (f) operator:

PS >$date = [DateTime] "05/09/1998 1:23 PM" PS >"{0:ddMMyyyy @ hh:mm:ss}" f $date 09051998 @ 01:23:00

• The object’s ToString() method:

PS >$date = [DateTime] "05/09/1998 1:23 PM" PS >$date.ToString("ddMMyyyy @ hh:mm:ss") 09051998 @ 01:23:00

• The GetDate cmdlet’s –UFormat parameter, which supports Unix date format strings:

PS >GetDate Date "05/09/1998 1:23 PM" UFormat "%d%m%Y @ %I:%M:%S" 09051998 @ 01:23:00

Discussion

Except for the –Uformat parameter of the GetDate cmdlet, all date formatting in PowerShell uses the standard .NET DateTime format strings. These format strings let you display dates in one of many standard formats (such as your system’s short or long date patterns), or in a completely custom manner.

If you are already used to the Unixstyle date formatting strings (or are converting an existing script that uses a complex one), the –Uformat parameter of the GetDate cmdlet may be helpful. It accepts the format strings accepted by the Unix date command, but does not provide any functionality that standard .NET date formatting strings cannot.

When working with the string version of dates and times, be aware that they are the most common source of internationalization issues—problems that arise from running a script on a machine with a different culture than the one it was written on. In North America “05/09/1998” means “May 9, 1998.” In many other cultures, though, it means “September 5, 1998.” Whenever possible use and compare DateTime objects (rather than strings) to other DateTime objects, as that avoids these cultural differences. Example 56 demonstrates this approach.

Example 56. Comparing DateTime objects with the gt operator

PS >$dueDate = [DateTime] "01/01/2006" PS >if([DateTime]::Now gt $dueDate) >> { >> "Account is now due" >> } >> Account is now due

PowerShell always assumes the North American date format when it interprets a DateTime constant such as [DateTime] "05/09/1998". This is for the same reason that all languages interpret numeric constants

(such as 12.34) in the North American format. If it did otherwise, nearly every script that dealt with dates and times would fail on international systems.

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

Automate Programs Using COM Scripting Interfaces Problem

Problem

You want to automate a program or system task through its COM automation interface.

Solution

To instantiate and work with COM objects, use the NewObject cmdlet’s –ComObject parameter.

$shell = NewObject ComObject "Shell.Application" $shell.Windows() | FormatTable LocationName,LocationUrl

Discussion

Like WMI, COM automation interfaces have long been a standard tool for scripting and system administration. When an application exposes management or automation tasks, COM objects are the second most common interface (right after custom commandline tools).

PowerShell exposes COM objects like it exposes most other management objects in the system. Once you have access to a COM object, you work with its properties and methods in the same way that you work with methods and properties of other objects in PowerShell.

In addition to automation tasks, many COM objects exist entirely to improve the scripting experience in languages such as VBScript. One example of this is working with files, or sorting an array.

One thing to remember when working with these COM objects is that PowerShell often provides better alternatives to them! In many cases, PowerShell’s cmdlets, scripting language, or access to the .NET Framework provide the same or similar functionality to a COM object that you might be used to.

Test Active Directory Scripts on a Local Installation

Problem

You want to test your Active Directory scripts against a local installation.

Solution

To test your scripts against a local system, install Active Directory Application Mode (ADAM) and its sample configuration.

Discussion

To test your scripts against a local installation, you’ll need to install ADAM, and then create a test instance.

Install ADAM

To install ADAM, the first step is to download it. Microsoft provides ADAM free of charge from the Download Center. You can obtain it by searching for “Active Directory Application Mode” at http://download.microsoft.com.

Create a test instance

From the ADAM menu in the Windows Start menu, select Create an ADAM instance. In the Setup Options page that appears next, select A unique instance.In the Instance Name page, type Test as an instance name. Accept the default ports, and then select Yes, create an application directory partition on the next page. As the partition name, type DC=Fabrikam,DC=COM

In the next pages, accept the default file locations, service accounts, and administrators.

When the setup wizard gives you the option to import LDIF files, import all available files except for MSAZMan.LDF. Click Next on this page and the confirmation page to complete the instance setup.

Open a PowerShell window, and test your new instance:

PS >[adsi] "LDAP://localhost:389/dc=Fabrikam,dc=COM"

distinguishedName

{DC=Fabrikam,DC=COM} The [adsi] tag is a type shortcut, like several other type shortcuts in PowerShell. The [adsi] type shortcut provides a quick way to create and work with directory entries through Active Directory Service Interfaces.

Although scripts that act against an ADAM test environment are almost identical to those that operate directly against Active Directory, there are a few minor differences. ADAM scripts specify the host and port in their binding string (that is, localhost:389/), whereas Active Directory scripts do not.

Learn About Types and Objects

Problem

You have an instance of an object and want to know what methods and properties it supports.

Solution

The most common way to explore the methods and properties supported by an object is through the GetMember cmdlet.

To get the instance members of an object you’ve stored in the $object variable, pipe it to the GetMember cmdlet:

$object | GetMember GetMember –InputObject $object

To get the static members of an object you’ve stored in the $object variable, supply the –Static flag to the GetMember cmdlet:

$object | GetMember –Static GetMember –Static –InputObject $object

To get the static members of a specific type, pipe that type to the GetMember cmdlet, and also specify the –Static flag:

[Type] | GetMember –Static GetMember –InputObject [Type]

To get members of the specified member type (for example, Method, Property) from an object you have stored in the $object variable, supply that member type to the –MemberType parameter:

$object | GetMember –MemberType memberType GetMember –MemberType memberType –InputObject $object

Discussion

The GetMember cmdlet is one of the three commands you will use most commonly as you explore Windows PowerShell. The other two commands are GetCommand and GetHelp.

If you pass the GetMember cmdlet a collection of objects (such as an Array or ArrayList) through the pipeline, PowerShell extracts each item from the collection, and then passes them to the GetMember cmdlet onebyone. The GetMember cmdlet then returns the members of each unique type that it receives. Although helpful the vast majority of the time, this sometimes causes difficulty when you want to learn about the members or properties of the collection class itself.

If you want to see the properties of a collection (as opposed to the elements it contains,) provide the collection to the –InputObject parameter, instead. Alternatively, you may wrap the collection in an array (using PowerShell’s unary comma operator) so that the collection class remains when the GetMember cmdlet unravels the outer array:

PS >$files = GetChildItem PS >,$files | GetMember

TypeName: System.Object[]

Name MemberType Definition

Count AliasProperty Count = Length Address Method System.Object& Address(Int32 ) (...)

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

View and Modify Environment Variables in PowerShell

Problem

You want to interact with your system’s environment variables.

Solution

To interact with environment variables, access them in almost the same way that you access regular PowerShell variables. The only difference is that you place env: between the ($) dollar sign and the variable name:

PS >$env:Username Lee

You can modify environment variables this way, too. For example, to temporarily add the current directory to the path:

PS >InvokeDemonstrationScript The term 'InvokeDemonstrationScript' is not recognized as a cmdlet, funct ion, operable program, or script file. Verify the term and try again. At line:1 char:26

+ InvokeDemonstrationScript PS >$env:PATH = $env:PATH + ";." PS >InvokeDemonstrationScript.ps1 The script ran!

Discussion

In batch files, environment variables are the primary way to store temporary information, or to transfer information between batch files. PowerShell variables and script parameters are more effective ways to solve those problems, but environment variables continue to provide a useful way to access common system settings, such as the system’s path, temporary directory, domain name, username, and more.

PowerShell surfaces environment variables through its environment provider—a container that lets you work with environment variables much like you would work with items in the filesystem or registry providers. By default, PowerShell defines an env: (much like the c: or d:) that provides access to this information:

PS >dir env:

Name
Value

Path
c:\progra~1\ruby\bin;C:\WINDOWS\system32;C:\

TEMP
C:\DOCUME~1\Lee\LOCALS~1\Temp

SESSIONNAME
Console

PATHEXT
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;

(...)

Since it is a regular PowerShell drive, the full way to get the value of an environment variable looks like this:

PS >GetContent Env:\Username Lee

When it comes to environment variables, though, that is a syntax you will almost never need to use, because of PowerShell’s support for the GetContent and SetContent variable syntax, which shortens that to:

PS >$env:Username Lee

This syntax works for all drives but is used most commonly to access environment variables.

Some environment variables actually get their values from a combination of two places: the machinewide settings and the currentuser settings. If you want to access environment variable values specifically configured at the machine or user level, use the [Environment]::GetEnvironmentVariable() method. For example, if you've defined a tools directory in your path, you might see:

PS >[Environment]::GetEnvironmentVariable("Path", "User") d:\lee\tools

To set these machine or userspecific environment variables permanently, use the [Environment]::SetEnvironmentVariable() method:

[Environment]::SetEnvironmentVariable(, , )

The Target parameter defines where this variable should be stored: User for the current user, and Machine for all users on the machine. For example, to permanently add your Tools directory to your path:

PS >$oldPersonalPath = [Environment]::GetEnvironmentVariable("Path", "User") PS >$oldPersonalPath += ";d:\tools" PS >[Environment]::SetEnvironmentVariable("Path", $oldPersonalPath, "User")