Managing Windows Server Containers with PowerShell: Managing Containers

In this article series, I’m showing you how to manage Windows Server Containers on Windows Server 2016 (WS2016) Technical Preview 3 (TPv3) using PowerShell. I’ve already covered where the files are stored, what kinds of files are used, and how to create and start a new container. In this post, we’ll look at how we can manage a container, along with instructions on how to create a new container image.
This post is part of a series:

Remote Administration

What good is a container if you don’t install something to run inside of it? Normally with a machine you would log into that machine. But there is no such thing as logging into a container because it’s not a machine. But you can use PowerShell’s remote capabilities to configure a container.
If you want to have an interactive administration experience, then you can use Enter-PSSession to get a PowerShell session inside of a container. Enter-PSSession requires the unique ID of the container to create that session. That ID is easy to provide if you previously stored a pointer to the container using a variable (see $Container in Part 1). For example:

Enter-PSSession -ContainerId $Container.ContainerID -RunAsAdministrator

If you haven’t saved a pointer to the container, then you can run:

Enter-PSSession -ContainerId (Get-Container Test1).ContainerID -RunAsAdministrator

Entering a PowerShell remote session with a Windows Server Container (Image credit: Aidan Finn)
Entering a PowerShell remote session with a Windows Server Container (Image credit: Aidan Finn)

If you run Enter-PSSession too soon after starting a container, then you will get the following misleading error:

Enter-PSSession : The term ‘Measure-Object’ is not recognized as the name of a cmdlet, function, script file, or operable program.

The advantage of Enter-PSSession is that it allows you to get quite a bit of manual work done inside of a container. However, it is an interactive-only approach, and it is not suitable for remote scripting.
If you want to run a single command against a container, or if you want to script administrative tasks inside of a container, then you should use Invoke-Command. Invoke-Command allows you to execute a script block against a machine or a container; the example below will send a script block to retrieve the IPv4 address of a container, which we might later use to create NAT rules on a VM host.

Invoke-Command -ContainerID $Container.ContainerId -ScriptBlock {(Get-NetIPAddress -AddressFamily IPv4).IPAddress}

Using Invoke-Command to run a script block in a container (Image credit: Aidan Finn)
Using Invoke-Command to run a script block in a container (Image credit: Aidan Finn)

Customizing a Container

The reason we use containers is to deploy services, so we need to install some software. Not every application is suitable for containerization; remember that containers are designed for born-in-the-cloud services. I had no joy with running IIS in more than one container on a VM host in my testing with TPv3; this might be why Microsoft documented the process for deploying the nginx web server instead.
One task you might find tricky with containers is getting the application setup files into the container, as containers do not support domain membership because of their intended short lives. The method most are using is to share the files on a web server and use wget to download the zip file into the container. The following example is run inside of a container to download the setup files for nginx into a container:

wget -uri 'http://nginx.org/download/nginx-1.9.3.zip' -OutFile "c:\nginx-1.9.3.zip"

You can install and customize your software as required, and then we’ll create a reusable image.

Creating a Container Image

The goal of containers is to create a golden image that you can deploy repeatedly and get identical results every time. Once you have prepped your first container, you can start the image creation process. The first step is to stop the container:

Stop-Container $Container

Next you will create a new container image, stored in the repository from the customized container. This is a non-destructive process:

New-ContainerImage -Container $Container -Name "DemoImage1" -Publisher "Petri" -Version 1.0

If we query the resulting new container image using Get-ContainerImage, then we can see there is a link between the new image and WindowsServerCore. Deploying a new container from DemoImage1 will cause WindowsServerCore to be used as a dependency.

Querying the parentage or dependencies of a container image (Image credit: Aidan Finn)
Querying the parentage or dependencies of a container image (Image credit: Aidan Finn)


You now have an image that you can deploy to rapidly create lots of identical containers. I’ll show you how to do that in the next post in this series, along with steps on how to configure network address translation (NAT) for containers on NAT-enabled VM hosts.