Skip to main content

Windows

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")