Using PowerShell in Containers
The vision for PowerShell Core is to be able to run PowerShell anywhere. In this article, I’m going to discuss how you can use Docker Containers to enable just that. We’ll look at running PowerShell in a container, running cmdlets, running different versions of PowerShell at the same time, and also how to build our own “serverless” computing platform.
Let’s address a few reasons why you would want to run PowerShell in a container.
- Speed and agility – this for me is probably the number one reason to run PowerShell in a container. The PowerShell container images are coming in at around 375MB, this means with a modern Internet connection you’ll be able to pull a PowerShell container image and be up in running in a very small amount of time.
- Version – there are container images available for every release of PowerShell Core, including preview/release candidate code. With containers, you can run multiple versions of PowerShell Core in a way where they will not conflict with each other.
- Platform independence – there are container images for Ubuntu, Fedora, Windows Server Core, Nano Server and more. This allows you to be able to consume PowerShell Core regardless of your underlying platform. You can select whichever image you want, pull the container and go.
- Testing – if you need to test your scripts across various versions of PowerShell Core you can pull the container, run the script on the exact version you need. You can have multiple containers on your system running multiple versions of PowerShell and be able to run them all at the same time.
- Isolation – containers will allow you to have self-contained environments for execution, security, environment, and configuration settings. You can also use this idea to isolate conflicting modules from each other. This is particularly valuable when developing modules and/or cmdlets.
Getting Up and Running
Let’s get started with using PowerShell Core in a container. First up, we will want to pull the Docker Container Image to our local machine. This will pull the image with the latest tag. Which at the time of this post is 6.2.0-ubuntu-18.04.
docker pull mcr.microsoft.com/powershell:latest
With the container image local, let’s go ahead and start up the container. In this first go, I’m going to start up the container with the docker run command and with the –interactive and –tty flags. What these flags do is, when the container starts, attach to the terminal of the container so I can use PowerShell Core interactively at the command line.
docker run \ --name "pwsh-latest" \ --interactive --tty \ mcr.microsoft.com/powershell:latest
This will get you a PowerShell prompt. I told you this was going to be fast.
PowerShell 6.2.0 Copyright (c) Microsoft Corporation. All rights reserved. https://aka.ms/pscore6-docs Type 'help' to get help. PS />
From that prompt, we can do the normal PowerShell things we need to do. Let’s start our journey like all good PowerShell demos do and run Get-Process. You’ll notice that there is only one process running in the container, and that’s your pwsh session. This is due to the isolation concepts of Containers. With this isolation, problems like conflicting modules and settings go away. The container gives you script an isolated execution environment. If you need to have two conflicting versions of a module, DLL or library to run your workload or script…you can use a container to isolate their execution giving them the ability to co-exist on the same system.
PS /> Get-Process NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName ------ ----- ----- ------ -- -- ----------- 0 0.00 110.03 2.01 1 1 pwsh
We can use exit to get out of PowerShell. When you exit PowerShell the container will stop. You can see that status of your container with docker ps.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8c9160fea43f mcr.microsoft.com/powershell:latest "pwsh" 6 minutes ago Exited (0) 8 seconds ago pwsh-latest
Running a cmdlet When Starting a Container
docker run mcr.microsoft.com/powershell:latest pwsh -c "&{Get-Process}"NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
0 0.00 81.35 0.54 1 1 pwsh
time docker run mcr.microsoft.com/powershell:latest pwsh -c "&{Get-Process}"NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
0 0.00 81.61 0.54 1 1 pwsh
real 0m1.901s user 0m0.038s sys. 0m0.086s
docker run mcr.microsoft.com/powershell:preview pwsh -c "&{Get-Host}"Name : ConsoleHost Version : 6.2.0-rc.1 …output omitted…
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d8d8d27ec7be mcr.microsoft.com/powershell:preview "pwsh -c &{Get-Host}" 4 seconds ago Exited (0) 2 seconds ago pensive_poincare 5eace290b47c mcr.microsoft.com/powershell:latest "pwsh -c &{Get-Proce…" 4 minutes ago Exited (0) 4 minutes ago dreamy_haibt c8361b9e0a76 mcr.microsoft.com/powershell:latest "pwsh -c &{Get-Proce…" 6 minutes ago Exited (0) 6 minutes ago boring_shirley 8c9160fea43f mcr.microsoft.com/powershell:latest "pwsh" 15 minutes ago Exited (0) 8 minutes ago pwsh-latest
Running a Script When Starting a Container
docker run \ --name "pwsh-script" \ --interactive --tty \ --volume PSScripts:/scripts \ mcr.microsoft.com/powershell:latest
docker cp Get-Containers.ps1 pwsh-script:/scripts
docker rm pwsh-script
docker run \ --name "pwsh-script" \ --interactive --tty \ --volume PSScripts:/scripts \ mcr.microsoft.com/powershell:latest
PS /> ls -la /scripts/ total 12 drwxr-xr-x 2 root root 4096 May 2 18:30 . drwxr-xr-x 1 root root 4096 May 2 18:33 .. -rw-r--r-- 1 502 dialout 73 Apr 28 21:43 Get-Containers.ps1 PS /> /scripts/Get-Containers.ps1 Hello, world! PS /> exit docker rm pwsh-script
Sounds Like…Serverless?
docker run \ --rm \ --volume PSScripts:/scripts \ mcr.microsoft.com/powershell:latest pwsh -F /scripts/Get-Containers.ps1 Hello, world!