6. Running PowerShell Scripts
Creating and running
PowerShell scripts has a couple of hurdles that can stop you in your
tracks if you don't know what they are. They aren't hard to overcome as
long as you recognize them. The two issues are
These issues are explained in the following sections.
6.1. PowerShell Execution Policy
Microsoft has embraced a secure-by-default mindset, and this is reflected in the PowerShell Execution Policy. This policy has several settings that you can modify to allow or disallow the execution of different types of scripts.
If you don't modify the
policy, you'll find that each attempt to execute a PowerShell script
will result in an error that says, "The execution of scripts is disabled
on this system." This message is telling you that the Execution Policy
is set to Restricted, the default setting. There are several possible
settings for the Execution Policy:
Restricted
The Restricted setting
prevents any scripts from being executed and is the default setting.
You can still execute individual PowerShell commands.
RemoteSigned
It's common to
change the Execution Policy to RemoteSigned. This will let you execute
any scripts on your local system but will prevent scripts that don't
have a digital signature from being executed remotely, such as over an
Internet connection.
Signed scripts have a digital
signature added to them, which is associated with a code-signing
certificate from a trusted publisher. The idea is that if a script is
signed with a certificate, you can identify the writer. Since malicious
scriptwriters don't want to be known or identified, they won't sign
their scripts.
AllSigned
This is a little more
secure than RemoteSigned. While RemoteSigned will allow the execution of
unsigned local scripts, AllSigned will not allow the execution of any
unsigned scripts. All scripts must be signed.
Unrestricted
Just as it sounds,
Unrestricted allows the execution of any scripts. This setting will warn
you before running scripts that are downloaded from the Internet.
Bypass
This is similar to
Unrestricted in that it allows the execution of any scripts; however, it
does not give any warnings. This would be used when an application is
using scripts, and it wouldn't be able to respond to any warnings.
You can use the following script to determine the current setting for the Execution Policy:
Get-ExecutionPolicy
You can change the policy using the Set-ExecutionPolicy
cmdlet with the name of the policy. You must be running Windows
PowerShell with administrative permissions to change the Execution
Policy.
Click Start => All Programs => Accessories => Windows PowerShell, right-click Windows PowerShell, and select Run As Administrator. You may notice that this defaults to a different location. Instead of your user name folder, it defaults to C:\Windows\System32. Enter the following command: Set-ExecutionPolicy RemoteSigned
When prompted, type Y and press Enter. Enter the following command to verify the policy is changed: Get-ExecutionPolicy
Close the Administrator Windows PowerShell window. If desired, open Windows PowerShell as a regular user.
|
6.2. Looping
Any programming language
has the ability to loop through code. This allows it to perform the same
action repeatedly without the programmer having to write the code
repeatedly.
You'll run across several
looping commands in scripts. They are very often used to loop through
collections, which are explained in the next section. In each of these
looping commands, the code that is executed is included in the curly
brackets.
The ForEach command is commonly used in PowerShell in the following format:
ForEach (Item in a collection) {
Code to execute
}
You'll see the ForEach loop used in this article.
Another looping command that is commonly used is the Do While loop. It will execute code as long as (or while) a condition is met. The format is
Do {
Code to execute
}
While (condition is true)
Here's a simple example of how a Do While loop could be used to execute code. It creates a variable named $counter and populates it with the number 0. It then outputs the value using the Write-Host line, increments the counter using $counter++, and then checks the value in the While line.
$counter = 0
Do {
Write-Host "Value is" $counter
$counter++
}
While ($counter -lt 5)
The output of the code will be five lines that look like this:
Value is 0
Value is 1
Value is 2
Value is 3
Value is 4
6.3. Collections
A collection is a type of an array used to hold information on objects.
Imagine that you have a bucket,
and you've placed 10 marbles in the bucket. These marbles are objects
and very likely they are different. Some may be clear and others cloudy,
some steel and others glass, some small and others large. You get the
idea. Each of these marbles has specific properties that can be used to
describe it.
Now if you created a
spreadsheet, you could list each of the marbles and their associated
properties. Later, if you wanted information on a specific marble, you
could access the spreadsheet and retrieve the information. In this
context, the data in the spreadsheet is the collection that represents
the actual objects.
Similarly, with PowerShell you
can create a collection of information on objects. You can then use
code to loop through the collection.
As an example, consider this code:
$colLog = get-EventLog -list
The Get-EventLog -list will list information on the EventLogs on a system in five columns: Max(K), Retain, OverflowAction, Entries, and Log. The $colLog = will store the result of the command in the variable named $colLog. While the get-EventLog command will retrieve many logs, imagine there are only five. The collection would look like Table 2.
Table 2. A Collection of Logs
Max(K) | Retain | OverFlowAction | Entries | Log |
---|
20,480 | 0 | OverwriteAsNeeded | 46,266 | Application |
20,480 | 0 | OverwriteAsNeeded | 53,635 | System |
20,480 | 7 | OverwriteOlder | 2,345 | Security |
15,360 | 0 | OverwriteAsNeeded | 1,206 | Session |
15,360 | 0 | OverwriteAsNeeded | 453 | Windows PowerShell |
Data stored in the collection can be accessed and retrieved, and it is commonly done so using a ForEach
command. As an example, the following script will store the data in a
collection and then loop through the collection to retrieve it:
$colLog = get-EventLog -list
ForEach ($Item in $colLog) {
Write-0utput $Item
}
This is actually no different than just executing the Get-EventLog -List
command. However, the real strength of the collection is that you can
manipulate it. If you were interested in only some of the columns, you
could pick and choose what is displayed.
Imagine that all you really
want to know is the log name and its size. These two columns have
headers of Log and Max(K) (though Max(K) is identified as
MaximumKilobytes). Each item in the collection can be identified using
these names. Because the script uses $Item as the variable name to hold the collection, the columns would be identified as $Item.Property. In other words, $Item.Log retrieves the log name and $Item.MaximumKilobytes retrieves the Max(K) value.
$colLog = get-EventLog -list
ForEach ($Item in $ColLog) {
Write-Output $Item.Log, $Item.MaximumKilobytes
}
However, this is pretty messy.
Instead of outputting the data as a table, it sends each item on a
single line.
6.4. Creating a PowerShell Script
With the basics out of the way,
it's time to walk through the creation of some basic scripts using the
Windows PowerShell Integrated Scripting environment.
Launch the Windows PowerShell ISE. Click in the top pane (labeled Untitled1.ps1), type in the following text, and press Enter: Get-EventLog -List
Notice the command doesn't execute when you press Enter but instead starts a line 2. Click
the green button to run the script. You can also press F5. You'll see
the result displayed in the middle pane. If there is a syntax error,
you'll see a red text error instead of black text as the output. If you do see a red error message, look for these characters: <<<<. They will often point to the specific string of text that is causing the problem. Click the Save icon on the menu (or press Ctrl+S). Browse to a folder named C:\Scripts (create it if you need to), and change Untitled to LogInfo to save the script as C:\Scripts\LogInfo.ps1. Move your cursor to the bottom of the screen, type C:\Scripts\LogInfo.ps1, and press Enter. The script will execute. Change the current location to the script file with this command: Set-Location C:\Scripts.
Try to run the script with this command (it will fail): LogInfo.ps1
Even though the script is in the current path, PowerShell needs a hint to find it. Press the up arrow, and modify the last command so it looks like this: .\LogInfo.ps1
The .\ characters tell PowerShell the script is in the current path.
|
At this point, you've
created and executed a PowerShell script. If it's the first time, feel
free to yell out, "Woo hoo!" However, you may want to create a script
that has a little more value.
Click File => New to create a blank area to work with a new script. Enter the following text, and press F5 to execute it. get-service | select-object name, status
This lists all the services on the system by name and their current status (such as running or stopped). Instead
of outputting this data to the screen, you can store it in a
collection. The following command will store the result in a collection
named $colService. Notice that you don't have to retype the entire command, but instead you just add $colService = to the beginning of the line: $colService = Get-service | select-object name, status
Now add a ForEach loop to loop through the collection. The loop will check each service stored in the collection and then use the Write-Host cmdlet to display only the services with a status equal to "running". ForEach ($item in $colService) { If ($item.status -eq "running") {write-host $item.name} }
|
Similarly, you could use the same method to list all the services that are stopped by simply changing "running" to "stopped" in the script.
While this example does show how a ForEach
loop can be used to loop through a collection, it's not the only way to
get a list of running services. As with most scripts, there's more than
one way to get what you need. For example, the following one-liner
could also be used to get a list of running services:
Get-Service | Select name, status | where {$_.status -eq "running"}
6.5. Documenting Scripts
It's worth your time to
spend a few minutes documenting your scripts. You can do it while you're
creating the scripts or spend a couple of minutes when you've finished,
but in either case it'll save you some headaches later. Documentation
is done by adding comment lines, which start with a # character in PowerShell (or by using REM, short for remarks, in batch files).
When you write a good
script, it'll automate a job for you, and you may not have to touch it
again for six months. I don't know about you, but if I haven't touched
something in six months, I just don't remember it as well. However, by
adding a few lines here and there, I'm quickly able to remember what I
did and why I did it.
Documentation in scripts starts with a few lines at the beginning that could be like this:
#Script written Sep 2009 by Darril Gibson
#Purpose. Checks status of service.
#Expects Display Name of service
# as a parameter ($Name)
Notes can also be embedded
within the script. This is often done when the script includes large
blocks of code. These notes are easy to add while you're writing and
debugging the script and don't need to be complex.