Working with Windows PowerShell to retrieve information from remote computers
I’ve kind of got used to Ed Wilson’s chaotic style of presenting. He must be clever to achieve such a position inside Microsoft, but each of his presentations looks and feels as though he has done no preparation what so ever. It really comes over as off the cuff with all the problems associated with that. Here we are in part 4; in real time this is the fourth consecutive day he has presented and again it really feels like he hasn’t bothered with his practice run.
Half of the first 5 minutes were devoted to him rambling about the scripting games that his wife had just signed up for on the
HeyScriptingGuy website, and the other half was watching him try to find the web page where she did it. Unfortunately he couldn’t find the page. He must have been sweating because he was jumping from page to page and hammering the page up/down buttons looking for it to no avail. The video feed struggled with the speed and so the top half of the screen was of one page and the bottom half was the scroll window from a couple of page-up’s ago. Then he started mumbling again and quickly changed the subject. What a fiasco!
After that embarrassment we get into what today’s lesson is all about.
Windows
PowerShell ships with an ISE which stands for
Integrated Scripting Environment or
Integrated Script Editor or
Incredible Script Editor. The third option is clearly a joke but which of the 2 first choices is correct Mr. Microsoft man? Surely he must know but has forgotten.
Apparently there are some other script editors out there. There was a brief stutter where he tried to say a list but just at the last second decided he couldn’t think of any off the top of his head and quickly changed the subject - nice! “But they are really cool and have a lot of feature".
The nice thing about Windows
Powershell ISE is that it comes bundled for free and “also it works pretty good” - and that would seem to be it!
Just as
PowerShell comes in a 32-bit version as well as the 64-bit version, so too does
PowerShell ISE. Most of the time it won’t make any difference which one you use. Occasionally there will be incompatibilities with things like (some) COM commands so you will have to detect the failure and rerun the script with the correct bit version. There are a couple of
HeyScriptingGuy articles on this subject.
PowerShell ISE has 3 sections. The top section is the script section which works like the command line in that you can hit the tab button to get command completion. Ed says that in his scripts, he likes to split the command up over several lines and he uses the pipe symbol as his new line point. What he doesn’t quite make clear is that you can do this in the command line as well. The command line interpreter knows when it’s the end of the statement because that’s what command line interpreters do!
You could have typed it into the third panel which is the
execution pane or the
command pane. Press
F5 or the green triangle to run the script.
Get-Process |
Sort-Object -Property cpu |
Format-Table -Property name, cpu
The results appear in the middle pane.
PS C:\> Get-Process |
Sort-Object -Property cpu |
Format-Table -Property name, cpu
Name CPU
---- ---
System
Idle
audiodg
svchost 0
svchost 0
atiesrxx 0
EngineServer 0
nsd 0.0156001
svchost 0.0156001
conhost 0.0156001
...
You will notice that some of the
CPU values are missing, so we’ll use a filter to remove those entries. We know from previous lessons that we need the
Where-Object cmdlet, but where do we add it? We can’t add it after the
Format-Table because the
Format-Table is just a text formatter. The text loses all its record or object information so the filter command won’t have anything to run against. We could add it after the
Soft-Object but this would be a waste because we would spend time sorting entries that we are going to get rid of. Obviously we can’t do it before the
Get-Process! so the best place is after the it as close to the source as possible. “Don’t forget to close the curly brackets, because it’s really easy to forget” - wow thanks Ed!
We can use the wind-screen wiper button in the tool bar to clear Output Pane. All that back and forth with the mouse was driving is a bit amateur-ish, I found it much easier to
control+d
to jump to the execution pane type
cls
and hit return, then
control+i
; that way your hands never leave the keyboard and it’s a bit more fluid.
Name CPU
---- ---
nsd 0.0156001
conhost 0.0156001
svchost 0.0156001
wuauclt 0.0312002
lockmgr 0.0312002
Ed said “We’ve only gotten the CPU information from the processes that are reporting that information” and then moves on. I think it’s important to notice that that is not what has been returned. You will notice that all the processes that reported zero CPU are missing too. His
$_.cpu
filter is a standard true/false condition so empty and zero are both false and this will skew your results. A better condition would have been:
Where-Object { $_.cpu -or $_.cpu -eq 0 }
This condition says keep everything that is not blank or zero or everything that is zero. It’s a bit convoluted but it could be seen as similar to the standard test for empty in Java:
var != null && “".equals(var)
We can change the order of the list by placing the caret immediately before the pipe symbol on the Sort-Object line and typing
-D then hitting the tab key to complete the word
Descending, then hit
F5
to run the new statement. This will return the list in reverse order.
In my representations of the output I have removed most of the white space so it will fit on the page without wrapping but you can do this as part of the
Format-Table cmdlet by adding the auto size switch:
Format-Table -Property name, cpu -AutoSize
One thing to note is that the auto size option can add a lot of extra running time to the cmdlet as it has to read in all the results before it can start displaying any. Only once all the results are held can the cmdlet determine the correct width for all the columns. Without the option, it can just do an offset from the width of the page and takes a best guess. For example if you were listing all the files on your hard drive it would take a while for the statement to start returning anything:
Get-ChildItem c:\ -Recurse | Format-Table length, name -AutoSize
If you don’t use auto size you will see the columns shift as it learns more about the lengths of each column.
The tab in the scripting (top) pane still reads “Untitled1.ps1*” and so it’s not considered a script yet (I think that’s open to debate: is a book a book if it’s in electronic form?) so he saves it using the blue disc icon in the tool bar and calls it
SortProcessesByCPU.ps1
“and then I say groovy” (I thought we were done with that!)
Now that we have saved the script, when we try to run it “three, two, one, boom we got an error” and it writes
Failed in the status bar at the bottom of the window.
PS C:\> C:\SortProcessesByCPU.ps1
File C:\SortProcessesByCPU.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
At line:0 char:0
“Everything’s cool. The ‘help about_signing’ is almost a little bogus, y’know, what we really need to see is help about the execution policy and you don’t have to sign your scripts if you don’t want to. In fact, in general I don’t see a whole lot of value from that.” - you heard it hear first!
We could run the command in
PowerShell ISE but it only stopped us running the script when we saved it. So go to the bottom pane which he has started referring to as the immediate pane and type:
PS C:\> Get-ExecutionPolicy
Restricted
Restricted is the default (as installed).
Enabling Script Execution
In order to enable script execution on your system there are a number of things you can do.
- Run the Set-Execution cmdlet with administrator rights which gives it permission to write to the registry.
- You can just hack the registry but they don’t recommend doing that!
- You can set it in the Enterprise wide Group Policy.
Enabling script execution is a security
convenience and not a security
feature. “The reason I say this, other than the fact it’s true is, is that there is a by-pass execution policy parameter, cos this is actually real cool and it’s a feature that was requested by a lot of our customers. They wanted the scripts to be locked down by default or secured but they still wanted the ability to run scripts". There followed an example of the difference between a convenience and a feature. So to cut a really long story short. A convenience is like the seat belt alarm in your car that reminds you to put your seat belt on. That’s a convenience to help you remember where as the actual seat belt and the air bags are the security features.
The alarm might be a bit annoying but it won’t save your life where as the other features will.
Sounds like bollox to me but there you have it.
So in summary it’s a feature that everyone asked for, that’s not a feature, that switches off all the security which doesn’t add anything anyway. Clear? Let’s see if the examples explain this any better!
We are going to open a command window and navigate to the place where we saved our
SortProcessByCPU.ps1
and we are going to use the execution policy bypass to run our script:
powershell -executionpolicy bypass -noexit -file .\SortProcessesByCPU.ps1
This command will bypass the security and run the script, without exiting, and drop us on the command line running in by-pass mode. If you type:
PS C:\> Get-ExecutionPolicy
Bypass
From this newly created command line you can run scripts just by typing their name. The shell will use the extension to launch the appropriate helper application. This works the same as running it in the old
command.exe
. The script will use the execution policy that the shell window has, so in this case that would be
Bypass.
There is another got-cha hidden away here as well. The order in which you specify the switches on the command line (for the
powershel.exe
command) makes a difference! So if is doesn’t work you will have to re-organise the switches until it does work!!!! To simplify, I think you should set the execution policy first so that it knows how to interpret the rest of the command line switches. Brilliant!!!
If we type
Set-ExecutionPolicy into the command window it will give us the list of valid enumerations:
cmdlet Set-ExecutionPolicy at command pipeline position 1
Supply values for the following parameters:
ExecutionPolicy:
Set-ExecutionPolicy : Cannot bind parameter 'ExecutionPolicy'. Cannot convert value "" to type "Microsoft.PowerShell.Ex
ecutionPolicy" due to invalid enumeration values. Specify one of the following enumeration values and try again. The po
ssible enumeration values are "Unrestricted, RemoteSigned, AllSigned, Restricted, Default, Bypass, Undefined".
At line:1 char:20
+ Set-ExecutionPolicy <<<<
+ CategoryInfo : InvalidArgument: (:) [Set-ExecutionPolicy], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
The list of valid enumerations is:
- AllSigned - all scripts are signed with a code signing certificate. If you are using Microsoft PKI you just add a new PKI certificate type of code signature, then you can issue it out to your network administrators. This mode will not stop users running any script they like because they can just run it in Bypass mode!
- RemoteSigned - (The important bit of Ed’s explanation for this was misleading, confusing and, after I had confirmed it in documentation, dead wrong!) So here is what it really does: scripts from an untrusted location are going to have to be signed. This works in combination with the Internet Option’s zones. There is a chance that the file servers on your network are in an untrusted zone. So you would need to use Group Policy to add those servers to the trusted zone so that they don’t need to be signed. Local file system PowerShell scripts are considered trusted so this option means that you won’t need to sign local files either.
- Restricted - Does not load configuration files or run scripts. On my system this is the default.
- Unrestricted - Ed sets everything to this because “it works". If you are downloading scripts from the Internet or an untrusted zone you will still get a warning about downloading PowerShell scripts, but that message comes from Internet Explorer. He said using ByPass will stop this. He then talks about downloading a modules from the Internet and when it comes down each item in the zip file is marked as from an untrusted network so it’s constantly prompting him for validation. The best way to get around this is to remove the internet from the untrusted zone - WHAT!?!?!?!? I don’t know about you, but that sounds like a *really* bad idea to me, but this is the kind of useless dangerous and misleading advice that Ed has been feeding us during these presentations.
- Default - Not listed in the documentation, so I’m assuming the the default (Restricted) is just the default default and that the Default option is what ever the default has been set to, which may vary from system to system.
- Bypass - Nothing is blocked and there are no warnings or prompts.
- Undefined - Removes the currently assigned execution policy from the current scope. This parameter will not remove an execution policy that is set in a Group Policy scope.
When you try to change the policy it throws up the warning:
Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic. Do you want to change the execution
policy?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
If you don’t have administrator rights it will fail anyway with
Access to the registry is denied. You should right click on the
PowerShell icon to
Run As Administrator. Depending on your set up you might get the following error:
sPowerShell\Microsoft.PowerShell_profile.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
At line:1 char:2
+ . <<<< 'C:\Users\me\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException
You will only get this message if you have a
PowerShell profile. The
PowerShell profile is a script that gets run when you open a
PowerShell command prompt. It may contain customisations like aliases or import module commands. The default location of the
PowerShell profile has the nice-and-easy-to-remember path of:
C:\Users\<user>\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
. The path to you profile is also held in the
$profile
environment variable.
If you are running with an execution policy of
Restricted then you will get the
scripts disabled error as it tries to run your profile.
From our administrator
PowerShell command window we are going to set the execution policy to
Unrestricted. Do you remember that totally unrelated problem in the event log lesson that filtered the list of logs and “using the force” made it work. So Ed starts saying “use the force” again, so that the
Set-ExecutionPolicy won’t prompt us for confirmation. How are they related other then saying “use the force". He is
so irritating!
Set-ExecutionPolicy restricted -Force
The
Set-ExecutionPolicy cmdlet run by the administrator updates the registry to make this change permanent. Now all new
PowerShell command windows, as well as existing open
PowerShell command windows will be set to the new policy of
Unrestricted.
So what is a Windows
PowerShell Script? It’s a bunch of Window’s
PowerShell commands a bit like a batch file, “but there are language thingys that can be used as well". He explains that a language thingy is a semi-colon that can separate commands on the same line.
PS C:\> echo hi ; echo bye
hi
bye
Ed describes his way of working and I can’t believe my ears! He has
PowerShell console open on one monitor and
Powershell ISE open on the other. He practices commands in the
PowerShell console and when it is correct he copies it into the
Powershell ISE. How does he do any work at this pace? He spent most of yesterday explaining that the
Powershell ISE has incorporated into it the
PowerShell console so either he has forgotten or he’s a bit simple.
Jeffery Hicks came up with this easy way to create
PowerShell scripts and as Ed Willson didn’t do any preparation for his tutorials he thought he’ll just pilfer it (but at least he passed on the credit).
We are going to populate the command history, read it out into a file and run it as a script. With all the lessons we have done so far you should have no problem doing this yourself.
We will start off by clearing our history with
Clear-History and creating some more. The examples that Ed gave were generic and a bit useless so let’s try and come up with some that you already know that you might find more useful. So enter the following at the command prompt which will give us the top 5 processes that have the most CPU time, the 5 most recent application log entries and all the running services respectively.
Get-Process | Sort-Object { $_.cpu } -Descending | Select-Object -First 5 | Format-Table cpu, processname
Get-EventLog -LogName application -Newest 5
Get-Service | Where-Object { $_.status -eq "Running" }
As you can see from my
Get-History I made a few typos so we’ll have to clean it up.
Id CommandLine
-- -----------
27 Get-Process
28 Get-Process | Sort-Object $_.cpu
29 Get-Process | Get-Member
30 Get-Process | Sort-Object { $_.cpu }
31 Get-Process | Sort-Object { $_.cpu }
32 Get-Process | Get-Member
33 Get-Process | Sort-Object { $_.cpu } -Descending
34 Get-Process | Sort-Object { $_.cpu } -Descending | Select-Object -First 10
35 Get-Process | Sort-Object { $_.cpu } -Descending | Select-Object -First 10 | Format-Table processname
36 Get-Process | Sort-Object { $_.cpu } -Descending | Select-Object -First 10 | Format-Table cpu, processname
37 Get-EventLog -LogName application
38 #Get-Process | Sort-Object { $_.cpu } -Descending | Select-Object -First 10 | Format-Table cpu, processname
39 Get-EventLog -LogName application -Newest 5
40 Get-Service
41 Get-Service | Get-Member
42 Get-Service | Where-Object { $_.status -eq "Running" }
If you remember from last time, each history element is an object so we can do a
Get-Member to see what properties are available.
Get-History | Get-Member
TypeName: Microsoft.PowerShell.Commands.HistoryInfo
Name MemberType Definition
---- ---------- ----------
Clone Method Microsoft.PowerShell.Commands.HistoryInfo Clone()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CommandLine Property System.String CommandLine {get;}
EndExecutionTime Property System.DateTime EndExecutionTime {get;}
ExecutionStatus Property System.Management.Automation.Runspaces.PipelineState ExecutionStatus {get;}
Id Property System.Int64 Id {get;}
StartExecutionTime Property System.DateTime StartExecutionTime {get;}
There is a property called
CommandLine which we will use:
Get-History | % { $_.commandline >> file.ps1 }
The above command gets the history objects and pipes them into the
% which is short hand for
ForEach-Object, and for each object run the script block appending the output to file.ps1. So fully expanded it would read:
Get-History | ForEach-Object { echo $_.commandline >> file.ps1 }
Personally, I don’t like their way. I think that the following way is far simpler with less typing and as it turns out better because file.ps1 will be emptied and the full history will be added whereas in their example if the file.ps1 exists it will be appended to; which is probably not what you want.
Get-History | ft commandline > file.ps1
The Jeff fellow sounds almost as good as Ed, who is Microsoft employing?
Once all the commands are in a file we can load it into Notepad or the
Powershell ISE and edit out the lines we don’t want.
Questions and Answers
Q: When are you going to do the webcast on remote
PowerShell?
A: Summer probably.
My A: This web cast was supposed to be about remoting but Ed elected to talk about execution policies instead.
Q: What are the best books for someone wanting to learn
PowerShell?
My A: Ed obviously things that the books he wrote on
PowerShell are the best, but if his books are anything like his webinars then I would steer well clear and try something else instead - like the internet!
Q: Ed mumbled though the question but I think it was something to do with how the Scripting Games work?
A: There will be a beginner event and an advanced event. Once you start competing you can’t do both. You will have 7 days to upload you solution. After 7 days the judges will grade them and post the scores. So every day there will be a leader board, so at the end of the 2 week period all the scores will be totalled and there will be prizes. There will also be daily prizes too. There is an FAQ on the site.
Q: Unfortunately he mumbled through reading the question again but it sounded like an interesting question about Importing Modules because Ed had brushed over it previously.
A: That’s going modules and he didn’t have time to get into that. Strange as there is still 10 minutes on the clock, I’m now wondering what it will hold! Go to the HeyScriptingGuy site click the tag for GettingStarted then MNodules, “but basically you use
Import-Module and it will import your modules, then you can use
Get-Command specifying which modules you want to look at. If you’re not sure if you are a beginner or an advanced user then you can take the
PowerShell quiz.
Q: Mumble, mumble… commands similar to pause that will keep a script running, after running a ps1 file ?
A: Well, there’s
Start-Sleep that will kind of halt execution of a script, so am thinking that maybe you could do that.
Q: Inside of using SRP from Windows 2008 R2, could you use the AppLocker?
A: Yes you could.
Q: I have a need to manage Windows 2003 remotely using
PowerShell. Is it mandatory to use WinRM on a remote machine, is it a security issue?
A: In general I don’t know of any major issues using WinRM. WinRM is designed to be firewall friendly and it’s going to be better to use WinRM than using the other options. So you would install Windows
PowerShell 2.0 on your remote machine, then you would enable remoting and at that point we could use WinRM and travel over there. If you didn’t want to do that you could use WMI to manage it remotely, but dude I mean WMI requires a whole lot more holes in your (?poorp? don’t understand what he said for this word - maybe it slang for firewall configuration?) because it’s not designed to be firewall friendly so you would really be better off using WinRM and
PowerShell.
Q: I noticed that your scripts are ps1 in
PowerShell 2?
A: Ha, ha, ha, yes ps1 is the file extension. This question has been asked a whole lot of times. There is no .ps2 extension. It does imply that, a erm, erm, we’re not necessarily backward compatible but we are forward compatible. So that means that things that ran in
PowerShell 1 should run in
PowerShell 2. Now I can write stuff in
PowerShell 2 and have it run on
PowerShell 1.0 if I don’t use any of the new cmdlets and any of the new features, but we made a lot of changes even to cmdlets that existed in
PowerShell 1 and so it’s really, really hard to keep track of all of the changes. And erm, y’know so so in that regard y’know you probably y’know erm need to watch out. Erm I don’t know of any specific reasons why you would need to use
PowerShell 1 as opposed to
PowerShell 2 unless for instance you were using the SQL-Mini shell or something like that.
My A: We would have had .ps for extension but that is taken up for PostScript files so one of the Microsoft wizards decided to .ps1 would be the next best thing. Out of all the possible extensions like .powershell, .psh then picked .ps1. Once you have selected an extension then you generally never change it unless a new version won’t parse because of structural changes.
Q: I get an error when I try Unrestricted just like I followed you.
A: That’s probably because you’re
PowerShell console did not have Administrator rights. So you have to grant Administrative rights, you’ve got to run as Administrator to change the script execution policy.
Q: What is the difference between Unrestricted execution policy and Bypass?
A: The difference is Unrestricted will still give you a message when you have downloaded scripts from the internet and you run it, Bypass does not.
Q: Is the dot ?flag? notation really only used in
PowerShell 1. Does it have much purpose in 2.0?
A: I’ve used it in 2.0. You can use it to include a file in a script or in a console. Most of the time we’re not doing that now-a-days because everyone’s putting stuff that would be included in an include file they’re putting them into modules. It still exists and is still around.
Q: Is Force available for all modifying commands?
A: I’m not certain about that, I always a little hesitant when a read “all", I don’know, it’s on a bunch of them. I haven’t specifically done a check to see if it is on all of them or not.
Q: Is there a way to comment a ps1 script file?
A: Yes the pound sign is the comment character in
PowerShell 1, it’s also the comment in
PowerShell 2. We also have a block comment which is an angled bracket pound followed by an angled bracket pound that you can use in
PowerShell 2.0
My A: Not sure why Ed is using a Eurpean keyboard that has a Great British pound symbol instead of a hash key. So in
PowerShell 1 it’s just a single line # and in
PowerShell it’s the single line hash and everything between <# … #>
Q: Would you recommend your book for a very basic beginner?
A: Lot’s or erm’s and arh’s, but basically watch these 5 videos then you’ll be ready for the book.
Q: What does dot sourcing mean?
A: Well dot sourcing is like when I say a dot space to include a file into a script or to include a script into a
PowerShell console. It’s a way of causing it to run and to pick up the files and stuff that are in there and functions and stuff and to bring them into the current session.
Q: If we configured the remote execution policy to be RemoteSigned then the user can pass it anyway?
A: Yes.
Q: Is there a was to display output if a command runs successfully?
A: If you write the script in such a way that you can do that, then yes. I’ve got an article that I was working for tomorrow where the scripting wife wanted to shut-down computers and doesn’t return any information. You can check the error object $error and see if there were any errors or not or $?. And if those are there, then that would be cool.
Q: What is the best book for remote
PowerShell scripting?
A: I don’t know of any book that specifically cover remote
PowerShell scripting in particular. Then he shamelessly plugs his book because it has a chapter on it. Leigh Holmes’ book talks about it, but no sure if there is a whole chapter. Richard Sidaway’s book PowerShell in Practice, Don Johnson and Jeffery Hicks’ book. Any
PowerShell book is going to cover it.