The world is full of legacy applications. They run fine until they don’t or you live in constant fear that they may break any moment and you are not even sure if your development environment can run this app anymore. Your client or your boss may have decided that it is time to upgrade this app to a newer tech stack and take advantage of what has been happening in your coding world.
There is one problem though: You do not remember what the app does or what the pages looked like. Sure you can go to the production site but you want more than that. You want to be able to look at the source code, run it debug it, get a feeling for it. Or maybe, just maybe for whatever reason the production site is not available to you.
At this point, you do not want to clutter your environment with old frameworks and what not.
So, what do you to?
You have 3 options:
Setup a virtual machine.
Setup a container.
Setup Windows Sandbox.
Virtual Machines are resource heavy. You want to use something that is quick and lightweight.
In this article, we will go through setting up a Docker container (Windows) and a Windows Sandbox and run our app to be able to compare the experience so we can decide which approach to use in the future under certain scenarios.
So, let’s begin…
Prerequisites
Windows 10 Pro or Enterprise (version 1903 or later) or later. I have Windows 11 Pro.
Administrative access to your computer
Your legacy MVC application files - I downloaded an open source MVC app for this purpose from https://github.com/dotnetdev-kr/nerddinner. This is the famous Nerd Dinner asp.NET MVC app. Since it is very old, I am going to run the MVC3 version.
Windows Sandbox
Enabling Windows Sandbox is easy. Just search for "Turn Windows features on or off" , select it and then check “Windows Sandbox”. Restart if Windows asks you to restart.
Once we let Windows do its thing, we then create a folder called C:\SandboxShared
and copy the MVC files in this folder.
After all this, we need to prepare a couple of script files. Create sandbox_config.wsb
in C:\SandboxShared
with the code below:
<Configuration>
<MappedFolders>
<MappedFolder>
<HostFolder>C:\SandboxShared</HostFolder>
<SandboxFolder>C:\Users\WDAGUtilityAccount\Desktop\SandboxShared</SandboxFolder>
</MappedFolder>
</MappedFolders>
<LogonCommand>
<Command>powershell -ExecutionPolicy Bypass -File C:\Users\WDAGUtilityAccount\Desktop\SandboxShared\setup.ps1</Command>
</LogonCommand>
</Configuration>
and the ps1 file we are telling the configuration file to run:
# This line creates a log file on the desktop
Start-Transcript -Path "$env:USERPROFILE\Desktop\setup_log.txt"
Write-Host "Script started at $(Get-Date)"
# Function to show progress
function Show-Progress($activity, $status) {
$message = "[$activity] $status"
Write-Host $message
Add-Content -Path "$env:USERPROFILE\Desktop\progress.txt" -Value $message
}
Show-Progress "Initialization" "Script is running..."
# Install IIS using DISM
Show-Progress "IIS Installation" "Installing IIS using DISM..."
dism /online /enable-feature /featurename:IIS-WebServerRole /all
# Check if .NET Framework 4.8 is already installed (it might be in newer Windows versions)
$net48Check = Get-ChildItem "HKLM:SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\" | Get-ItemPropertyValue -Name Release
if ($net48Check -ge 528040) {
Show-Progress ".NET Framework" ".NET Framework 4.8 or later is already installed."
} else {
Show-Progress ".NET Framework" "Downloading .NET Framework 4.8..."
$url = "https://go.microsoft.com/fwlink/?LinkId=2085155"
$output = "$env:TEMP\netframework48.exe"
Invoke-WebRequest -Uri $url -OutFile $output
Show-Progress ".NET Framework" "Installing .NET Framework 4.8..."
Start-Process -FilePath $output -ArgumentList "/q /norestart" -Wait
}
# Set up IIS site
Show-Progress "IIS Configuration" "Setting up IIS site..."
Import-Module WebAdministration
New-WebAppPool -Name "MVCAppPool"
Set-ItemProperty IIS:\AppPools\MVCAppPool -name "managedRuntimeVersion" -value "v4.0"
New-Website -Name "MVCApp" -PhysicalPath "C:\Users\WDAGUtilityAccount\Desktop\SandboxShared\YourAppFolder" -ApplicationPool "MVCAppPool" -Port 8080
# Configure firewall
Show-Progress "Firewall Configuration" "Configuring firewall..."
New-NetFirewallRule -DisplayName "Allow Port 8080" -Direction Inbound -LocalPort 8080 -Protocol TCP -Action Allow
# Diagnostic Information
Show-Progress "Diagnostics" "Gathering diagnostic information..."
Write-Host "IIS Service Status:"
Get-Service W3SVC | Select-Object Name, Status
Write-Host "Website Status:"
Get-Website -Name "MVCApp" | Select-Object Name, State, PhysicalPath, ApplicationPool
Write-Host "Application Pool Status:"
Get-WebAppPoolState -Name "MVCAppPool"
# Attempt to start the site
Show-Progress "Site Startup" "Attempting to start the site..."
Start-Process "http://localhost:8080"
Write-Host "Script completed at $(Get-Date)"
Stop-Transcript
Read-Host -Prompt "Press Enter to exit"
In the setup script above, we attempt to download and install .NET framework as well as IIS in this Sandbox environment.
So, we double click sandbox_config.wsb
and wait for everything to finish so we can see our legacy app.
… and it fails.
It fails because Windows Sandbox is designed more for checking how unknown or potentially harmful applications behave, without risking your main system. It just does not allow us to install and run Web Services.
The good thing is that what we did had no impact on our system because once you close the Sandbox windows, everything is wiped out.
Wit this successful failure, we now turn our attention to Docker.
Docker
First, we need to download and install Docker Desktop from docker.com.
When you are installing Docker Desktop, also follow the prompts to install WSL2. Windows Subsystem for Linux (WSL) uses less resources than a Hyper-V virtual machine, and you can always switch between the two anyway.
After installing Docker Desktop, you will need to restart Windows. Once everything is done, this is the screen you will get:
Since we want to run an MVC app in Windows, we need to switch to Windows containers. To do that we need to right-click the Docker icon on the system tray and select switch to Windows containers option.
Next, we need to create an image - Windows image that is. Create a folder for your legacy app and create a file named Dockerfile
(no extensions) there and copy this code:
# Use Windows Server Core as base image
FROM mcr.microsoft.com/windows/servercore:ltsc2019
# Install IIS, ASP.NET 4.5, and necessary features
RUN powershell -Command \
Add-WindowsFeature Web-Server; \
Add-WindowsFeature Web-Asp-Net45; \
Add-WindowsFeature NET-Framework-45-Core; \
Add-WindowsFeature Web-Mgmt-Console; \
Add-WindowsFeature WAS-Process-Model; \
Add-WindowsFeature Web-Http-Redirect; \
Add-WindowsFeature Web-Log-Libraries; \
Add-WindowsFeature NET-Framework-45-ASPNET; \
Add-WindowsFeature Web-Net-Ext45
# Download and install .NET Framework 4.8
RUN powershell -Command \
$ProgressPreference = 'SilentlyContinue'; \
Invoke-WebRequest -Uri "https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe" -OutFile C:\dotnet-framework-installer.exe; \
Start-Process -FilePath C:\dotnet-framework-installer.exe -ArgumentList '/q', '/norestart' -Wait; \
Remove-Item -Force C:\dotnet-framework-installer.exe
# Set working directory
WORKDIR /inetpub/wwwroot
# Copy your app files
COPY . .
# Set up the website in IIS
RUN powershell -Command \
Import-Module WebAdministration; \
Set-ItemProperty 'IIS:\AppPools\DefaultAppPool' -Name processModel.identityType -Value LocalSystem; \
Remove-Website -Name 'Default Web Site'; \
New-Website -Name 'MVCApp' -Port 80 -PhysicalPath 'C:\inetpub\wwwroot' -ApplicationPool 'DefaultAppPool'
# Set services to start automatically
RUN powershell -Command \
Set-Service -Name WAS -StartupType Automatic; \
Set-Service -Name W3SVC -StartupType Automatic
# Expose port 80
EXPOSE 80
# Create a simple startup script
RUN powershell -Command \
Set-Content -Path C:\start.ps1 -Value 'Start-Service WAS; Start-Service W3SVC; while ($true) { Start-Sleep -Seconds 60 }'
# Set the entry point to our startup script
ENTRYPOINT ["powershell", "-File", "C:\\start.ps1"]
We install Windows using this command:
# Use Windows Server Core as base image
FROM mcr.microsoft.com/windows/servercore:ltsc2019
This is much much faster because it downloads a pre-built Windows image which has either .NET Framework 4.8 installed or, using Windows Features to enable .NET Framework.
RUN powershell -Command \
Add-WindowsFeature NET-Framework-45-ASPNET; \
Add-WindowsFeature Web-Asp-Net45
Then open a command prompt and run this command:
docker build -t my-windows:mvc .
And our image will start building.
In the command line above, “my-windows” is my image name and mvc
, is the tag. When build more versions, you can use different tags to keep them organized.
Once the image building is done, you will get a friendly screen telling you that all is good. After that run your image either from Docker Desktop or command line:
docker run -d -p 80:80 --name mvc-container my-windows:mvc
You can check Docker logs to see if there are any issues:
docker logs mvc-container
Provided that everything is working fine, you should be able to access the website from your browser:
We get an internal server error because we did not satisfy all the conditions to run Nerd Dinner, such as ELMAH and SQL Server Express to say the least.
Installing SQL Server is a pretty complex exercise in a container. To resolve an issue like SQL Server (or Express) you have a few options:
Install SQL Server Express in your container (this can be complex and is generally not recommended for containerized applications).
Modify your application to use a different database that's more container-friendly, such as SQL Server LocalDB, SQLite, or an in-memory database for testing.
Update your connection strings to point to an external SQL Server instance that your container can access.
If this is just for testing and you don't need actual database functionality, you could temporarily modify your application to not require a database connection.
Conclusion
In this article, we looked at how to run a legacy application in our system without having to clutter our operating system.
Windows Sandbox is a great concept, however its limitations, especially those around security and permissions make it hard to use to test any web applications that require complex setup. Windows Sandbox, while limited for web applications, could still be useful for quick tests of desktop applications or scripts.
Docker, is more promising and we managed to run our application successfully inside a container. This is a pretty straight-forward and lightweight solution. However, beware that there is a learning curve with Docker, especially if things do not go as planned. Since there is no GUI, you need to interact with the container either through command line or PowerShell.
And then there is the virtual machines. While we did not try this one out, I think for Windows it is probably the most feasible solution as you can install an instance of SQL Server and/or Visual Studio/Visual Code for debugging. This solution, however, is resource intensive and not lightweight at all.
So, to sum it up:
For quick, lightweight testing of simple web apps, Docker is often the best choice.
For complex applications requiring full database support and debugging capabilities, virtual machines are typically more suitable.
However, the best solution can vary based on specific project requirements, team expertise, and available resources. Docker can handle more complex setups with proper configuration, and there are ways to debug applications in containers.
Windows Sandbox, while limited for web applications, could still be useful for quick tests of desktop applications or scripts.