The file might resemble something like this: SQLTBWS SQLTBXP SQLTBW7 SQLPROD1 SQLPROD2 By using theGet-Contentcmdlet, this list of servers can easily be brought into a PowerShell variabl
Trang 1PS> [string] $counter = ‘7’
Variables can also contain objects or collections Collections are just a group of objects, such as an array
It’s easy to create a collection just by assigning a list of values to a variable, like this:
PS> $stuff = 1,2,4,6,8 The list of numbers is grouped into a collection of integer objects and placed in the variable$stuff
Individual elements within the collection can be accessed by using their ordinal number:
PS> $stuff[2]
4 PS>
Addressing individual elements of an array is nice, but the power of collections is realized by being able
to iterate through the members of the collection PowerShell offers two versions offoreachlogic, the
first being a cmdlet to which a collection is piped, like this:
PS> $stuff | foreach-object {write-output $_}
1 2 4 6 8 Notice the variable$_, which is defined as the current object in the set of objects the cmdlet is iterating
through The other version is theforeachlanguage element, which enables naming of the member:
PS> foreach ($thing in $stuff) {write-output $thing}
1 2 4 6 8 Now, within the script block operating on the members of the collection, conditions can be checked
For example, if the script should only operate on elements with a value not greater than 4, the script
would read as follows:
PS> $stuff | foreach-object { if ($_ -gt 4) {break}
else {write-output $_}}
1 2 4 Table 7-1 shows most of the comparison operators within PowerShell
Trang 2TABLE 7-1
Comparison Operators
Operator Description
-le less than or equal to
-ge greater than or equal to
-like like wildcard pattern matching
Suppose you have a text file calledservers.txtthat contains a list of servers, one server name per
line The file might resemble something like this:
SQLTBWS
SQLTBXP
SQLTBW7
SQLPROD1
SQLPROD2
By using theGet-Contentcmdlet, this list of servers can easily be brought into a PowerShell variable
as a collection by issuing the following command:
$servers = Get-Content ‘servers.txt’
Each element in the collection can be addressed by its ordinal number, so the first item is referenced
by$servers[0], the second item by$servers[1], and so on This ability to create collections will
come in handy later in this discussion
Comments, always good in any language, are specified in PowerShell 1.0 by using the pound (#)
charac-ter, with the comments following that character on the line (PowerShell 2.0 will support an additional
comment operator, enabling multi-line comments, e.g.,<# Multi-Line Comment #>.)
Referencing the$serverscollection, each element in the collection is a string object, and string objects
have aLengthproperty, so the element can be tested to determine whether it has a value using the
following commands:
if ($servers[0].Length -gt 0) {
#work
}
Trang 3To help with logic flow, PowerShell provides a group of ‘‘object’’ cmdlets, as shown in Table 7-3.
For example, in the earlier example of the$serverscollection, the collection can be iterated through
using the following commands:
$servers | Foreach-Object { Write-Output $_
}
TABLE 7-2
Control Flow Commands
#work }
#work }
"Val1" {
#work }
"Val2" {
#work } }
#work } Until ($val -eq "target")
#work } While ($val -eq "target")
While While ($val -eq "target") {
#work }
Trang 4TABLE 7-3
Object cmdlets in PowerShell
ForEach-Object % Executes once for each member in the collection
Where-Object ? Filters objects based on conditions
Select-Object select Pipes only the specified properties
Sort-Object sort Sorts the objects
Tee-Object tee Sends the objects in two directions
These cmdlets will be very useful when scripting For example, they enable iteration through a collection
of properties in objects, as shown here:
get-service | where-object {$_.Status -eq "Running"}
The preceding produced the following results:
-Running 1-vmsrvc Virtual Machine Additions Services
Running AeLookupSvc Application Experience Lookup Service
Running Browser Computer Browser
Running CryptSvc Cryptographic Services
Running DcomLaunch DCOM Server Process Launcher
Running dmserver Logical Disk Manager
Running Dnscache DNS Client
Running ERSvc Error Reporting Service
Running Eventlog Event Log
Running EventSystem COM+ Event System
Running helpsvc Help and Support
Running HTTPFilter HTTP SSL
Running lanmanserver Server
Running lanmanworkstation Workstation
Running LmHosts TCP/IP NetBIOS Helper
Running MSDTC Distributed Transaction Coordinator
Running MsDtsServer100 SQL Server Integration Services 10.0
Running MSOLAP$INST01 SQL Server Analysis Services (INST01)
Running MSSQL$INST01 SQL Server (INST01)
Running MSSQL$INST02 SQL Server (INST02)
In this example, PowerShell sifts through the current services on the system and returns only those
services that are currently running Note the braces ({}) delimiting the operation of thewhere-object
Trang 5script content between them, are referred to as a script block.
Most important, help is always available using the Get-Help cmdlet It can also be called using the help or man aliases Either way, help can be obtained for any cmdlet; and it will return syntax, description, and related links Get-Help has options to return normal, full, detailed, or
examples.
To get help on any cmdlet, type Get-Help followed by the cmdlet To get help onGet-Help, type the
following:
PS> Get-Help Get-Help NAME
Get-Help SYNOPSIS Displays information about Windows PowerShell cmdlets and concepts
SYNTAX Get-Help [[-name] <string>] [-component <string[]>]
[-functionality <string[]>] [-role <string[]>] [-category <stri ng[]>] [-full] [<CommonParameters>]
Get-Help [[-name] <string>] [-component <string[]>]
[-functionality <string[]>] [-role <string[]>] [-category <stri ng[]>] [-detailed] [<CommonParameters>]
Get-Help [[-name] <string>] [-component <string[]>]
[-functionality <string[]>] [-role <string[]>] [-category <stri ng[]>] [-examples] [<CommonParameters>]
Get-Help [[-name] <string>] [-component <string[]>]
[-functionality <string[]>] [-role <string[]>] [-category <stri ng[]>] [-parameter <string>] [<CommonParameters>]
DETAILED DESCRIPTION The Get-Help cmdlet displays information about Windows PowerShell cmdlets and concepts You can also use "Help {<cm
dlet name> | <topic-name>" or "<cmdlet-name> /?"
"Help" displays the help topics one page at a time The "/?" disp lays help for cmdlets on a single page
RELATED LINKS Get-Command Get-PSDrive Get-Member
Trang 6For more information, type: "get-help Get-Help -detailed"
For technical information, type: "get-help Get-Help -full"
Creating scripts
While it’s sometimes useful to enter ad hoc commands into PowerShell to evaluate system state or other
information, the real power of PowerShell comes with writing scripts A good collection of scripts to
perform normal administrative functions is a sign of an effective administrator
For example it’s a good idea to have information about the physical servers on which SQL Server is
running Windows Management Instrumentation (WMI) provides this information through simple
queries, available to PowerShell through theGet-WMIObjectcmdlet (aliased asgwmi) A simple
script to gather this information is shown in Listing 7-1 In this script, four different WMI classes are
polled, and their results are piped into theselect-objectcmdlet (aliased asselect), where the
specific properties needed are retrieved Those results are then piped into theformat-listcmdlet for
presentation
LISTING 7-1
Get System Info
#getsysinfo.ps1
# Use WMI queries to retrieve information about the computer, operating
# system and disk devices
gwmi -query "select * from Win32_ComputerSystem" | select Name, Model,
Manufacturer, Description, DNSHostName, Domain, DomainRole,
PartOfDomain, NumberOfProcessors, SystemType, TotalPhysicalMemory,
UserName, Workgroup | format-list
gwmi -query "select * from Win32_OperatingSystem" | select Name,
Version, FreePhysicalMemory, OSLanguage, OSProductSuite, OSType,
ServicePackMajorVersion, ServicePackMinorVersion | format-list
gwmi -query "select * from Win32_PhysicalMemory" | select Name,
Capacity, DeviceLocator, Tag | format-table -Autosize
gwmi -query "select * from Win32_LogicalDisk where
DriveType=3" | select Name, FreeSpace, Size | format-table -Autosize
When this script is run on a server it will return results like this:
Manufacturer : Microsoft Corporation
Description : AT/AT COMPATIBLE
DNSHostName : sqltbws
Trang 7DomainRole : 2 PartOfDomain : False NumberOfProcessors : 1 SystemType : X86-based PC TotalPhysicalMemory : 1073192960 UserName : SQLTBWS\Administrator
Name : Microsoft Windows Server 2003 R2 Enterprise Edition|C:\WINDOWS|
FreePhysicalMemory : 340816
OSProductSuite : 274
ServicePackMajorVersion : 2 ServicePackMinorVersion : 0 Name Capacity DeviceLocator Tag - -Physical Memory 16777216 DIMM1 Physical Memory 0 Physical Memory 16777216 DIMM2 Physical Memory 1 Physical Memory 16777216 DIMM2 Physical Memory 2 Physical Memory 16777216 DIMM2 Physical Memory 3 Name FreeSpace Size
- C: 60792463360 68705730560 E: 16124747776 17173573632 F: 17087807488 17173573632 Anytime a set of commands will be used repeatedly, it’s useful to encapsulate those commands in a
function Functions must be defined before they can be used, because PowerShell is an interpretive
language and doesn’t ‘‘read ahead’’ to see what functions might be defined later
Best Practice
Begin all PowerShell scripts with the set of functions that will be used in the script, with the main part
of the script listed at the very end PowerShell cannot forward-reference function code, so the function
must have been read before it’s called in the main part of the script
Trang 8The basic format of a function is as follows:
Function MyFunction {
#work } This, of course, doesn’t do anything Between the braces the real work needs to be coded, but most
often functions need parameters You can add functions in a number of ways, but two ways are most
commonly used The first, most obvious, format is like this:
Function MyFunction ($param) {
#work } This works fine, but the recommended method is to use aparamblock within the function, which
enables the specification of multiple parameters, the specification of the data type for each parameter,
and even default values for each parameter:
Function MyFunction {
param (
[int]$x = 7, [int]$y = 9 )
#work } TheGetSysInfo.ps1script shown in Listing 7-1 is useful when run on an individual server, but
would be even more so if it could be run against all servers in the data center By putting the working
code fromGetSysInfo.ps1into a function (and adding the – computernameparameter to each
Get-WMIObjectcommand within the function to specify which server to run the command against) it’s
possible to iterate through the set of servers in the$serverscollection discussed earlier
Listing 7-2 shows exactly how to do this The function with the calls to the four WMI classes is defined
first, followed by the main part of the script Note that before attempting to get the server information,
the main part of the script uses the WMI classWin32_PingStatusto determine whether the
com-puter is reachable through the network This saves time because the script doesn’t attempt to run the
four main queries against a server that doesn’t respond
LISTING 7-2
ServerStatus.ps1
function getwmiinfo ($svr) {
gwmi -query "select * from
Win32_ComputerSystem" -computername $svr | select Name,
Model, Manufacturer, Description, DNSHostName,
Domain, DomainRole, PartOfDomain, NumberOfProcessors,
Trang 9Workgroup | format-list gwmi -query "select * from
Win32_OperatingSystem" -computername $svr | select Name, Version, FreePhysicalMemory, OSLanguage, OSProductSuite, OSType, ServicePackMajorVersion,
ServicePackMinorVersion | format-list gwmi -query "select * from
Win32_PhysicalMemory" -computername $svr | select Name, Capacity, DeviceLocator, Tag | format-table -Autosize gwmi -query "select * from Win32_LogicalDisk
where DriveType=3" -computername $svr | select Name, FreeSpace, Size | format-table -Autosize
}
$servers = get-content ‘servers.txt’
foreach ($server in $servers) {
$results = gwmi -query "select StatusCode from Win32_PingStatus
where Address = ‘$server’"
$responds = $false
foreach ($result in $results) {
if ($result.statuscode -eq 0) {
$responds = $true break
}
}
if ($responds) {
getwmiinfo $server
} else {
Write-Output "$server does not respond"
}
}
The results of this script look like this:
Manufacturer : Microsoft Corporation Description : AT/AT COMPATIBLE DNSHostName : sqltbws
Trang 10PartOfDomain : False
NumberOfProcessors : 1
SystemType : X86-based PC
TotalPhysicalMemory : 1073192960
UserName : SQLTBWS\Administrator
Name : Microsoft Windows Server 2003 R2
Enterprise Edition|C:\WINDOWS|
FreePhysicalMemory : 341320
OSProductSuite : 274
ServicePackMajorVersion : 2
ServicePackMinorVersion : 0
Name Capacity DeviceLocator Tag
-
-Physical Memory 16777216 DIMM1 Physical Memory 0
Physical Memory 16777216 DIMM2 Physical Memory 1
Physical Memory 16777216 DIMM2 Physical Memory 2
Physical Memory 16777216 DIMM2 Physical Memory 3
Name FreeSpace Size
-
C: 60792463360 68705730560
E: 16124743680 17173573632
F: 17088528384 17173573632
SQLTBXP does not respond
SQLTBW7 does not respond
SQLPROD1 does not respond
SQLPROD2 does not respond
The day after I wrote this script for this book, I was on a consulting engagement
that called for collecting server data for more than 70 SQL Server instances,
and I was able to use this script as a starting point for my data collection After I completed
the script I wrote an article about it for the Simple-Talk newsletter The article is available at
www.simple-talk.com/sql/database-administration/let-powershell-do-an-inventory-of-your-servers/
Errors are handled using aTrapfunction The Try-Catch mechanism for error handling will be added
to PowerShell in version 2.0, but for the time being the best method is to create aTrapfunction at the
beginning of the script, like this:
Function Error_Handler {
$errmsg = "Error Category: " + $error[0].CategoryInfo.Category
$errmsg = $errmsg + " Error Object: " + $error[0].TargetObject