Getting started with Dev Containers (W/ .NET, AZ CLI and Terraform)
In this series, we are going to be building an .NET API which encrypts and decrypts files using a key from Azure Key Vault, we are going to be showing how to build a CI/CD pipeline...
In this series, we are going to be building an .NET API which encrypts and decrypts files using a key from Azure Key Vault, we are going to be showing how to build a CI/CD pipeline, implement testing, static code analysis, implement authentication and deploy to Azure. It will equip you with all the tools you need to implement a project on your own end to end.
First of, we’re starting with creating a Dev Container…
But Jade, What Exactly IS a Dev Container?
Well, I’m glad you asked! You see, setting up a consistent and reliable development environment used to be tricky...
I remember at one company I worked at, it took me more than a week for me to get my set up configured because the system was so complex and documentation was so poor.
That’s where Dev Containers come in. They simplify the process by ensuring every developer has the right tools and dependencies installed, regardless of what's already on their machine. Dev Containers isolate the development environment, containing only the specific tools and configurations needed for the project.
No complicated set ups.
No long winded readmes.
No “ThIS vErSiOn iSn’T cOmPaTiBlE wItH mY mAcHiNe.”
Just quick, easy, and efficient.
This week, we’ll walk through how to create a simple setup for .NET 8 development. It’s ideal for starting new projects or improving existing ones, ensuring you have all the tools you need at your fingertips.
This configuration uses the mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm base image, which provides a robust foundation for .NET development. It will include the Azure CLI (version 2.67.0) and Terraform (version 1.10.3), enabling resource management and Infrastructure-as-Code capabilities directly within the container. Additionally, we will automatically be installing essential VS Code extensions like the .NET Dev Kit, Intellicode for C#, and Terraform support, optimizing your development workflow.
Prerequisites
Before setting up the containerized environment, make sure you have the following installed:
VS Code - The primary editor for this workflow, available at code.visualstudio.com.
Dev Containers Extension - Adds support for running development containers within VS Code.
Docker - Provides the underlying container runtime. Download it from docker.com.
With these tools installed, you’re ready to launch the configured environment.
Setting Up The Dev Container Configuration
To create a consistent, reusable development environment, you’ll need a devcontainer.json file. This file defines everything your Dev Container will include.
Let’s walk through the key components of the one we’re setting up today.
1. The name Field
The name field defines a label for your container environment, helping you easily identify it in your project.
"name": ".NET Key Vault Sample"This name appears in VS Code’s interface, allowing you to distinguish this container from others you might be using. In our case, it’s named “.NET Key Vault Sample” to reflect the project we’re going to be building in the following tutorials.
2. The image Field
The image field specifies the base image for our container. This image acts as the foundation, pre-configured with tools and settings for our development needs.
"image": "mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm"This configuration uses Microsoft’s official .NET 8 Dev Container image. It’s optimized for .NET development and includes the .NET SDK and runtime. The bookworm tag refers to the stable Debian-based version of the image, ensuring compatibility with most development tools.
We have chosen to select the bookworm tag specifically rather than just 9.0 to ensure greater control over the environment and to avoid unexpected issues if Microsoft updates the base image for 9.0 to use a different operating system in the future (unlikely, but being explicit is never a bad thing).
You can visit here to see the possible options for .NET.
3. The features Section
The features section is where we specify additional tools or capabilities to be included in the container. These features are added on top of the base image.
"features": {
"ghcr.io/devcontainers/features/azure-cli:1": {
"version": "2.67.0"
},
"ghcr.io/devcontainers/features/terraform:1": {
"version": "1.10.3"
}
}Because we’re going to be deploying a key vault to Azure in further lessons, we need the following also installing:
Azure CLI Version
2.67.0- This allows us to manage Azure resources from the container’s terminal.Terraform Version
1.10.3- This is used for Infrastructure-as-Code tasks, enabling us to provision cloud resources, specifically Azure in our case.
These tools are automatically installed when the container is built, saving manual setup and consistency between each developer’s environment.
But Jade, why specify versions instead of using
latest?
Specifying exact versions for tools like the Azure CLI and Terraform is a best practice. It ensures that your development and deployment workflows remain predictable and stable. If you rely on the latest tag, there’s a risk that future updates could introduce breaking changes or unexpected behaviours, which might disrupt your team’s workflow. By locking the version, you gain control over your environment and avoid surprises when collaborating across multiple developers or environments.
You can visit here to get a list of features you can add to a dev container.
4. The customizations Section
The customizations section (it pains me to spell it in American English) configures tools and extensions for your development environment, making it ready for immediate use.
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csdevkit",
"ms-dotnettools.vscodeintellicode-csharp",
"hashicorp.terraform" ]
}
}
}These are the ones we’re going to be installing:
ms-dotnettools.csdevkit: Adds debugging, project templates, and productivity features for .NET.ms-dotnettools.vscodeintellicode-csharp: AI-powered suggestions to improve C# coding efficiency.hashicorp.terraform: Provides syntax highlighting and IntelliSense for Terraform configuration files.
By specifying these extensions, VS Code will automatically install and activate them inside the container.
My rule of thumb is to use extensions for tools that enhance the development experience, such as code syntax highlighting, IntelliSense, and other visual aids. On the other hand, I rely on features and Dockerfiles to handle the installation of core languages and frameworks (we’ll go more into Dockerfiles later).
How to Build and Use the Dev Container
Step 1: Creating the Configuration File
In your project’s root directory, create a folder called
.devcontainer.Inside the folder, create a file named
devcontainer.json.Add the code we’ve broken down into the
devcontainer.jsonfile.
Step 2: Reopening Your Project in the Dev Container
Open the project folder in VS Code.
When prompted, click “Reopen in Container”. If you don’t see a prompt, open the Command Palette (
Ctrl+Shift+PorCmd+Shift+Pon macOS) and search for “Reopen Folder in Container”.VS Code will:
Pull the
dotnet:9.0-bookwormimage.Install the specified features (Azure CLI and Terraform).
Add the listed VS Code extensions.
Step 3: Verifying the Setup
To confirm the setup is working:
Verify Azure CLI: Open the integrated terminal and run:
az --versionThe output should show version 2.67.0.
Verify Terraform: Run:
terraform --versionThe output should indicate version 1.10.3.
Check VS Code Extensions: Open the Extensions view (
Ctrl+Shift+XorCmd+Shift+Xon macOS) and ensure the following are installed:
.NET Dev Kit
Intellicode for C#
Terraform
What Have We Achieved?
This configuration streamlines your development process by ensuring:
A consistent environment across machines and team members.
All tools are pre-installed and ready to use.
Quick onboarding for new developers. They can just clone the project and open it in VS Code (providing they have the pre-requisites installed).
With these steps, we have a fully functional .NET development environment that saves time and avoids manual setup headaches.
In our upcoming newsletter, we’ll explore how we can re-write this as a Dockerfile instead of using a pre-built image. This can be useful if we want to run a Dev Container as a base image of a pipeline.
That’s all for today’s post! If you liked this, check out the video where I go through it too:


