Skip to main content

Windows

Get, Install, and Uninstall Management Packs

Problem

You want to automate the deployment or configuration of management packs.

Solution

To retrieve information about installed management packs, use the GetManagementPack cmdlet, as shown in Example 261.

Example 261. Using the GetManagementPack cmdlet

PS Monitoring:\Oxford.contoso.com >$mp = GetManagementPack | WhereObject { $_.DisplayName eq "Health Internal Library" } PS Monitoring:\Oxford.contoso.com >$mp

Name : System.Health.Internal TimeCreated : 5/22/2007 9:38:40 AM LastModified : 5/22/2007 9:38:40 AM KeyToken : 31bf3856ad364e35 Version : 6.0.5000.0 Id : 9395a1eb63221c8b71ad7ab6955c7e11 VersionId : dfaeece9437e7d46edce260bd77a8667 References : {System.Library, System.Health.Library} Sealed : True

Example 261. Using the GetManagementPack cmdlet (continued)

ContentReadable
: False

FriendlyName
: System Health Internal Library

DisplayName
: Health Internal Library

Description
: System Health Interal Library: This Management Pack (...)

DefaultLanguageCode : ENU LockObject : System.Object

Use the UninstallManagementPack cmdlet to remove a management pack:

$mp = GetManagementPack | WhereObject { $_.DisplayName eq "Management Pack Name" } $mp | UninstallManagementPack

To install a management pack, provide its path to the InstallManagementPack cmdlet:

InstallManagementPack

Discussion

For more information about the GetManagementPack cmdlet, type GetHelp GetManagementPack. For more information about the InstallManagementPack cmdlet, type GetHelp InstallManagementPack. For more information about the UninstallManagementPack cmdlet, type GetHelp UninstallManagementPack.

How to Get the System Date and Time in Windows PowerShell

Problem

You want to get the system date.

Solution

To get the system date, run the command GetDate.

Discussion

The GetDate command generates rich objectbased output, so you can use its result for many daterelated tasks. For example, to determine the current day of the week:

PS >$date = GetDate PS >$date.DayOfWeek Sunday

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

Create an Array or List of Items

Problem

You want to create an array or list of items.

Solution

To create an array that holds a given set of items, separate those items with commas:

PS >$myArray = 1,2,"Hello World" PS >$myArray 1 2 Hello World

To create an array of a specific size, use the NewObject cmdlet:

PS >$myArray = NewObject string[] 10 PS >$myArray[5] = "Hello" PS >$myArray[5] Hello

To store the output of a command that generates a list, use variable assignment:

PS >$myArray = GetProcess PS >$myArray

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

274 6 1316 3908 33 3164 alg 983 7 3636 7472 30 688 csrss 69 4 924 3332 30 0.69 2232 ctfmon 180 5 2220 6116 37 2816 dllhost (...)

To create an array that you plan to modify frequently, use an ArrayList, as shown by Example 111.

Example 111. Using an ArrayList to manage a dynamic collection of items

PS >$myArray = NewObject System.Collections.ArrayList PS >[void] $myArray.Add("Hello") PS >[void] $myArray.AddRange( ("World","How","Are","You") ) PS >$myArray Hello World How Are You PS >$myArray.RemoveAt(1) PS >$myArray Hello How Are You

Discussion

Aside from the primitive data types (such as strings, integers, and decimals), lists of items are a common concept in the scripts and commands that you write. Most commands generate lists of data: the GetContent cmdlet generates a list of strings in a file, the GetProcess cmdlet generates a list of processes running on the system, and the GetCommand cmdlet generates a list of commands, just to name a few.

The solution shows how to store the output of a command that generates a list. If a command outputs only one item (such as a single line from a file, a single process, or a single command), then that output is

no longer a list. If you want to treat that output as a list even when it is not, use the list evaluation syntax ( @() ) to force PowerShell to interpret it as an array:

$myArray = @(GetProcess Explorer)

Move a File or Directory in PowerShell

Problem

You want to move a file or directory.

Solution

To move a file or directory, use the MoveItem cmdlet: PS >MoveItem example.txt c:\temp\example2.txt

Discussion

The MoveItem cmdlet moves an item from one location to another. Like the other *Item cmdlets, the MoveItem doesn’t work only against the filesystem. Any providers that support the concept of items automatically support this cmdlet as well.

The MoveItem cmdlet lets you specify multiple files through its Path, Include, Exclude, and Filter parameters.

Although the MoveItem cmdlet works in every provider, you cannot move items between providers. For more information about the MoveItem cmdlet, type GetHelp MoveItem.

Program: Summarize System Information in Windows PowerShell

WMI provides an immense amount of information about the current system or remote systems. In fact, the msinfo32.exe application traditionally used to gather system information is based largely on WMI.

The script shown in Example 246 summarizes the most common information, but WMI provides a great deal more than that.

Example 246. GetDetailedSystemInformation.ps1

############################################################################## ## ## GetDetailedSystemInformation.ps1 ## ## Get detailed information about a system. ## ## ie: ## ## PS >GetDetailedSystemInformation LEEDESK > output.txt ## ##############################################################################

param( $computer = "." )

"#"*80 "System Information Summary" "Generated $(GetDate)" "#"*80 "" ""

"#"*80 "Computer System Information" "#"*80 GetWmiObject Win32_ComputerSystem Computer $computer | FormatList *

"#"*80 "Operating System Information" "#"*80 GetWmiObject Win32_OperatingSystem Computer $computer | FormatList *

"#"*80 "BIOS Information" "#"*80 GetWmiObject Win32_Bios Computer $computer | FormatList *

Example 246. GetDetailedSystemInformation.ps1 (continued)

"#"*80 "Memory Information" "#"*80 GetWmiObject Win32_PhysicalMemory Computer $computer | FormatList *

"#"*80 "Physical Disk Information" "#"*80 GetWmiObject Win32_DiskDrive Computer $computer | FormatList *

"#"*80 "Logical Disk Information" "#"*80 GetWmiObject Win32_LogicalDisk Computer $computer | FormatList *

Import Structured Data from a CSV File

Problem

You want to import structured data that has been stored in a CSV file. This is helpful when you want to use structured data created by another program, or structured data modified by a person.

Solution

Use PowerShell’s ImportCsv cmdlet to import structured data from a CSV file.

For example, imagine that you previously exported an inventory of the patches applied to a preVista system by KB number:

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

Somebody reviewed the CSV, and kept only lines from patch logs that they would like to review further. You would like to copy those actual patch logs to a directory so that you can share them.

PS >ImportCsv C:\temp\patch_log_reviewed.csv | ForeachObject { >> CopyItem –LiteralPath $_.FullName –Destination c:\temp\sharedlogs\ } >>

Discussion

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

PowerShell’s ImportCsv cmdlet provides an easy way to import semistructured data to the PowerShell environment from other programs. When PowerShell imports your data from the CSV, it creates a new object for each row in the CSV. For each object, PowerShell creates properties on the object from the values of the columns in the CSV.

The preceding solution uses the ForeachObject cmdlet to pass each object to the CopyItem cmdlet. For each item, it uses the incoming object’s FullName property as the source path, and uses c:\temp\

sharedlogs\ as the destination. However, the CSV includes a PSPath property that represents the source, and most cmdlets support PSPath as an alternative (alias) parameter name for –LiteralPath. Because of this, we could have also written

PS >ImportCsv C:\temp\patch_log_reviewed.csv | >> CopyItem Destination c:\temp\sharedlogs\ >>

One thing to keep in mind is that the CSV file format supports only plain strings for property values. When you import data from a CSV, properties that look like dates will still only be strings. Properties that look like numbers will only be strings. Properties that look like any sort of rich data type will only be strings. That means that sorting on any property will always be an alphabetical sort, which is usually not the same as the sorting rules for the rich data types that the property might look like.

If your ultimate goal is to load rich unmodified data from something that you’ve previously exported from PowerShell, the ImportCliXml cmdlet provides a much better alternative.

Access User and Machine Certificates

Problem

You want to retrieve information about certificates for the current user or local machine.

Solution

To browse and retrieve certificates on the local machine, use PowerShell’s certificate drive. This drive is created by the certificate provider, as shown in Example 165.

Example 165. Exploring certificates in the certificate provider

PS >SetLocation cert:\CurrentUser\ PS >$cert = GetChildItem Rec CodeSign PS >$cert | FormatList

Subject : CN=PowerShell User Issuer : CN=PowerShell Local Certificate Root Thumbprint : FD48FAA9281A657DBD089B5A008FAFE61D3B32FD FriendlyName : NotBefore : 4/22/2007 12:32:37 AM NotAfter : 12/31/2039 3:59:59 PM Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptogr

aphy.Oid}

Discussion

The certificate drive provides a useful way to navigate and view certificates for the current user or local machine. For example, if your execution policy requires the use of digital signatures, the following command tells you which publishers are trusted to run scripts on your system:

GetChildItem cert:\CurrentUser\TrustedPublisher

The certificate provider is probably most commonly used to select a codesigning certificate for the SetAuthenticodeSignature cmdlet. The following command selects the “best” code signing certificate—that being the one that expires last:

$certificates = GetChildItem Cert:\CurrentUser\My CodeSign $signingCert = @($certificates | Sort Desc NotAfter)[0] In this CodeSign parameter lets you search for certificates in the certificate store that support code signing.

Although the certificate provider is useful for browsing and retrieving information from the computer’s certificate stores, it does not lets you add or remove items from these locations. If you want to manage certificates in the certificate store, the System.Security.Cryptography.X509Certificates.X509Store class (and other related classes from the System.Security.Cryptography.X509Certificates namespace) from the .NET Framework support that functionality.

For more information about the certificate provider, type GetHelp Certificate.

List the Members of a Group in Windows PowerShell

Problem

You want to list all the members in a group.

Solution

To list the members of a group, use the [adsi] type shortcut to bind to the group in Active Directory, and then access the Member property:

$group =

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

$group.Member

Discussion

The solution lists all members of the Management group in the Sales West OU. Since Active Directory stores this information as a property of the group, this is simply a specific case of retrieving information about the group.

Program: Convert Text Streams to Objects in Windows PowerShell

One of the strongest features of PowerShell is its objectbased pipeline. You don’t waste your energy creating, destroying, and recreating the object representation of your data. In other shells, you lose the fullfidelity representation of data when the pipeline converts it to pure text. You can regain some of it through excessive text parsing, but not all of it.

However, you still often have to interact with lowfidelity input that originates from outside PowerShell. Textbased data files and legacy programs are two examples.

PowerShell offers great support for two of the three textparsing staples:

Sed

Replaces text. For that functionality, PowerShell offers the replace operator.

Grep

Searches text. For that functionality, PowerShell offers the SelectString cmdlet, among others.

The third traditional textparsing tool, Awk, lets you to chop a line of text into more intuitive groupings. PowerShell offers the Split() method on strings, but that lacks some of the power you usually need to break a string into groups.

The ConvertTextObject script presented in Example 57 lets you convert text streams into a set of objects that represent those text elements according to the rules you specify. From there, you can use all of PowerShell’s objectbased tools, which gives you even more power than you would get with the textbased equivalents.

Example 57. ConvertTextObject.ps1

############################################################################## ## ## ConvertTextObject.ps1 Convert a simple string into a custom PowerShell ## object.

##

##
Parameters:

##

##
[string] Delimiter

##
If specified, gives the .NET Regular Expression with which to

##
split the string. The script generates properties for the

##
resulting object out of the elements resulting from this split.

##
If not specified, defaults to splitting on the maximum amount

##
of whitespace: "\s+", as long as ParseExpression is not

##
specified either.

##

##
[string] ParseExpression

##
If specified, gives the .NET Regular Expression with which to

##
parse the string. The script generates properties for the

##
resulting object out of the groups captured by this regular

##
expression.

##

Example 57. ConvertTextObject.ps1 (continued)

##
** NOTE ** Delimiter and ParseExpression are mutually exclusive.

##

##
[string[]] PropertyName

##
If specified, the script will pair the names from this object

##
definition with the elements from the parsed string. If not

##
specified (or the generated object contains more properties

##
than you specify,) the script uses property names in the

##
pattern of Property1,Property2,...,PropertyN

##

##
[type[]] PropertyType

##
If specified, the script will pair the types from this list with

##
the properties from the parsed string. If not specified (or the

##
generated object contains more properties than you specify,) the

##
script sets the properties to be of type [string]

##

##

##
Example usage:

##
"Hello World" | ConvertTextObject

##
Generates an Object with "Property1=Hello" and "Property2=World"

##

##
"Hello World" | ConvertTextObject Delimiter "ll"

##
Generates an Object with "Property1=He" and "Property2=o World"

##

##
"Hello World" | ConvertTextObject ParseExpression "He(ll.*o)r(ld)"

##
Generates an Object with "Property1=llo Wo" and "Property2=ld"

##

##
"Hello World" | ConvertTextObject PropertyName FirstWord,SecondWord

##
Generates an Object with "FirstWord=Hello" and "SecondWord=World

##

##
"123 456" | ConvertTextObject PropertyType $([string],[int])

##
Generates an Object with "Property1=123" and "Property2=456"

##
The second property is an integer, as opposed to a string

##

##############################################################################

param( [string] $delimiter, [string] $parseExpression, [string[]] $propertyName, [type[]] $propertyType )

function Main( $inputObjects, $parseExpression, $propertyType, $propertyName, $delimiter)

{ $delimiterSpecified = [bool] $delimiter $parseExpressionSpecified = [bool] $parseExpression

## If they've specified both ParseExpression and Delimiter, show usage if($delimiterSpecified and $parseExpressionSpecified)

Example 57. ConvertTextObject.ps1 (continued)

{ Usage return

}

## If they enter no parameters, assume a default delimiter of whitespace if(not $($delimiterSpecified or $parseExpressionSpecified)) {

$delimiter = "\s+" $delimiterSpecified = $true }

## Cycle through the $inputObjects, and parse it into objects foreach($inputObject in $inputObjects) {

if(not $inputObject) { $inputObject = "" } foreach($inputLine in $inputObject.ToString()) {

ParseTextObject $inputLine $delimiter $parseExpression ` $propertyType $propertyName } } }

function Usage

{ "Usage: " " ConvertTextObject" " ConvertTextObject ParseExpression parseExpression " +

"[PropertyName propertyName] [PropertyType propertyType]" " ConvertTextObject Delimiter delimiter " + "[PropertyName propertyName] [PropertyType propertyType]" return }

## Function definition ParseTextObject. ## Perform the heavylifting parse a string into its components. ## for each component, add it as a note to the Object that we return function ParseTextObject {

param( $textInput, $delimiter, $parseExpression, $propertyTypes, $propertyNames)

$parseExpressionSpecified = not $delimiter

$returnObject = NewObject PSObject

$matches = $null $matchCount = 0 if($parseExpressionSpecified) {

Example 57. ConvertTextObject.ps1 (continued)

## Populates the matches variable by default [void] ($textInput match $parseExpression) $matchCount = $matches.Count

} else {

$matches = [Regex]::Split($textInput, $delimiter) $matchCount = $matches.Length }

$counter = 0 if($parseExpressionSpecified) { $counter++ } for(; $counter lt $matchCount; $counter++) {

$propertyName = "None" $propertyType = [string]

## Parse by Expression if($parseExpressionSpecified) {

$propertyName = "Property$counter"

## Get the property name if($counter le $propertyNames.Length) {

if($propertyName[$counter 1]) { $propertyName = $propertyNames[$counter 1] } }

## Get the property value if($counter le $propertyTypes.Length) {

if($types[$counter 1]) { $propertyType = $propertyTypes[$counter 1] }

} } ## Parse by delimiter else {

$propertyName = "Property$($counter + 1)"

## Get the property name if($counter lt $propertyNames.Length) {

if($propertyNames[$counter]) { $propertyName = $propertyNames[$counter] } }

Example 57. ConvertTextObject.ps1 (continued)

## Get the property value if($counter lt $propertyTypes.Length) {

if($propertyTypes[$counter]) { $propertyType = $propertyTypes[$counter] } } }

AddNote $returnObject $propertyName ` ($matches[$counter] as $propertyType) }

$returnObject }

## Add a note to an object function AddNote ($object, $name, $value) {

$object | AddMember NoteProperty $name $value }

Main $input $parseExpression $propertyType $propertyName $delimiter

Generate Large Reports and Text Streams in Windows PowerShell

Problem

You want to write a script that generates a large report or large amount of data

Solution

The best approach to generating a large amount of data is to take advantage of PowerShell’s streaming behavior whenever possible. Opt for solutions that pipeline data between commands:

GetChildItem C:\ *.txt Recurse | OutFile c:\temp\AllTextFiles.txt

rather than collect the output at each stage:

$files = GetChildItem C:\ *.txt –Recurse $files | OutFile c:\temp\AllTextFiles.txt

If your script generates a large text report (and streaming is not an option), use the StringBuilder class:

$output = NewObject System.Text.StringBuilder

GetChildItem C:\ *.txt Recurse |

ForeachObject { [void] $output.Append($_.FullName + "`n") }

$output.ToString()

rather than simple text concatenation:

$output = "" GetChildItem C:\ *.txt Recurse | ForeachObject { $output += $_.FullName } $output

Discussion

In PowerShell, combining commands in a pipeline is a fundamental concept. As scripts and cmdlets generate output, PowerShell passes that output to the next command in the pipeline as soon as it can. In the solution, the GetChildItem commands that retrieve all text files on the C: drive take a very long time to complete. However, since they begin to generate data almost immediately, PowerShell can pass that data onto the next command as soon as the GetChildItem cmdlet produces it. This is true of any commands that generate or consume data and is called streaming. The pipeline completes almost as soon as the GetChildItem cmdlet finishes producing its data and uses memory very efficiently as it does so.

The second GetChildItem example (that collects its data) prevents PowerShell from taking advantage of this streaming opportunity. It first stores all the files in an array, which, because of the amount of data, takes a long time and enormous amount of memory. Then, it sends all those objects into the output file, which takes a long time as well.

However, most commands can consume data produced by the pipeline directly, as illustrated by the OutFile cmdlet. For those commands, PowerShell provides streaming behavior as long as you combine the commands into a pipeline. For commands that do not support data coming from the pipeline directly, the ForeachObject cmdlet (with the aliases of foreach and %) lets you to still work with each piece of data as the previous command produces it, as shown in the StringBuilder example.

Creating large text reports

When you generate large reports, it is common to store the entire report into a string, and then write that string out to a file once the script completes. You can usually accomplish this most effectively by streaming the text directly to its destination (a file or the screen), but sometimes this is not possible.

Since PowerShell makes it so easy to add more text to the end of a string (as in $output += $_.FullName), many initially opt for that approach. This works great for smalltomedium strings, but causes significant performance problems for large strings.

As an example of this performance difference, compare the following:

PS >MeasureCommand { >> $output = NewObject Text.StringBuilder

>> 1..10000 | >> ForeachObject { $output.Append("Hello World") } >> } >>

(...) TotalSeconds : 2.3471592

PS >MeasureCommand { >> $output = "" >> 1..10000 | ForeachObject { $output += "Hello World" } >> } >>

(...) TotalSeconds : 4.9884882

In the .NET Framework (and therefore PowerShell), strings never change after you create them. When you add more text to the end of a string, PowerShell has to build a new string by combining the two smaller strings. This operation takes a long time for large strings, which is why the .NET Framework includes the System.Text. StringBuilder class. Unlike normal strings, the StringBuilder class assumes that you will modify its data—an assumption that allows it to adapt to change much more efficiently.

Program: Query a SQL Data Source

It is often helpful to perform ad hoc queries and commands against a data source such as a SQL server, Access database, or even an Excel spreadsheet. This is especially true when you want to take data from one system and put it in another, or when you want to bring the data into your PowerShell environment for detailed interactive manipulation or processing.

Although you can directly access each of these data sources in PowerShell (through its support of the .NET Framework), each data source requires a unique and hard to remember syntax. Example 155 makes working with these SQLbased data sources both consistent and powerful.

Example 155. InvokeSqlCommand.ps1

############################################################################## ## ## InvokeSqlCommand.ps1 ## ## Return the results of a SQL query or operation ## ## ie:

##

##
## Use Windows authentication

##
InvokeSqlCommand.ps1 Sql "SELECT TOP 10 * FROM Orders"

##

##
## Use SQL Authentication

##
$cred = GetCredential

##
InvokeSqlCommand.ps1 Sql "SELECT TOP 10 * FROM Orders" Cred $cred

##

##
## Perform an update

##
$server = "MYSERVER"

##
$database = "Master"

##
$sql = "UPDATE Orders SET EmployeeID = 6 WHERE OrderID = 10248"

##
InvokeSqlCommand $server $database $sql

##

##
$sql = "EXEC SalesByCategory 'Beverages'"

##
InvokeSqlCommand Sql $sql

##

##
## Access an access database

##
InvokeSqlCommand (ResolvePath access_test.mdb) Sql "SELECT * from Users"

##

##
## Access an excel file

##
InvokeSqlCommand (ResolvePath xls_test.xls) Sql 'SELECT * from [Sheet1$]'

##

##############################################################################

param( [string] $dataSource = ".\SQLEXPRESS", [string] $database = "Northwind", [string] $sqlCommand = $(throw "Please specify a query."), [System.Management.Automation.PsCredential] $credential

)

## Prepare the authentication information. By default, we pick ## Windows authentication $authentication = "Integrated Security=SSPI;"

## If the user supplies a credential, then they want SQL ## authentication if($credential) {

$plainCred = $credential.GetNetworkCredential() $authentication = ("uid={0};pwd={1};" f $plainCred.Username,$plainCred.Password) }

Example 155. InvokeSqlCommand.ps1 (continued)

## Prepare the connection string out of the information they ## provide $connectionString = "Provider=sqloledb; " +

"Data Source=$dataSource; " + "Initial Catalog=$database; " + "$authentication; "

## If they specify an Access database or Excel file as the connection ## source, modify the connection string to connect to that data source if($dataSource match '\.xls$|\.mdb$') {

$connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=$dataSource; "

if($dataSource match '\.xls$') { $connectionString += 'Extended Properties="Excel 8.0;"; '

## Generate an error if they didn't specify the sheet name properly if($sqlCommand notmatch '\[.+\$\]') {

$error = 'Sheet names should be surrounded by square brackets, and ' +

'have a dollar sign at the end: [Sheet1$]' WriteError $error return

} } }

## Connect to the data source and open it $connection = NewObject System.Data.OleDb.OleDbConnection $connectionString $command = NewObject System.Data.OleDb.OleDbCommand $sqlCommand,$connection $connection.Open()

## Fetch the results, and close the connection $adapter = NewObject System.Data.OleDb.OleDbDataAdapter $command $dataset = NewObject System.Data.DataSet [void] $adapter.Fill($dataSet) $connection.Close()

## Return all of the rows from their query $dataSet.Tables | SelectObject Expand Rows