PowerShell (also written pwsh) is a powerful open source command-line and object-oriented shell developed and maintained by Microsoft. It is syntactically verbose and intuitive for the user. This article is a guide on how to install PowerShell on the host and inside a Podman or Toolbox container.

Table of contents

Why use PowerShell

PowerShell, as the name suggests, is powerful. The syntax is verbose and semantically clear to the end user. For those that don’t want to write long commands all the time, most commands are aliased. The aliases can be viewed with Get-Alias or here.

The most important difference between PowerShell and traditional shells, however, is its output pipeline. While normal shells output strings or character streams, PowerShell outputs objects. This has far reaching implications for how command pipelines work and comes with quite a few advantages.

Demonstration

The following examples illustrate the verbosity and simplicity. Lines that start with the pound symbol (#) are comments. Lines that start with PS > are commands, PS > being the prompt:

# Return all files greater than 50MB in the current directory.
## Longest form
PS > Get-Childitem | Where-Object Length -gt 50MB
## Shortest form (with use of aliases)
PS > gci | ? Length -gt 40MB
## Output looks like this    Directory: /home/Ozymandias42/Downloads Mode                 LastWriteTime         Length Name ----                 -------------         ------ ---- -----          20/08/2020    13:55     2000683008 40MB-file.img # In order: get VMs, get snapshots, only select the last 3 and remove selected list:
PS > Get-VM VM-1 | Get-Snapshot | Select-Object -Last 3 | Remove-Snapshot

What this shows quite well is that input-output reformatting with tools like cut, sed, awk or similar, which Bash scripts often need, is usually not necessary in PowerShell. The reason for this is that PowerShell works fundamentally different than traditional POSIX shells such as Bash, Zsh, or other shells like Fish. The commands of traditional shells are output as strings whereas in PowerShell they are output as objects.

Comparison between Bash and PowerShell

The following example illustrates the advantages of the object-output in PowerShell in contrast to the traditional string-output in Bash. Suppose you want a script that outputs all processes that occupy 200MB or more in RAM. With Bash, this might look something like this:

$ ps -eO rss | awk -F' '  '{ if($2 >= (1024*200)) { 
 printf("%st%st%sn",$1,$2,$6);}   }' PID RSS COMMAND
A B C
[...]

The first obvious difference is readability or more specifically, semantic clarity. Neither ps nor awk are self-descriptive. ps shows the process status and awk is a text processing tool and language whose letters are the initials of its developers’ last names, Aho, Weinberger, Kernighan (see Wikipedia). Before contrasting it with PowerShell however, examine the script:

With this in mind, step back and look at what was required for this script to be written and for it to work:

  • The input command ps had to have the field we wanted to filter against in its output. This was not the case by default and required us to use the -O flag with the rss field as argument.
  • We had to treat the output of ps as a list of input fields, requiring us to know their order and structure. Or in other words, we had to at least know that RSS would be the second field. Meaning we had to know how the output of ps would look beforehand.
  • We then had to know what unit the data we were filtering against was in as well as what unit the processing tool would work in. Meaning we had to know that the RSS field uses kilobytes and that awk does too. Otherwise we would not have been able to write the expression ($2 <= 1024*200)

Now, contrast the above with the PowerShell equivalent:

# Longest form
PS > Get-Process | Where-Object WorkingSet -ge 200MB
# Shortest form (with use of aliases)
PS > gps | ? ws -ge 200MB NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- ----------- A B C D E F G
[...]

This first thing to notice is that we have perfect semantic clarity. The commands are perfectly self-descriptive in what they do.

Furthermore there is no requirement for input-output reformatting, nor is there concern about the unit used by the input command. The reason for this is that PowerShell does not output strings, but objects.

To understand this think about the following. In Bash the output of a command is equal to that what it prints out in the terminal. In PowerShell what is printed on the terminal is not equal to the information, that is actually available. This is, because the output-printing system in PowerShell also works with objects. So every command in PowerShell marks some of the properties of its output objects as printable and others not. However, it always includes all properties, whereas Bash only includes what it actually prints. One can think of it like JSON objects. Where output in Bash would be separated into “fields” by a delimiter such as a space or tab, it becomes an easily addressable object property in PowerShell, with the only requirement being, that one has to know its name. Like WorkingSet in the above example.

To see all available properties of a command’s output objects and their types, one can simply do something like:

PS > Get-Process | Get-Member

Install PowerShell

PowerShell is available in several package formats, including RPM used by Fedora Linux. This article shows how to install PowerShell on Fedora Linux using various methods.

I recommend installing it natively. But I will also show how to do it in a container. I will show using both the official Microsoft PowerShell container and a Fedora Linux 30 toolbox container. The advantage of the container-method is that it’s guaranteed to work, since all dependencies are bundled in it, and isolation from the host. Regardless, I recommend doing it natively, despite the official docs only explicitly stating Fedora Linux releases 28 to 30 as being supported.

Note: Supported means guaranteed to work. It does not necessarily mean incompatible with other releases. This means, that while not guaranteed, releases higher than 30 should still work. They did in fact work in our tests.

It is more difficult to set up PowerShell and run it in a container than to run it directly on a host. It takes more time to install and you will not be able to run host commands directly.

Install PowerShell on a host using the package manager

Method 1: Microsoft repositories

Installation is as straight-forward as can be and the procedure doesn’t differ from any other software installed through third party repositories.

It can be split into four general steps:

  1. Adding the new repository’s GPG key
  2. Adding repository to DNF repository list
  3. Refreshing DNF cache to include available packages from the new repository
  4. Installing new packages

Powershell is then launched with the command pwsh.

$ sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
$ curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo
$ sudo dnf makecache
$ sudo dnf install powershell
$ pwsh

To remove the repository and packages, run the following.

$ sudo rm /etc/yum.repos.d/microsoft.repo
$ sudo dnf remove powershell

Method 2: RPM file

This method is not meaningfully different from the first method. In fact it adds the GPG key and the repository implicitly when installing the RPM file. This is because the RPM file contains the link to both in it’s metadata.

First, get the .rpm file for the version you want from the PowerShell Core GitHub repository. See the readme.md
“Get Powershell” table for links.

Second, enter the following:

$ sudo dnf install powershell-<version>.rhel.7.<architecture>.rpm

Substitute <version> and <architecture> with the version and architecture you want to use respectively, for example powershell-7.1.3-1.rhel.7.x86_64.rpm.

Alternatively you could even run it with the link instead, skipping the need to download it first.

$ sudo dnf install https://github.com/PowerShell/PowerShell/releases/download/v<version>/powershell-<version>.rhel.7.<architecture>.rpm

To remove PowerShell, run the following.

$ sudo dnf remove powershell

Install via container

Method 1: Podman container

Podman is an Open Container Initiative (OCI) compliant drop-in replacement for Docker.

Microsoft provides a PowerShell Docker container. The following example will use that container with Podman.

For more information about Podman, visit Podman.io. Fedora Magazine has a tag dedicated to Podman.

To use PowerShell in Podman, run the following script:

$ podman run -it --privileged --rm --name powershell --env-host --net=host --pid=host --ipc=host --volume $HOME:$HOME --volume /:/var/host mcr.microsoft.com/powershell /usr/bin/pwsh -WorkingDirectory $(pwd)

This script creates a Podman container for PowerShell and immediately attaches to it. It also mounts the /home and the host’s root directories into the container so they’re available there. However, the host’s root directory is available in /var/host.

Unfortunately, you can only indirectly run host commands while inside the container. As a workaround, run chroot /var/host to chroot to the root and then run host commands.

To break the command down, everything is mandatory unless specified:

  • -it creates a persistent environment that does not kick you out when you enter it;
  • --privileged gives extended privileges to the container (optional);
  • --rm removes the container when you exit;
  • --name powershell sets the name of the container to powershell;
  • --env-host sets all host environment variables to the container’s variables (optional);
  • --volume $HOME:$HOME mounts the user directory;
  • --volume /:/var/host mounts the root directory to /var/host (optional);
  • --net=host --pid=host --ipc=host runs the process in the host’s namespaces instead of a separate set of namespaces for the contained process;
  • docker.io/microsoft/powershell enters the container;
  • /usr/bin/pwsh -WorkingDirectory $(pwd) enters the container in the current directory (optional).

Optional but very convenient: alias pwsh with the script to easily access the Podman container by typing pwsh.

To remove the PowerShell image, run the following.

$ podman rmi mcr.microsoft.com/powershell

Method 2: Fedora Linux Toolbox container

Toolbox is an elegant solution to setup persistent environments without affecting the host system as a whole. It acts as a wrapper around Podman and takes care of supplying a lot of the flags demonstrated in the previous method. For this reason, Toolbox is a lot easier to use than Podman. It was designed to work for development and debugging. With Toolbox, you can run any command the same as you would directly on the Fedora Workstation host (including dnf).

The installation procedure is similar to the installation on the host methods, with the only difference being that those steps are done inside a container. Make sure you have the toolbox package installed.

Preparing and entering the Fedora 34 Toolbox container is a two step process:

  1. Creating the Fedora 34 Toolbox container
  2. Running the Fedora 34 Toolbox container
$ toolbox create --image registry.fedoraproject.org/f34/fedora-toolbox
$ toolbox enter --container fedora-toolbox

Then, follow the instructions at Method 1: Microsoft repositories.

Optional but very convenient: alias pwsh with toolbox run –container fedora-toolbox pwsh to easily access the Toolbox container by typing pwsh.

To remove the Toolbox container, make certain you have stopped the Toolbox session by entering exit and then run the following:

$ podman kill fedora-toolbox
$ toolbox rm fedora-toolbox

Posted by Contributor