Skip to main content

Windows

Modify or Remove a Registry Key Value

Problem

You want to modify or remove a property of a specific registry key.

Solution

To set the value of a registry key, use the SetItemProperty cmdlet:

PS >(GetItemProperty .).MyProgram c:\temp\MyProgram.exe PS >SetItemProperty . MyProgram d:\Lee\tools\MyProgram.exe PS >(GetItemProperty .).MyProgram d:\Lee\tools\MyProgram.exe

To remove the value of a registry key, use the RemoveItemProperty cmdlet:

PS >RemoveItemProperty . MyProgram PS >(GetItemProperty .).MyProgram

Discussion

In the registry provider, PowerShell treats registry keys as items and key values as properties of those items. To change the value of a key property, use the SetItemProperty cmdlet. The SetItemProperty cmdlet has the standard alias, sp.To remove a key property altogether, use the RemoveItemProperty cmdlet.

As always, use caution when changing information in the registry. Deleting or changing the wrong item can easily render your system unbootable.

For more information about the GetItemProperty cmdlet, type GetHelp GetItemProperty. For information about the SetItemProperty and RemoveItemProperty cmdlets, type GetHelp SetItemProperty or GetHelp RemoveItemProperty, respectively. For more information about the registry provider, type GetHelp Registry.

Manage Mailboxes

Problem

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

Solution

To retrieve information about a mailbox (or multiple mailboxes), use the GetMailbox cmdlet:

$user = GetMailbox *preeda*

$user | FormatList * To modify information about a mailbox, use the SetMailbox cmdlet. This example prevents Preeda from sending mail when her mailbox goes over 2 GB, and then verifies it:

$user | SetMailbox –ProhibitSendQuota 2GB $user | GetMailbox

Discussion

In addition to the common task of retrieving and modifying mailbox information, another useful mailboxrelated command is the MoveMailbox cmdlet. For example, to move all users from one storage group to another database:

GetMailbox |

WhereObject { $_.Database –like "*SMBEX01\First Storage Group" } |

MoveMailbox –TargetDatabase "Mailbox Database 3"

After a few moments, that command displays the progress of the bulk mailbox move.

For more information about the GetMailbox cmdlet, type GetHelp GetMailbox. For more information about the SetMailbox cmdlet, type GetHelp SetMailbox. For more information about the MoveMailbox cmdlet, type GetHelp MoveMailbox.

Run a PowerShell Command in Windows PowerShell

Problem

You want to run a PowerShell command.

Solution

To run a PowerShell command, type its name at the command prompt. For example:

PS >GetProcess

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName

133 5 11760 7668 46 1112 audiodg

184 5 33248 508 93 1692 avgamsvr

143 7 31852 984 97 1788 avgemc

Discussion

The GetProcess command is an example of a native PowerShell command, called a cmdlet. As compared to traditional commands, cmdlets provide significant benefits to both administrators and developers:

  • They share a common and regular commandline syntax.
  • They support rich pipeline scenarios (using the output of one command as the input of another).
  • They produce easily manageable objectbased output, rather than errorprone plain text output.

Because the GetProcess cmdlet generates rich objectbased output, you can use its output for many processrelated tasks.

The GetProcess cmdlet is just one of the many that PowerShell supports.

Write a Script Block in Windows PowerShell

Problem

You have a section of your script that works nearly the same for all input, aside from a minor change in logic.

Solution

As shown in Example 103, place the minor logic differences in a script block, and then pass that script block as a parameter to the code that requires it. Use the invoke operator (&) to execute the script block.

Example 103. A script that applies a script block to each element in the pipeline

############################################################################## ## MapObject.ps1 ## ## Apply the given mapping command to each element of the input ## ## Example: ## 1,2,3 | MapObject { $_ * 2 } ############################################################################## param([ScriptBlock] $mapCommand)

process { & $mapCommand }

Discussion

Imagine a script that needs to multiply all the elements in a list by two:

function MultiplyInputByTwo

{ process {

$_ * 2 } }

but it also needs to perform a more complex calculation:

function MultiplyInputComplex

{ process {

($_ + 2) * 3 } }

These two functions are strikingly similar, except for the single line that actually performs the calculation. As we add more calculations, this quickly becomes more evident. Adding each new seven line function gives us only one unique line of value!

PS >1,2,3 | MultiplyInputByTwo 2 4 6 PS >1,2,3 | MultiplyInputComplex

9 12 15

If we instead use a script block to hold this “unknown” calculation, we don’t need to keep on adding new functions:

PS >1,2,3 | MapObject { $_ * 2 } 2 4 6 PS >1,2,3 | MapObject { ($_ + 2) * 3 } 9 12 15 PS >1,2,3 | MapObject { ($_ + 3) * $_ } 4 10 18

In fact, the functionality provided by MapObject is so helpful that it is a standard PowerShell cmdlet—called ForeachObject.

Program: Get Disk Usage Information

Discussion

When disk space starts running low, you’ll naturally want to find out where to focus your cleanup efforts. Sometimes, you may tackle this by looking for large directories (including the directories in them), but other times, you may solve this by looking for directories that are large simply from the files they contain.

Example 172 collects both types of data. It also demonstrates an effective use of calculated properties. Like the AddMember cmdlet, calculated properties let you add properties to output objects by specifying the expression that generates their data.

Example 172. GetDiskUsage.ps1

############################################################################## ## ## GetDiskUsage.ps1 ## ## Retrieve information about disk usage in the current directory and all ## subdirectories. If you specify the IncludeSubdirectories flag, this ## script accounts for the size of subdirectories in the size of a directory. ## ## ie: ## ## PS >GetDiskUsage ## PS >GetDiskUsage IncludeSubdirectories ## ##############################################################################

param( [switch] $includeSubdirectories )

## If they specify the IncludeSubdirectories flag, then we want to account ## for all subdirectories in the size of each directory if($includeSubdirectories) {

GetChildItem | WhereObject { $_.PsIsContainer } |

SelectObject Name, @{ Name="Size"; Expression={ ($_ | GetChildItem Recurse |

MeasureObject Sum Length).Sum + 0 } } } ## Otherwise, we just find all directories below the current directory, ## and determine their size else {

GetChildItem Recurse | WhereObject { $_.PsIsContainer } |

SelectObject FullName, @{ Name="Size"; Expression={ ($_ | GetChildItem |

MeasureObject Sum Length).Sum + 0 } } }

Program: List All Installed Software in PowerShell

The best place to find information about currently installed software is actually from the place that stores information about how to uninstall it: the HKLM:\SOFTWARE\ Microsoft\Windows\CurrentVersion\Uninstall registry key.

Each child of that registry key represents a piece of software you can uninstall—traditionally through the Add/Remove Programs entry in the Control Panel. In addition to the DisplayName of the application, other useful properties usually exist (depending on the application). Examples include Publisher, UninstallString, and HelpLink.

To see all the properties available from software installed on your system, type the following:

$properties = GetInstalledSoftware | ForeachObject { $_.PsObject.Properties }

$properties | SelectObject Name | SortObject Unique Name

This lists all properties mentioned by at least one installed application (although very few are shared by all installed applications).

To work with this data, though, you first need to retrieve it. Example 243 provides a script to list all installed software on the current system, returning all information as properties of PowerShell objects.

Example 243. GetInstalledSoftware.ps1

############################################################################## ## ## GetInstalledSoftware.ps1 ## ## List all installed software on the current computer. ## ## ie: ##

Example 243. GetInstalledSoftware.ps1 (continued)

## PS >GetInstalledSoftware PowerShell ## ##############################################################################

param( $displayName = ".*" )

## Get all the listed software in the Uninstall key $keys = GetChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

## Get all of the properties from those items $items = $keys | ForeachObject { GetItemProperty $_.PsPath }

## For each of those items, display the DisplayName and Publisher foreach($item in $items) {

if(($item.DisplayName) and ($item.DisplayName match $displayName)) { $item } }

Parse and Manage Binary Files in Windows PowerShell

Problem

You want to work with binary data in a file.

Solution

Two main techniques are used when working with binary data in a file. The first is to read the file using the Byte encoding, so that PowerShell does not treat the content as text. The second is to use the BitConverter class to translate these bytes back and forth into numbers that you more commonly care about.

Example 73 displays the “characteristics” of a Windows executable. The beginning section of any executable (a .DLL, .EXE, and several others) starts with a binary section known as the PE (portable executable) header. Part of this header includes characteristics about that file—such as whether the file is a DLL.

For more information about the PE header format, see http://www.microsoft.com/ whdc/system/platform/firmware/PECOFF.mspx.

Example 73. GetCharacteristics.ps1

############################################################################## ## ## GetCharacteristics.ps1 ## ## Get the file characteristics of a file in the PE Executable File Format. ## ## ie: ## ## PS >GetCharacteristics $env:WINDIR\notepad.exe ## IMAGE_FILE_LOCAL_SYMS_STRIPPED ## IMAGE_FILE_RELOCS_STRIPPED ## IMAGE_FILE_EXECUTABLE_IMAGE

Example 73. GetCharacteristics.ps1 (continued)

## IMAGE_FILE_32BIT_MACHINE ## IMAGE_FILE_LINE_NUMS_STRIPPED ## ##############################################################################

param([string] $filename = $(throw "Please specify a filename."))

## Define the characteristics used in the PE file file header. ## Taken from http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx $characteristics = @{} $characteristics["IMAGE_FILE_RELOCS_STRIPPED"] = 0x0001 $characteristics["IMAGE_FILE_EXECUTABLE_IMAGE"] = 0x0002 $characteristics["IMAGE_FILE_LINE_NUMS_STRIPPED"] = 0x0004 $characteristics["IMAGE_FILE_LOCAL_SYMS_STRIPPED"] = 0x0008 $characteristics["IMAGE_FILE_AGGRESSIVE_WS_TRIM"] = 0x0010 $characteristics["IMAGE_FILE_LARGE_ADDRESS_AWARE"] = 0x0020 $characteristics["RESERVED"] = 0x0040 $characteristics["IMAGE_FILE_BYTES_REVERSED_LO"] = 0x0080 $characteristics["IMAGE_FILE_32BIT_MACHINE"] = 0x0100 $characteristics["IMAGE_FILE_DEBUG_STRIPPED"] = 0x0200 $characteristics["IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP"] = 0x0400 $characteristics["IMAGE_FILE_NET_RUN_FROM_SWAP"] = 0x0800 $characteristics["IMAGE_FILE_SYSTEM"] = 0x1000 $characteristics["IMAGE_FILE_DLL"] = 0x2000 $characteristics["IMAGE_FILE_UP_SYSTEM_ONLY"] = 0x4000 $characteristics["IMAGE_FILE_BYTES_REVERSED_HI"] = 0x8000

## Get the content of the file, as an array of bytes $fileBytes = GetContent $filename ReadCount 0 Encoding byte

## The offset of the signature in the file is stored at location 0x3c. $signatureOffset = $fileBytes[0x3c]

## Ensure it is a PE file $signature = [char[]] $fileBytes[$signatureOffset..($signatureOffset + 3)] if([String]::Join('', $signature) ne "PE`0`0") {

throw "This file does not conform to the PE specification." }

## The location of the COFF header is 4 bytes into the signature $coffHeader = $signatureOffset + 4

## The characteristics data are 18 bytes into the COFF header. The BitConverter ## class manages the conversion of the 4 bytes into an integer. $characteristicsData = [BitConverter]::ToInt32($fileBytes, $coffHeader + 18)

## Go through each of the characteristics. If the data from the file has that ## flag set, then output that characteristic. foreach($key in $characteristics.Keys)

Example 73. GetCharacteristics.ps1 (continued)

{ $flag = $characteristics[$key] if(($characteristicsData band $flag) eq $flag) {

$key } }

Discussion

For most files, this technique is the easiest way to work with binary data. If you actually modify the binary data, then you will also want to use the Byte encoding when you send it back to disk:

$fileBytes | SetContent modified.exe Encoding Byte

For extremely large files, though, it may be unacceptably slow to load the entire file into memory when you work with it. If you begin to run against this limit, the solution is to use file management classes from the .NET Framework. These classes include BinaryReader, StreamReader, and others.

Program: Create a Self-Signed Certificate in PowerShell

Discussion

It is possible to benefit from the tamperprotection features of signed scripts without having to pay for an official codesigning certificate. You do this by creating a selfsigned certificate. Scripts signed with a selfsigned certificate will not be recognized as valid on other computers, but still lets you sign scripts on your own computer.

When Example 161 runs, it prompts you for a password. Windows uses this pass word to prevent malicious programs from automatically signing files on your behalf.

Example 161. NewSelfSignedCertificate.ps1

############################################################################## ## ## NewSelfSignedCertificate.ps1 ## ## Generate a new selfsigned certificate. The certificate generated by these ## commands allow you to sign scripts on your own computer for protection ## from tampering. Files signed with this signature are not valid on other ## computers. ## ## ie: ## ## PS >NewSelfSignedCertificate.ps1 ## ##############################################################################

if(not (GetCommand makecert.exe ErrorAction SilentlyContinue)) { $errorMessage = "Could not find makecert.exe. " + "This tool is available as part of Visual Studio, or the Windows SDK."

WriteError $errorMessage return }

$keyPath = JoinPath ([IO.Path]::GetTempPath()) "root.pvk"

## Generate the local certification authority

makecert n "CN=PowerShell Local Certificate Root" a sha1 ` eku 1.3.6.1.5.5.7.3.3 r sv $keyPath root.cer ` ss Root sr localMachine

## Use the local certification authority to generate a selfsigned ## certificate makecert pe n "CN=PowerShell User" ss MY a sha1 `

eku 1.3.6.1.5.5.7.3.3 iv $keyPath ic root.cer

## Remove the private key from the filesystem. RemoveItem $keyPath

Example 161. NewSelfSignedCertificate.ps1 (continued)

## Retrieve the certificate GetChildItem cert:\currentuser\my codesign | WhereObject { $_.Subject match "PowerShell User" }

Create a Security or Distribution Group in Windows PowerShell

Problem

You want to create a security or distribution group.

Solution

To create a security or distribution group, use the [adsi] type shortcut to bind to a container in Active Directory, and then call the Create() method:

$salesWest =

[adsi] "LDAP://localhost:389/ou=West,ou=Sales,dc=Fabrikam,dc=COM" $management = $salesWest.Create("Group", "CN=Management") $management.SetInfo()

Discussion

The solution creates a group named Management in the Sales West OU.

When you run this script against a real Active Directory deployment (as opposed to an ADAM instance), be sure to update the sAMAccountName property, or you’ll get an autogenerated default.

When you create a group in Active Directory, it is customary to also set the type of group by defining the groupType attribute on that group. To specify a group type, use the –bor operator to combine group flags and use the resulting value as the groupType property. Example 233 defines the group as a global, securityenabled group.

Example 233. Creating an Active Directory security group with a custom groupType

$ADS_GROUP_TYPE_GLOBAL_GROUP = 0x00000002 $ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP = 0x00000004 $ADS_GROUP_TYPE_LOCAL_GROUP = 0x00000004 $ADS_GROUP_TYPE_UNIVERSAL_GROUP = 0x00000008 $ADS_GROUP_TYPE_SECURITY_ENABLED = 0x80000000

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

$groupType = $ADS_GROUP_TYPE_SECURITY_ENABLED bor $ADS_GROUP_TYPE_GLOBAL_GROUP

$management = $salesWest.Create("Group", "CN=Management") $management.Put("groupType", $groupType) $management.SetInfo()

If you need to create groups in bulk from the data in a CSV, the ImportADUser script. To make the script create groups instead of users, change this line:

$newUser = $userContainer.Create("User", "CN=$username")

to this:

$newUser = $userContainer.Create("Group", "CN=$username")

If you change the script to create groups in bulk, it is helpful to also change the variable names ($user, $users, $username, and $newUser) to correspond to grouprelated names: $group, $groups, $groupname, and $newgroup.

Prevent a String from Including Dynamic Information in Windows PowerShell

Problem

You want to prevent PowerShell from interpreting special characters or variable names inside a string.

Solution

Use a nonexpanding string to have PowerShell interpret your string exactly as entered. A nonexpanding uses the single quote character around its text.

PS >$myString = 'Useful PowerShell characters include: $, `, " and { }' PS >$myString Useful PowerShell characters include: $, `, " and { }

If you want to include newline characters as well, use a nonexpanding here string, as in Example 52.

Example 52. A nonexpanding here string that includes newline characters

PS >$myString = @' >> Tip of the Day >> >> Useful PowerShell characters include: $, `, ', " and { } >> '@ >> PS >$myString Tip of the Day

Useful PowerShell characters include: $, `, ', " and { }

Discussion

In a literal string, all the text between the single quotes becomes part of your string. This is in contrast to an expanding string, where PowerShell expands variable names (such as $myString) and escape sequences (such as `n) with their values (such as the content of $myString and the newline character).

Nonexpanding strings are a useful way to manage files and folders that contain special characters that might otherwise be interpreted as escape sequences.

“Create a String, ” one exception to the “all text in a literal string is literalrule comes from the quote characters themselves. In either type of string, PowerShell let you place two of that string’s quote characters together to include the quote character itself:

$myString = "This string includes ""double quotes"" because it combined quote characters." $myString = 'This string includes ''single quotes'' because it combined quote characters.'