Introduction to Powershell for Linux admins
Even though I am rather a Linux power user my dayjob as an IT Security guy requires that I also deal with
Windows clients and servers on a regular basis.
Before I switched to Linux full time, I was using Windows XP with the dreaded cmd.exe
as the only built-in terminal.
Luckily things have changed quite a bit:
- Microsoft provides a modern command line interface and object-oriented scripting language with Powershell
- We got bash on Windows (Windows Subsystem for Linux)
- We have Powershell for Linux
- We could run Powershell on bash on Windows
So these are quite confusing times. The focus of this post will be on Powershell and to give Linux powerusers an introduction about similarities and differences between commonly used POSIX shells like bash and zsh and Windows Powershell. Note that even though you can install Powershell on your Linux system the information provided will refer to usage from Windows. Moreover I will not cover installation of Powershell at all and the example code provided was executed on Powershell 5.0.
Getting started with Powershell
The Powershell developers and product managers made sure that Linux admins will feel welcome. This is highlighted by the fact, that many internal commands are aliased to their counterpart binaries on your standard Linux system. You can e.g. simply run ls
instead of DIR
and even curl
will do what you would expect. To take that welcoming atmosphere even further, they created man-pages and provide a Powerful Tab-completion (even for parameters)!
So fire up Powershell and run ls
, ps > file.txt
, cp file.txt file.txt.copy
, cat file.txt
and rm file.txt.copy
. If you want to learn about options or arguments for a specific command just ask man
:
To get a list of currently defined aliases, just run alias
:
I truncated the output a little bit to highlight 2 facts:
- Even though you used
ls
etc. before like you did on Unix, these well known commands are actually aliases for the actual commands (actually not commands but cmdlets, more on that in the next section) - Some aliases point to the same command (e.g.
curl
andwget
are aliases forInvoke-WebRequest
,rm
andrmdir
point toRemove-Item
)
Before digging a little deeper, be aware that even though many things appear quite familiar, the functionality (e.g. output style or option names) might differ severely from a standard binary on Unix.
Terminology
To make sure we are on the same page here is some terminology that will pop up when you are dealing with Powershell. Common language is important but be aware that some terms used between Linux users might mean something different when consulting Powershell documentation.
- Cmdlet: Cmdlets are lightweight commands for execution in Powershell. This means that those are not full blown PE/ELF executables but .NET programs designed and compiled for Powershell. Some of them are included in the core of Powershell, so you could almost consider them built-ins; others are included in modules. Most Cmdlets have names matching a Verb-Noun pattern.
- Cmdlet parameters: Parameters of a cmdlet can be required, named, positional and switch (you could say optional) parameters. In Unix those are often called options.
- Cmdlet Parameter sets: Some parameters are mutually exclusive with others, some require additional parameters. The possible combinations of these are called parameter sets.
man
will tell you all parameter sets in theSYNTAX
section. - Commands: Commands in Powershell are not only Cmdlets, but also of the following types: Function, Application, Configuration, ExternalScript, Filter, Script, Workflow. More on these types in an upcoming post.
- Host: This is how the interface between the user and the backend interpreter is provided.
- Member: See next section
- Modules: As in other programming languages modules are used to package and namespace a certain functionality. In Powershell installing and loading additional modules means that you have more Cmdlets available. Modules can either be appropriately assembled binaries or scripts with a
.psm1
suffix. In older versions of Powershells before the module style was en vogue the term for module like packages was SnapIn. - Objects: Powershell Cmdlets work on .NET objects, thus they consume and produce objects (as in object oriented programming).
- Package management: This term describes a symbiosis of Linux distribution package management (e.g. apt/dpkg) and library management for scripting languages (rubygems, CPAN, pip) and can be used to either install Powershell modules or use Powershell code to install applications or setup services.
- Providers: To accommodate the Unix philosophy of “everything is a file” Powershell providers are used to present arbitrary data sources as files or directories (e.g. local certificate store, registry). Besides the built in providers you can create your own. The resulting “directories” presenting the data are called drives.
- Script: A script is a plaintext file - usually with a
.ps1
suffix - that contains Powershell instructions
Fundamental concepts
Object Orientation
As noted above Powershell as a .NET product is very reliant on object orientation. This not only manifests in how Cmdlets are named (Verb-Noun, e.g. Get-Item
, Remove-Item
) but also in how output can be handled. If you run a command in bash your output will usually be a string, whether it is multiline or not. Thus if you are only interested in a certain part of the output you will usually use string filtering and manipulation tools, such as grep
, cut
or awk
. Let’s demonstrate this by looking at the arp
command on Debian:
So even though the output format is clearly defined (c.f. the heading), arp
won’t hand you each arp cache entry as an object, that you can query for certain characteristics. This poses no problem if you are a human looking at it or if you are only interested in the Address field, but once you want to use only the Iface field for subsequent processing, you’re gonna have a lot of fun with string manipulation.
In Powershell on the other hand, all Cmdlets return objects. Each of these objects have Members which are most of the time either Properties or Methods. You can use the Get-Member
Cmdlet in order to retrieve information about a returned object, e.g.
Woah! Quite a lot of output. So we see that a Get-ChildItem
on a single file will return an object of type System.IO.FileInfo
.
This type has among others a property called CreationTime
of type datetime
. We can now simply retrieve it in an object oriented way:
Now lets get some details about the datetime
type:
Here I truncated the output to maintain minor readability, but again we have an object with Methods and Properties. Let’s add some hours:
It is important to note that even though the host outputs a string that can be copy pasted, this string is a mere representation of the underlying object. So if the representation does not fit your needs, pipe the output to a Format-*
Cmdlet to tune it accordingly. Of course simple string manipulation, even though in an object oriented way, is not the main use case for this. You can script complex workflows by relying on the objects and their properties and thus avoiding the dangers of using grep
, awk
etc. when handling strings, since these tools have no context information on the processed data.
The Pipeline
The pipe character |
can be used to create complex pipelines, i.e. sequences of cmdlets that transform or filter the output of the first cmdlet of the pipeline to derive the desired output.
We already saw examples of pipelines in the previous section where we e.g. used the Get-Member
cmdlet to gain information on the type of object that Get-ChildItem
(or in short: ls
) returns.
Even though the basic concept of piping is the same for POSIX shells and Powershell, one major advantage is the ability to use the object orientation in each step. I might dig into this a little deeper in an upcoming post, for now just have a look at this example from the Restart-Service
man page and guess what it does.
More tips and tricks
- You can use wildcards with many cmdlets (e.g.
Get-Service -Name "net*"
) - Powershell does not care about case sensitivity for the most part
- Declare variables with
$variablename =
- Call methods or retrieve properties on variables with
($variablename).method_or_property
- To install all man pages, run
Update-Help
, but be aware that you need to be be an administrator in order to do this - Use the help system and tab completion excessively
- Get a list of available modules with
Get-Module -ListAvailable
- Environment variables are stored in
$env
and can be accessed by a colon, e.g.$env:PSModulePath
, or you can docd env:; ls
- Output redirection is similar to
bash
, c.f.man about_Redirection
- If you have long strings as output, they will get truncated by Powershell, even when you write them to a file. Use
| Out-String -Width 256
(or an appropriate value) - If output is still truncated, we are probably talking about arrays or hashes, in these cases run
$FormatEnumerationLimit = -1
- You can distribute longer pipelines over multiple lines by just adding a new line after the
|
without indicating that the command will continue on the next line, since Powershell syntax always expects more cmdlets after a|
. The same holds true for opening parentheses - In other cases you can use the backtick to signal Powershell that the command should continue on the next line
- To see what cmdlets matching a certain pattern (e.g. anything with firewall in its name) available, run
Get-Command *firewall*
I guess that’s a lot to digest, hopefully I’ll be able to follow this up soon and walk you through some levels of Under The Wire (which by the way provides a sandboxed playground for Powershell that can be accessed via ssh).