"What is the one technology I need to learn to get a job like the one you have?" - this is a question I've received from time to time, and my answer would vary, depending on who asked. Or rather; who I perceive the questioner to be - I cannot *know* for a fact what most suits his or her particular ambitions and style.. But in the last few years my answer, more often than not, has been "PowerShell"! Simply because I see it as one of the most important and game-changing technologies to come out of [Microsoft] Redmond in many many years!

PowerShell is, in a nutshell, in fact quite impossible to fit in a nutshell. But hey, let's assume that we're dealing with the NutShell of Holding here, one that can indeed contain the immense power of PowerShell. :) 

"It's a command line/cli/terminal/shell", you'd say, on first opening it, and it's not until you start exploring further that you realize that it's not so much about the "Shell" part as it is about the "Power" of PowerShell. Yes, you can access the power through a shell, but much more important is the fact that this particular shell  gives you all the best from several worlds, including the good old Windows cmd shell, Linux' Bash shell and others, so if you're used other shells before, you should feel right at home!

But still, if it was "just a shell", or even "the best shell ever", which is an entirely different discussion, that wouldn't even be newsworthy. The part of PowerShell that is "exposed" as a shell is still just the tip of the iceberg of the Power of PowerShell. So what is it then? That's a question that is hard to answer in a single sentence. PowerShell is a framework for managing all aspects of Windows, and what's more, it's an interface to everything under the hood in Windows, meaning (not exhaustively) the Win32 API and the .NET Framework. But even if you know nothing about these two technologies, you will still leverage their power in your daily dealings with PowerShell, through the simplified access methods available to you.

To start using PowerShell, simply run "powershell.exe" in any modern (post XP) Windows environment [I'm using the latest version as of this writing, 5.0]. In the PowerShell console you can access your usual DOS-style commands, such as dir, copy, cd, find, move, md, rd, attrib and a whole suit of others. Although they mostly work exactly as in your old DOS-box (aka CMD), there are some (irritating) differences in the syntax, but mostly you'll be fine using them the way you're used to. The pros outweigh the cons by orders of magnitude.

Objectively speaking..

Under the hood, though, these commands are aliases, pointing to their PowerShell (henceforth PS) cousins, called cmdlets (pronounced 'commandlets'). For instance, the command you know as dir or ls, still exists, but merely points to a cmdlet called Get-ChildItem. There is one (maybe more) key difference(s) from using either ls in Bash or dir in CMD, though, and that is a very powerful concept. You'll soon begin to understand why they named it "PowerShell"..

The data returned from Get-ChildItem (aka dir, ls) in PowerShell is NOT plain text, it's a full-blown object, with properties and methods for accessing and manipulating data in the object. Don't know what I mean by that? Well, I'd like to explain it by first shining a light on how you'd parse data plain text data from the good ol' dir command:

Plain text output from dir can look like this (well, sans the fancy colors):

x
 
1
D:\data\A directory>dir
2
 Volume in drive D is DATA
3
 Volume Serial Number is E491-2BE5
4
5
 Directory of D:\data\A directory
6
7
07.09.2016  09.18    <DIR>          .
8
07.09.2016  09.18    <DIR>          ..
9
19.05.2014  15.57            44 045 2014-05-19_15-55-26.png
10
06.09.2016  16.05                 4 a textfile.txt
11
07.09.2016  09.18                 4 another textfile.txt
12
               3 File(s)         44 053 bytes
13
               2 Dir(s)  1 964 271 837 184 bytes free
14
15
D:\data\A directory>

Say you wanted to write a script that would a the list of files and directories from an arbitrary place on your (or someone else's) hard drive, and then perform some action on each of the files, but only those matching certain conditions such as file size or date last written, how would you go about doing that using this output?

While certainly possible, the resulting CMD script would be a complex and fragile one, as it has to parse filenames, dates and file sizes by navigating to positions in the output, read a certain number of characters from a certain position to find the relevant data, and then make assumptions about the formatting. The output gives no clue to the formatting or even placement of data, so you have to look for it, count to where it is and hope that the script finds it there.

Then, after you finally make it all work, you proudly send the script to your friend to run, and all your carefully aligned code just breaks, 'cause your friend runs his Windows in a different language which turns the date on it's head, and doesn't use thousand-separators in numbers... This StackOverflow article perfectly supports my argument.

Microsoft previously applied VBScript to solve this lack of scriptability in Windows. VBScript is certainly powerful and can perform almost any task, but it's syntax is convoluted and it seems that even the smallest task requires hours of coding to achieve any sort of result. I could never get my head around it. Still, if you've learned and used VBScript, you have my deepest respect. Also, you'll feel right at home here and soon love PowerShell!

So I come back to this simple, yet immensely powerful concept: The output you get in PS is an object. That means that all the data about each file and directory in the output for our dir command is accessible in an array of values. So while the output in PS looks a lot like the previous:

x
 
1
PS D:\data\A directory> dir
2
3
4
    Directory: D:\data\A directory
5
6
7
Mode                LastWriteTime         Length Name
8
----                -------------         ------ ----
9
-a----       19.05.2014     15.57          44045 2014-05-19_15-55-26.png
10
-a----       06.09.2016     16.05              4 a textfile.txt
11
-a----       07.09.2016     09.18              4 another textfile.txt
12
13
14
PS D:\data\A directory>

..this time it's very different. If we want, we can capture this output in a variable (which is also an object):

1
 
1
PS D:\data\A directory> $files = dir

The dollar-sign signifies a variable, this one is named "files", so $files. The equal-sign is an assignment operator. Dir creates the object that holds all the files. And to prove that the variable $files contains the object that was returned from dir, we can simply output it by typing it's name, like this:

x
 
1
PS D:\data\A directory> $files
2
3
4
    Directory: D:\data\A directory
5
6
7
Mode                LastWriteTime         Length Name
8
----                -------------         ------ ----
9
-a----       19.05.2014     15.57          44045 2014-05-19_15-55-26.png
10
-a----       06.09.2016     16.05              4 a textfile.txt
11
-a----       07.09.2016     09.18              4 another textfile.txt
12
13
14
PS D:\data\A directory>

Looks exactly the same, doesn't it? So what am I so worked up about?? Let's start accessing properties and methods..

The $files object itself has a lot of useful properties and methods, which are accessed using dot-notation. For instance, this will return the directory where the dir command was run:

1
 
1
PS D:\data\A directory> $files.DirectoryName
2
D:\data\A directory

The .Count attribute returns the number of lines in the output:

x
1
PS D:\data\A directory> $files.Count
2
3

And, if we give the object an index, specifying a particular file in the object, we can access it's properties and methods!

1
1
PS D:\data\A directory> $files[1]
2
3
4
    Directory: D:\data\A directory
5
6
7
Mode                LastWriteTime         Length Name
8
----                -------------         ------ ----
9
-a----       06.09.2016     16.05              4 a textfile.txt

And why don't we combine the index and the dot-notation, so that we can access properties of a single file:

1
1
PS D:\data\A directory> $files[0].LastWriteTime
2
3
mandag 19. mai 2014 15.57.39

Is it beginning to make sense? If you think the date-formatting looks screwy, it's because my Windows is set up to use Norwegian locale, but PS recognizes that, and all date-calculations will work, even on a computer with a different language. It's a just a Date-object, which happens to here be represented in the Norwegian style. If I want to, I can format the date exactly as I want, but I usually let Windows handle that, so I can concentrate on the logic of my script.

I used dir in my example here, because it is one of the commands I've used most often, and sysadmins have daily encounters of files and directories in need of attention. Other commands have similar equivalent PS cmdlets, try Get-Alias for a comprehensive list of them. You can also create your own aliases, although I haven't found the need to.

Tab-completion

Deserving a chapter all on its own, is Tab-completion. It will save you a ton of work, help you explore your options and reduce a lot of typing errors. If you aren't familiar, here's the low-down:

Tab-completion works by trying to guess what you want to type. It will work on files and directories (as in Bash, Cmd) but it will also flip through cmdlets, functions, parameters and values! Try typing this, use the TAB-key each time it says <tab>:

x
1
get-ch<tab> c:\win<tab>sys<tab><tab><tab> -r<tab> <enter>

You see what happens as you type? The first Tab, PS sees that you've started typing the name of a cmdlet, and finishes Get-ChildItem for you, properly capitalized. Then you point it at C:\Win and the second Tab completes C:\Windows\ for you. When you type sys and hit Tab three times, it cycles through the options, pointing you to the C:\Windows\System32 folder. The Tab following -r completes the parameter -Recurse, which in turn makes the command list files in all subdirectories of C:\Windows\System32. 

In short - if in doubt, try TAB! :)

Getting Help and some Grammar

If you're used to Linux, you've probably used the man command. It returns  documentation on topics you specify. PS has an alias for that too, pointing to a cmdlet called Get-Help. Simply stating Get-Help (or man, or even Help) gives you an overview page. Specifying a topic will search for and return information on that topic.

Small sidenote: All the standard cmdlets follow the Verb-Noun nomenclature, and you're encouraged to do the same, although it's not required. This style makes it easy to guess the name of a command; Get is used when you want to get some information, Set is used for changing things. Add, Remove, Start, Stop, New, Write and Wait are examples of others.

For instance, let's take a look at Get-Process. Can you guess from the name what it does? It gets information on running processes! If you want to know how it works, just run it, and see for yourself! But if you want to know more about it, try Get-Help Get-Process, or man Get-Process. 

Here's a trick: To get a list of all cmdlets that are "related" to Get-Process, you can specify just the Noun and have all the cmdlets for Process returned:

x
 
1
PS D:\data\A directory> Get-Command -Noun Process
2
3
CommandType     Name                                               Version    Source
4
-----------     ----                                               -------    ------
5
Cmdlet          Debug-Process                                      3.1.0.0    Microsoft.PowerShell.Management
6
Cmdlet          Get-Process                                        3.1.0.0    Microsoft.PowerShell.Management
7
Cmdlet          Start-Process                                      3.1.0.0    Microsoft.PowerShell.Management
8
Cmdlet          Stop-Process                                       3.1.0.0    Microsoft.PowerShell.Management
9
Cmdlet          Wait-Process                                       3.1.0.0    Microsoft.PowerShell.Management

Get-Help by itself gives a brief summary, for the full documentation, try the parameter -Full. For examples of use, try -Examples. In fact, type just the - and use TAB to go through all the alternatives! :D

As PS has grown A LOT over the years, so has the documentation. And documentation is updated more frequently, so they've added a way to update the information, by using Update-Help. It will run and download documentation for you.

Summary

I hope this article sparked your curiosity enough that you want to go explore PowerShell on your own. Of course, searching the web for other information on PowerShell will give you more, and you will find endless tutorials on the subject. I want to do more articles on the subject, giving you some magic automation-powerskills for your sysadmin toolchest..

I recently read that PS has been open-sourced and is available on Linux as well, I haven't had a chance to explore that aspect of it yet, but I certainly will as soon as I can!

Cheers and happy coding,

Thomas