Read Windows Server 2008 R2 Unleashed Online
Authors: Noel Morimoto
example, there’s no reason, other than to encode a script, to create aliases consisting
of only two letters.
724
CHAPTER 21
Automating Tasks Using PowerShell Scripting
Scopes
A scope is a logical boundary in PowerShell that isolates the use of functions and vari-
ables. Scopes can be defined as global, local, script, and private. They function in a hierar-
chy in which scope information is inherited downward. For example, the local scope can
read the global scope, but the global scope can’t read information from the local scope.
Scopes and their use are described in the following sections.
Global
As the name indicates, a global scope applies to an entire PowerShell instance. Global
scope data is inherited by all child scopes, so any commands, functions, or scripts that run
make use of variables defined in the global scope. However, global scopes are not shared
between different instances of PowerShell.
The following example shows the $Processes variable being defined as a global variable in
the ListProcesses function. Because the $Processes variable is being defined globally,
checking $Processes.Count after ListProcesses completes returns a count of the number
of active processes at the time ListProcesses was executed:
PS C:\> function ListProcesses {$Global:Processes = get-process}
PS C:\> ListProcesses
ptg
PS C:\> $Processes.Count
37
NOTE
In PowerShell, an explicit scope indicator can be used to determine the scope a vari-
able resides in. For instance, if a variable is to reside in the global scope, it should be
defined as $Global:variablename. If an explicit scope indicator isn’t used, a variable
resides in the current scope for which it’s defined.
Local
A local scope is created dynamically each time a function, filter, or script runs. After a
local scope has finished running, information in it is discarded. A local scope can read
information from the global scope but can’t make changes to it.
The following example shows the locally scoped variable $Processes being defined in the
ListProcesses function. After ListProcesses finishes running, the $Processes variable
no longer contains any data because it was defined only in the ListProcesses function.
Notice how checking $Processes.Count after the ListProcesses function is finished
produces no results:
PS C:\> function ListProcesses {$Processes = get-process}
PS C:\> ListProcesses
PS C:\> $Processes.Count
PS C:\>
Understanding the PowerShell Basics
725
Script
A script scope is created whenever a script file runs and is discarded when the script
21
finishes running. To see an example of how a script scope works, create the following
script and save it as ListProcesses.ps1:
$Processes = get-process
write-host “Here is the first process:” -Foregroundcolor Yellow
$Processes[0]
After creating the script file, run it from a PowerShell session. The output should look
similar to this example:
PS C:\> .\ListProcesses.ps1
Here is the first process:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
105 5 1992 4128 32 916 alg
PS C:\> $Processes[0]
Cannot index into a null array.
ptg
At line:1 char:12
+ $Processes[0 <<<< ]
PS C:\>
Notice that when the ListProcesses.ps1 script runs, information about the first process
object in the $Processes variable is written to the console. However, when you try to
access information in the $Processes variable from the console, an error is returned
because the $Processes variable is valid only in the script scope. When the script finishes
running, that scope and all its contents are discarded.
What if an administrator wants to use a script in a pipeline or access it as a library file for
common functions? Normally, this isn’t possible because PowerShell discards a script
scope whenever a script finishes running. Luckily, PowerShell supports the dot-sourcing
technique, a term that originally came from UNIX. Dot sourcing a script file tells
PowerShell to load a script scope into the calling parent’s scope.
To dot source a script file, simply prefix the script name with a period (dot) when running
the script, as shown here:
PS C:\> . .\coolscript.ps1
Private
A private scope is similar to a local scope, with one key difference: Definitions in the
private scope aren’t inherited by any child scopes.
The following example shows the privately scoped variable $Processes defined in the
ListProcesses function. Notice that during execution of the ListProcesses function, the
726
CHAPTER 21
Automating Tasks Using PowerShell Scripting
$Processes variable isn’t available to the child scope represented by the script block
enclosed by { and } in lines 6–9.
PS C:\> function ListProcesses {$Private:Processes = get-process
>> write-host “Here is the first process:” -Foregroundcolor Yellow
>> $Processes[0]
>> write-host
>>>> &{
>> write-host “Here it is again:” -Foregroundcolor Yellow
>> $Processes[0]
>> }
>> }
>>PS C:\> ListProcesses
Here is the first process:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
105 5 1992 4128 32 916 alg
Here it is again:
ptg
Cannot index into a null array.
At line:7 char:20
+ $Processes[0 <<<< ]
PS C:\>
This example works because it uses the & call operator. With this call operator, you can
execute fragments of script code in an isolated local scope. This technique is helpful for
isolating a script block and its variables from a parent scope or, as in this example, isolat-
ing a privately scoped variable from a script block.
Providers and Drives
Most computer systems are used to store data, often in a structure such as a file system.
Because of the amount of data stored in these structures, processing and finding informa-
tion can be unwieldy. Most shells have interfaces, or providers, for interacting with data
stores in a predictable, set manner. PowerShell also has a set of providers for presenting
the contents of data stores through a core set of cmdlets. You can then use these cmdlets
to browse, navigate, and manipulate data from stores through a common interface. To get
a list of the core cmdlets, use the following command:
PS C:\> help about_core_commands
...
ChildItem CMDLETS
Get-ChildItem
CONTENT CMDLETS
Add-Content
Clear-Content
Understanding the PowerShell Basics
727
Get-Content
Set-Content
21
...
To view built-in PowerShell providers, use the following command:
PS C:\> get-psprovider
Name Capabilities Drives
---- ------------ ------
WSMan Credentials {WSMan}
Alias ShouldProcess {Alias}
Environment ShouldProcess {Env}
FileSystem Filter, ShouldProcess {C, D, E}
Function ShouldProcess {Function}
Registry ShouldProcess, Transactions {HKLM, HKCU}
Variable ShouldProcess {Variable}
Certificate ShouldProcess {cert}
PS C:\>
ptg
The preceding list displays not only built-in providers, but also the drives each provider
currently supports. A drive is an entity that a provider uses to represent a data store
through which data is made available to the PowerShell session. For example, the Registry
provider creates a PowerShell drive for the HKEY_LOCAL_MACHINE and
HKEY_CURRENT_USER Registry hives.
To see a list of all current PowerShell drives, use the following command:
PS C:\> get-psdrive
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
Alias Alias
C 68.50 107.00 FileSystem C:\
cert Certificate \
D 8.98 1.83 FileSystem D:\
E FileSystem E:\
Env Environment
Function Function
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
Variable Variable
WSMan WSMan
PS C:\>
728
CHAPTER 21
Automating Tasks Using PowerShell Scripting
Profiles
A PowerShell profile is a saved collection of settings for customizing the PowerShell envi-
ronment. There are four types of profiles, loaded in a specific order each time PowerShell
starts. The following sections explain these profile types, where they should be located,
and the order in which they are loaded.
The All Users Profile
This profile is located in %windir%\system32\windowspowershell\v1.0\profile.ps1.
Settings in the All Users profile are applied to all PowerShell users on the current machine.
If you plan to configure PowerShell settings across the board for users on a machine, this
is the profile to use.
The All Users Host-Specific Profile
This profile is located in %windir%\system32\windowspowershell\v1.0\ShellID_
profile.ps1. Settings in the All Users host-specific profile are applied to all users of the
current shell (by default, the PowerShell console). PowerShell supports the concept of
multiple shells or hosts. For example, the PowerShell console is a host and the one most
users use exclusively. However, other applications can call an instance of the PowerShell
runtime to access and run PowerShell commands and scripts. An application that does
this is called a hosting application and uses a host-specific profile to control the
ptg
PowerShell configuration. The host-specific profile name is reflected by the host’s ShellID.
In the PowerShell console, the ShellID is the following:
PS C:\ $ShellId
Microsoft.PowerShell
PS C:\
Putting this together, the PowerShell console’s All Users host-specific profile is named
Microsoft.PowerShell_profile.ps1. For other hosts, the ShellID and All Users host-specific
profile names are different. For example, the PowerShell Analyzer (www.powershellana-
lyzer.com) is a PowerShell host that acts as a rich graphical interface for the PowerShell
environment. Its ShellID is PowerShellAnalyzer.PSA, and its All Users host-specific profile
name is PowerShellAnalyzer.PSA_profile.ps1.
The Current User’s Profile
This profile is located in %userprofile%\My Documents\WindowsPowerShell\profile.ps1.
Users who want to control their own profile settings can use the current user’s profile.
Settings in this profile are applied only to the user’s current PowerShell session and don’t
affect any other users.
The Current User’s Host-Specific Profile
This profile is located in %userprofile%\My Documents\WindowsPowerShell\ShellID_
profile.ps1. Like the All Users host-specific profile, this profile type loads settings for
the current shell. However, the settings are user specific.
Understanding the PowerShell Basics
729
NOTE
21
When PowerShell is started for the first time, you might see a message indicating that
scripts are disabled and no profiles are loaded. This behavior can be modified by
changing the PowerShell execution policy.
Security
When WSH was released with Windows 98, it was a godsend for Windows administrators
who wanted the same automation capabilities as their UNIX brethren. At the same time,
virus writers quickly discovered that WSH also opened up a large attack vector against
Windows systems.
Almost anything on a Windows system can be automated and controlled by using WSH,
which is an advantage for administrators. However, WSH doesn’t provide any security in
script execution. If given a script, WSH runs it. Where the script comes from or its purpose
doesn’t matter. With this behavior, WSH became known more as a security vulnerability
than an automation tool.
ptg
Execution Policies
Because of past criticisms of WSH’s security, when the PowerShell team set out to build a
Microsoft shell, the team decided to include an execution policy to mitigate the security
threats posed by malicious code. An execution policy defines restrictions on how
PowerShell allows scripts to run or what configuration files can be loaded. PowerShell has