ASP.NET application in Window Containers
Published: 2020-12-03 11:49:10

Suppose you have a legacy app that you want to containerize, or for some reason, you are using .Net Framework and not .net core (or .net 5). Then you need to use Windows containers. In this article, I am going to start from scratch and show you the steps. Let's begin!

The source code for this article is available [here](https://github.com/bahman616/WinContainers)

In order to run this application check the README file in the above repository.

##Build your first Windows container 
Let's start with the dockerfile:

```docker
# escape=`
 
FROM mcr.microsoft.com/dotnet/framework/sdk:4.8-windowsservercore-ltsc2019 AS build
WORKDIR /app

COPY . .

RUN nuget restore;
RUN msbuild /p:Configuration=Debug

FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
WORKDIR /inetpub/wwwroot
COPY --from=build /app/SampleWebsite/. ./
```

This is a simple dockerfile which is building our super simple app! It starts with the windows server base image. When it comes to choosing your base image, you have multiple options:

**1. Windows Server Core**: This is the one which you may need to build your ASP.NET Framework application. It just has what you need not more than that.

**2. Nano Server**: This base image can be used for .Net Core application and it is smaller than Windows Server Core image in size.

**3. Windows**: Entire Windows!

**4. Windows IoT Core**: Suitable for IoT devices.

If you want to know more about these base images, you can read [this](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-base-images).

I have selected .Net Framework 4.8 sdk which is based on Windows Server Core 2019 Long-Term Servicing Channel (ltsc). Then I labelled this step as "build".

The next step in the docker file is moving to a folder called 'app'. Then we copy everything there.

Restoring nuget packages and building the application are the next steps.

In the final section of the dockerfile, we use ASP.NET base image in order to host our website using IIS. Note that behind the scenes we are using the same base Windows images in this step and the first line of the dockerfile which is [.Net framework runtime](https://hub.docker.com/_/microsoft-dotnet-framework-runtime/). You can check the source code of .Net base images from [here](https://github.com/microsoft/dotnet-framework-docker/tree/master/src)

##Configuration Builders
A typical .Net Framework website has a web.config file that includes all the configuration values. When you deploy your website, you transform/update the configuration values for your environment. This is not going to work for containers. Because when you build your container image, you bake configuration values in it. You should be able to inject environment variables in containers when you run them. Then your application should be able to access those environment variables in order to get its configuration values. This is why you need configuration builders.

[Configuration Builders](https://docs.microsoft.com/en-us/aspnet/config-builder) are some nuget packages that you can install on your .Net app (>= 4.7.1) and help your app to get its required configuration values from environment variables, Azure Key Vault, etc. 

When you install a configuration builder nuget package, it changes some settings in your web.config. Here is an example:

```XML
<configuration>
  <configSections>
    <section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false" />
  </configSections>
  <configBuilders>
    <builders>
    <add name="Environment" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /></builders>
  </configBuilders>
  <appSettings configBuilders="Environment">
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="Message" value="The app is not running in a container" />
  </appSettings>
...
<configuration>
```
As you can see in the above code, there is a new "configBuilders" section on the web.config. We only have one entry in this section and that is "Microsoft.Configuration.ConfigurationBuilders.Environment". Because we want to pass configuration values as environment variables to our containers, we only need this one entry in <configBuilders> section.

Then all we need to do is specifying the name of the configBuilders on appSettings. Note that config builders have some modes and the default mode can only be used on appSettings and connectionStrings section because they can have key-value pairs. If you need to change a different area on web.config, then you need to use a different mode for config builders.

In windows container, there is a limitation which is important to note. This is from [Microsoft docs](https://docs.microsoft.com/en-us/aspnet/config-builder):
> In a Windows container environment, variables set at run time are only injected into the EntryPoint process environment. Apps that run as a service or a non-EntryPoint process do not pick up these variables unless they are otherwise injected through a mechanism in the container. For IIS/ASP.NET-based containers, the current version of ServiceMonitor.exe handles this in the DefaultAppPool only. Other Windows-based container variants may need to develop their own injection mechanism for non-EntryPoint processes.

##Running your container
I created a script in the Github repo which you can use to run the container. The instructions about how to use the script, are in the [README file](https://github.com/bahman616/WinContainers) of the repo.
##Debugging
In order to debug your windows container in Visual Studio, make sure you have updated your visual studio to the latest version as there have been many improvements around windows containers at the time of writing this post.

The easiest way to debug your container is using the container window in Visual Studio which is available from View menu => Other windows.

You should be able to see all running containers on your machine and check the environment variables, logs, ports, and even files on a container:

![windows containers debug](https://dev-to-uploads.s3.amazonaws.com/i/xaudc5bx80hbc3mz0zcc.png)

You can also run commands against the running container or attach your debugger to it using the same containers windows. You can get more information about containers window in Visual Studio from [Here](https://docs.microsoft.com/en-us/visualstudio/containers/view-and-diagnose-containers?view=vs-2019)