Need to perform the same Windows PowerShell task over and over and over again? Ever thought about putting the commands for carrying out this task into a script?
In this world there’s always something to be glad about. (Hey, it’s true – just ask Pollyanna.) For example, system administration can occasionally be a complicated job that requires a lot of time and effort. But we can all be glad that we have Windows PowerShell to make the job easier. Sometimes PowerShell requires that you type a lot of information from the command line. But we can be glad we have shortcuts (see Windows PowerShell Shortcuts) and aliases (see Windows PowerShell Aliases) to make this quicker and simpler. And we can be especially glad that we have Windows PowerShell scripts.
Suppose you find yourself typing the same commands over and over practically every time you start up Windows PowerShell. Wouldn’t it be much easier to simply type a file name at the command prompt than to retype this potentially long and complicated command over and over? Of course it would. Or suppose you want to schedule a command to run at a particular time. You can’t always be there at 2:00 AM to type in the command, but with a script you can schedule the command. Or, as another example, maybe you have a long series of commands that need to be run in sequence in order to perform a somewhat complicated set of actions. A script will make this a very simple task to accomplish.
In this section we’re going to explain everything you need to know to get started writing scripts in Windows PowerShell. But before we start writing scripts, you need to know how some of the pieces work.
Objects, Properties, and Variables
If you read the Getting Started with Windows PowerShell article you know (or at least you should know) that cmdlets are the heart and soul of Windows PowerShell. What we didn’t tell you – because you were just getting started and we didn’t want to scare you away – was that there’s a basic principal of software development that lies behind the workings of every cmdlet: the object.
Hang on, don’t go yet. Yes, we said “software development,” and you’re not a software developer, you’re a system administrator. Big difference, whole different skill set, we know. But don’t worry, you don’t need to be a developer to work with PowerShell (that would kind of defeat the whole purpose, wouldn’t it?), and you don’t need to be a developer to understand objects. But as a system administrator who’s going to be working with Windows PowerShell, objects are a very useful thing to know about.
So your first question is probably, ”Why do I need to know about objects?” (Either that or “What is an object?”, but we’re sure you’ll quickly get around to the “Why do I care?” question.) If we do our jobs right (which is never a guarantee, but, in this case, we’ll try), the reason you need to know will become clear as you read through this. As to what an object is, well, that’s easy: an object is a thing.
See, that was painless, wasn’t it?
What’s that? What kind of thing? Well…anything. A boat is an object. A chair is an object. A donut is an object. (Mmmm, donuts.) A process is an object. A shoe is an object. A –
Huh? Yes, as a matter of fact we did say that a process is an object. Okay, let’s start over. We’ll start with the chair. (We’d start with the donut, but some of us tend to lose our focus when donuts are mentioned.) A chair is an object – it’s a thing that exists and has various qualities. Take a look at our chair:
As we mentioned, an object has various qualities. Let’s look at some of the qualities of this chair: it has a color; it has arms; it has feet; it has a cushion; it has a back. These are all qualities that you can use to identify this particular chair, like this:
In the world of computers, these qualities are known as properties. For this particular chair, the Color property has a value of Purple. You might prefer to choose a chair with a Color property of Green. A chair either has arms or it doesn’t: this chair does, so the Arms property is True. Some stools have only three legs, while some rolling chairs don’t really have legs, they have wheels. Our chair has four legs, so our Legs property has a value of 4.
Computers work the same way. A process is a thing: it has qualities, or properties, that identify that particular process. Try running the Get-Process cmdlet in Windows PowerShell. Run this from the command prompt and press Enter:
Depending on the processes running on your computer, you’ll get output similar to the following:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
71 3 1640 5544 52 0.08 2276 AcroBroker
27 1 376 1616 11 1136 AEADISRV
38 2 732 2348 21 1332 agrsmsvc
57 3 1480 4944 53 0.06 3472 apdproxy
105 3 1100 4108 37 1172 Ati2evxx
137 4 1820 6216 42 1636 Ati2evxx
111 4 11812 14520 43 1352 audiodg
314 12 22684 3536 158 0.48 2824 CCC
517 19 33476 11236 198 2.37 4400 CCC
1066 16 38848 50692 148 2956 CcmExec
1219 27 34580 49268 250 24.94 3936 communicator
723 6 1752 5528 86 560 csrss
The output shows you some of the properties of a process. For example, you can see that each process has a Name. The first process in the list has a Name of AcroBroker. A process also has an Id; here the first process in the list has an Id of 2276. And so on.
As a general rule, you can find objects based on specific properties by using cmdlet parameters. For example, if you want to find the process with the Id 2824 you’d type this at the command prompt:
Get-Process –Id 2340
And, at least on our test machine, this is what we got back:
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
125 5 5928 9744 60 2340 svchost
Another way to work with properties is by assigning the output of your commands to a variable. A variable is simply a place to save data in memory so you can later manipulate that data. In Windows PowerShell, all variables begin with a dollar sign ($). Let’s assign the results of our Get-Process call to a variable we’ll call $p:
$p = Get-Process –Id 2340
The first thing you’ll notice when you run this command is that you don’t get any output. (Keep in mind you might not have a process with Id 2340 on your computer. Run Get-Process and choose an Id that will work on your computer.) That’s because, instead of going to the display, all the information about this process object has been stored in the variable $p. How do we know this for sure? Just type this at the command prompt:
Surprise! There’s the output. Now let’s find out what the name of this process is. To do that, we simply add a dot (.) plus the name of the property to the end of our variable name, like this:
Type this at the command prompt and you’ll see the name of the process with the Id 2340.
You can also use variables for values other than objects. For example, type this at the command prompt:
$a = 2 + 3
Now type $a at the command prompt and press Enter. Here’s what you’ll see:
You can continue to use this variable for as long as this Windows PowerShell session is open. Try this:
PS C:\scripts> $b = 8
PS C:\scripts> $b - $a
Aren’t you glad we showed you that?
Creating a Script
Now that we have a general idea of how objects, properties and variables work, let’s talk a little bit about actually creating a script. A script in Windows PowerShell is simply a text file with a .PS1 file extension. Let’s create our first script.
Start by opening Notepad. (Simply type notepad.exe at the PowerShell command prompt; or, from the Start menu, select All Programs, Accessories, then select Notepad.) Do that, and you’ll have an empty Notepad window:
All you need to do here is type in the commands you would normally type at the command prompt. For example, type this into Notepad:
$a = Get-Process –Name svchost
Write-Host "Here are all the processes with the name svchost:"
Before you run this script you need to save it. Select Save from the File menu (or press Ctrl+S). In the Save dialog box, enter the name you’d like to save the script to. Before you select Save, however (you didn’t just go ahead and try to save did you?), make sure you save the file with a .PS1 file extension. If you simply type test.ps1 in the File Name box of the Save dialog box you’ll end up with a file with the name test.ps1.txt. That’s because Notepad will helpfully assume that you’re saving a text file and will append the .txt extension for you. To keep this from happening, either enclose the file name in double quotes (“test.ps1”) or type test.ps1 in the File Name box and select All Files (*.*) from the Save As Type drop-down list box.
You’ve just created your very first Windows PowerShell script. Congratulations! Aren’t you glad you know how to write a script now?
Note: Keep in mind that what we showed you here isn’t the only thing you can do in a script. Anything you can do from the command prompt you can put in a script. A script can be anywhere from one line to hundreds of lines long. It all depends on what you want it to do and how complicated you want to get.
Running a Script
Now that you’ve created your script you probably want to run it and see it in action. So you open up Windows Explorer to the folder where you saved your script file, you double-click the .PS1 file, sit back, and wait for the magic to happen.
As it turns out, however, your script opened up in Notepad.
Hmmm, instead of running, your script opened up in Notepad. Interesting, but not exactly what you had in mind. “Oh wait,” you think. “I get it: you probably have to run Windows PowerShell before you can run a Windows PowerShell script. OK, that makes sense.” And so, with that in mind, you open up Windows PowerShell and type the path to the .PS1 file at the command prompt. You press ENTER and wait for the magic to happen.
As it turned out, however, this is what happens:
File C:\scripts\test.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing” for more details.
Wow, how … nice. A new command shell and scripting environment that doesn’t even let you run scripts. What will those guys at Microsoft think of next?
Listen, don’t panic; believe it or not, everything is fine. You just need to learn a few little tricks for running Windows PowerShell scripts.
Running Scripts From Within Windows PowerShell
Let’s start with running scripts from within Windows PowerShell itself. (Which, truth be told, is probably the most common way to run Windows PowerShell scripts.) Why do you get weird error messages when you try to run a script? That’s easy. The security settings built into Windows PowerShell include something called the “execution policy;” the execution policy determines how (or if) PowerShell runs scripts. By default, PowerShell’s execution policy is set to Restricted; that means that scripts – including those you write yourself – won’t run. Period.
Note. You can verify the settings for your execution policy by typing the following at the PowerShell command prompt and then pressing ENTER:
Now, admittedly, this might seem a bit severe. After all, what’s the point of having a scripting environment if you can’t even run scripts with it? But that’s OK. If you don’t like the default execution policy (and you probably won’t) then just go ahead and change it. For example, suppose you want to configure PowerShell to run – without question – any scripts that you write yourself, but to run scripts downloaded from the Internet only if those scripts have been signed by a trusted publisher. In that case, use this command to set your execution policy to RemoteSigned:
Alternatively, you can set the execution policy to AllSigned (all scripts, including those you write yourself, must be signed by a trusted publisher) or Bypass (all scripts will run, regardless of where they come from and whether or not they’ve been signed).
See? No need to panic at all, is there?
Note. Not sure what we mean by “signing scripts?” Then open up PowerShell, type the following, and press ENTER:
After you change your execution policy settings it becomes possible to run scripts. However, you still might run into problems. For example, suppose you change directories from your Windows PowerShell home directory to C:\Scripts (something you can do by typing cd C:\Scripts). As it turns out, the C:\Scripts folder contains a script named Test.ps1. With that in mind you type the following and then press ENTER:
And here’s the response you get:
The term 'test.ps1' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
We know what you’re thinking: didn’t we just change the execution policy? Yes, we did. However, this has nothing to do with the execution policy. Instead, it has to do with the way that PowerShell handles file paths. In general, you need to type the complete file path in order to run a script. That’s true regardless of your location within the file system. It doesn’t matter if you’re in C:\Scripts; you still need to type the following:
Now, we said “in general” because there are a couple of exceptions to this rule. For example, if the script happens to live in the current directory you can start it up using the .\ notation, like so:
Note. There’s no space between the .\ and the script name.
And while PowerShell won’t search the current directory for scripts it will search all of the folders found in your Windows PATH environment variable. What does that mean? That means that, if the folder C:\Scripts is in your path, then you can run the script using this command:
But be careful here. Suppose C:\Scripts is not in your Windows path. However, suppose the folder D:\Archive is in the path, and that folder also contains a script named Test.ps1. If you’re in the C:\Scripts directory and you simply type Test.ps1 and press ENTER, guess which script will run? You got it: PowerShell won’t run the script in C:\Scripts, but it will run the script found in D:\Archive. That’s because D:\Archive is in your path.
Just something to keep in mind.
Note. Just for the heck of it, here’s a command that retrieves your Windows PATH environment variable and displays it in a readable fashion:
$a = $env:path; $a.Split(";")
Even More About File Paths
Now we know that all we have to do is type in the full path to the script file and we’ll never have to worry about getting our scripts to run, right? Right.
Well, almost right. There’s still the matter of scripts whose path name includes a blank space. For example, suppose you have a script stored in the folder C:\My Scripts. Try typing this command and see what happens:
Of course, by now you’ve come to expect the unexpected, haven’t you? Here’s what you get back:
The term 'C:\My' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
This one you were able to figure out on your own, weren’t you? Yes, just like good old Cmd.exe, PowerShell has problems parsing file paths that include blank spaces. (In part because blank spaces are how you separate command-line arguments used when you started the script.) In Cmd.exe you can work around this problem by enclosing the path in double quotes. Logically enough, you try the same thing in PowerShell:
And here’s what you get back:
Um, OK …. You try it again. And here’s what you get back:
You try it – well, look, there’s no point in trying it again: no matter how many times you try this command, PowerShell will simply display the exact same string value you typed in. If you actually want to execute that string value (that is, if you want to run the script whose path is enclosed in double quotes) you need to preface the path with the Call operator (the ampersand). You know, like this:
& "C:\My Scripts\Test.ps1"
Note. With this particular command you can either leave a space between the ampersand and the path name or not leave a space between the ampersand and the path name; it doesn’t matter.
To summarize, here’s how you run scripts from within Windows PowerShell:
· Make sure you’ve changed your execution policy. By default, PowerShell won’t run scripts at all, no matter how you specify the path.
· To run a script, specify the entire file path, or either: 1) use the .\ notation to run a script in the current directory or 2) put the folder where the script resides in your Windows path.
· If your file path includes blank spaces, enclose the path in double quote marks and preface the path with an ampersand.
And, yes, that all takes some getting used to. However, you will get used to it. (To make life easier for you, we recommend that you keep all your scripts in one folder, such as C:\Scripts, and add that folder to your Windows path.)
Note. So can you use PowerShell to add a folder to your Windows Path? Sure; here’s a command (that we won’t bother to explain in this introductory article) that tacks the folder C:\Scripts onto the end of your Windows path:
$env:path = $env:path + ";c:\scripts"
Bonus: “Dot Sourcing” a Script
Admittedly, up to this point the news hasn’t been all that good: you can’t run a PowerShell script by double-clicking the script icon; PowerShell doesn’t automatically look for scripts in the current working directory; spaces in path names can cause all sorts of problems; etc. etc. Because of that, let’s take a moment to talk about one very cool feature of Windows PowerShell scripting: dot sourcing.
Suppose we have a very simple PowerShell script like this one:
$a = 5
$b = 10
$c = $a + $b
Suppose we run this script, then type $c at the command prompt. What do you think we’ll get back? If you guessed nothing, then you guessed correctly.
In other words, we don’t get back anything at all. Which, again, should come as no great surprise. And we know what you’re thinking, you’re thinking: “Come on, shouldn’t this be leading us somewhere?”
Yes, it should. And believe it or not, it is. Let’s run our PowerShell script again, only this time let’s “dot source” it; that is, let’s type a period and a blank space and then type the path to the script file. For example:
When we run the script nothing will seem to happen; that’s because we didn’t include any code for displaying the value of $C. But now try typing $C at the command prompt . Here’s what you’ll get back:
Good heavens! Was this a lucky guess on the part of the PowerShell console, or is this some sort of magic?
Surprisingly enough, it’s neither. Instead, this is dot sourcing. When you dot source a script (that is, when you start the script by prefacing the path to the script file with a dot and a blank space) any variables used in the script become global variables that are available in multiple scopes. What does that mean? Well, a script happens to represent one scope; the console window happens to represent another scope. We started the script Test.ps1 by dot sourcing it; that means that the variable $C remains “alive” after the script ends. In turn, that means that this variable can be accessed via the command window. In addition, these variables can be accessed from other scripts. (Or at least from other scripts started from this same instance of Windows PowerShell.)
Suppose we have a second script (Test2.ps1) that does nothing more than display the value of the variable $c:
Look what happens when we run Test2.ps1 (even if we don’t use dot sourcing when starting the script):
Cool. Because $c is a global variable everyone has access to it.
And, trust us here: this is pretty cool. For example, suppose you have a database that you periodically like to muck around with. If you wanted to, you could write an elaborate script that includes each and every analysis you might ever want to run on that data. Alternatively, you could write a very simple little script that merely connects to the database and returns the data (stored in a variable). If you dot source that script on startup you can then sit at the command prompt and muck around with the data all you want. That’s because you have full access to the script variables and their values.
Note. OK, sure, this could cause you a few problems as well, especially if you tend to use the same variable names in all your scripts. But that’s OK; if you ever need to wipe out the variable $C just run the following command (note that, with the Remove-Variable cmdlet, we need to leave off the $ when indicating the variable to be removed):
Play around with this a little bit and you’ll start to see how useful dot sourcing can be.
Running Scripts Without Starting Windows PowerShell
We realize that it’s been awhile, but way back at the start of this article we tried running a Windows PowerShell script by double-clicking a .PS1 file. That didn’t go quite the way we had hoped: instead of running the script all we managed to do was open the script file in Notepad. Interestingly enough, that’s the way it’s supposed to work: as a security measure you can’t start a PowerShell script by double-clicking a .PS1 file. So apparently that means that you do have to start PowerShell before you can run a PowerShell script.
In a somewhat roundabout way, that’s technically true. However, that doesn’t mean that you can’t start a PowerShell script from a shortcut or from the Run dialog box; likewise you can run a PowerShell script as a scheduled task. The secret? Instead of calling the script you need to call the PowerShell executable file, and then pass the script path as an argument to PowerShell.exe. For example, in the Run dialog box you might type a command like this:
powershell.exe -noexit &’c:\scripts\test.ps1’
There are actually three parts to this command:
• Powershell.exe, the Windows PowerShell executable.
• -noexit, an optional parameter that tells the PowerShell console to remain open after the script finishes. Like we said, this is optional: if we leave it out the script will still run. However, the console window will close the moment the script finishes, meaning we won’t have the chance to view any data that gets displayed to the screen. Incidentally, the –noexit parameter must immediately follow the call to the PowerShell executable. Otherwise the parameter will be ignored and the window will close anyway.
• C:\Scripts\Test.ps1, the path to the script file.
What if the path to the script file contains blank spaces? In that case you need to do the ampersand trick we showed you earlier; in addition, you need to enclose the script path in single quote marks, like so:
powershell.exe -noexit &'c:\my scripts\test.ps1'
Strange, but true!
Note. Here’s an interesting variation on this same theme: instead of starting PowerShell and asking it to run a particular script you can start PowerShell and ask it to run a particular command. For example, typing the following in the Run dialog box not only starts PowerShell but also causes it to run the Get-ChildItem cmdlet against the folder C:\Scripts:
powershell.exe -noexit get-childitem c:\scripts
It’s possible to get even more elaborate when starting Windows PowerShell, but this will do for now. If you’d like more information on PowerShell startup options just type powershell.exe /? from either the Windows PowerShell or the Cmd.exe command prompt.
So is there a catch here? Unfortunately, there is. If you are trying to carry out a task that requires administrator privileges then you can’t start Windows PowerShell from the Run dialog box on either Vista, Windows Server 2008, or Windows 7. OK, check that: you can start PowerShell, but the command you are trying to run will fail. For example, if this is your script, it will fail; that’s because changing the execution policy requires administrator privileges:
Fortunately, there’s a workaround. If you’d like to always be able to start PowerShell from the Run dialog box on one of these operating systems then you should check out the Script Elevation PowerToys for Windows Vista (http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx).
See? That Wasn’t So Bad
Admittedly, running Windows PowerShell scripts might not be as straightforward and clearcut as it could be. On the other hand, it won’t take you long to catch on, and you’ll soon be running PowerShell scripts with the best of them. Most important, you’ll also be able to say things like, “You know, you really ought to dot source that script when you run it.” If that doesn’t impress your colleagues then nothing will.
Aren’t you glad you know how to do all this now?
Just checked -- Get-ExecutionPolicy gave RemoteSigned and local scripts are just running, as they should.
This is on 2k8 R2 again but it is out of the box psh (as in no MSIT "additions" :-) so chances are that they fixed in in general. 2k8 R2 ships with psh 2.0 as "intrinsic" as in it won't let you instal the one from msdn - you have to "turn on the feature" instead :-[[
@ZX: When you install Communications Server we actually modify the Execution Policy to 'RemoteSigned' (unless it is already looser). This is because we install and use some scripts ourselves, and you'd generally expect them to work out of the box. (Incidentally, we've verified with the PowerShell team that they consider it OK to change the policy on install like this - Exchange 2010 does something similar.)
As far as I know, the default out-of-the-box on a fresh Windows 2008 R2 server is still 'Restricted'.
Thanks for the blog. Good information!