0:00
/
0:00
Transcript

Getting started with Dev Container Dockerfiles (W/ .NET, AZ CLI and Terraform)

In the previous post, we set up a Dev Container for our project using a pre-built .NET image. It was a quick and easy way to standardize development environments. Today, we’re stepping up by...

Welcome Back to the Series: Building .NET Key Vault API

In the previous post, we set up a Dev Container for our project using a pre-built .NET image. It was a quick and easy way to standardize development environments. Today, we’re stepping up by creating a custom Dockerfile. This approach not only gives us full control over the environment but also allows us to use the same image in our CI/CD pipeline for consistent builds.

By aligning the development environment with the pipeline, we eliminate "works on my machine" issues and ensure seamless integration across workflows.

Let’s get into it.

Tech Unfiltered is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.


Why a Custom Dockerfile?

While a pre-built image like mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm is convenient, it can be limiting when:

  • You need specific versions of tools like Terraform or Azure CLI.

  • Your project has unique dependencies not included in the base image.

  • You want to reuse the same environment for both development and pipelines.

With this approach, the Dockerfile serves as a single source of truth, ensuring consistency across local development and automated pipelines.

Great point! Incorporating the pipeline use case adds more context and utility to the newsletter. Here's the updated version, weaving in how the custom image serves as the base for a pipeline:


Custom Dockerfile for .NET Key Vault API

Let’s break down the Dockerfile step by step.

1. Base Image

We start by defining the base image for the container:

FROM mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm
  • Why this image? It’s purpose-built for .NET 9 development, providing the runtime, SDK, and essential tools.

  • Version Control - The bookworm tag ensures stability, using the latest Debian-based environment for .NET.


2. Avoid Installation Prompts

To streamline package installations, we set the environment to noninteractive mode:

ENV NONINTERACTIVE=1

This avoids prompts during installation that could block the build process.


3. Installing Terraform: Step-by-Step Breakdown

Purpose:
Terraform is a tool for defining and provisioning infrastructure as code (IaC). In this project, it will help automate the creation and management of Azure resources like Key Vaults.

Here’s the relevant section of the Dockerfile:

ARG TERRAFORM_VERSION=1.10.3-1

RUN wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg && \
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list && \
    apt-get update && \
    apt-get install -y terraform=${TERRAFORM_VERSION} && \
    apt-mark hold terraform && \
    terraform -v
What’s Happening?
  1. ARG TERRAFORM_VERSION=1.10.3-1 - Defines the Terraform version as a build argument.

  2. Adding HashiCorp’s GPG Key - HashiCorp signs its packages with a GPG key. This command downloads the key and stores it in a secure location.

wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
  1. Setting Up the Repository - Adds HashiCorp’s package repository to the system, scoped to the architecture (amd64) and distribution ($(lsb_release -cs) outputs your Linux distro's codename, e.g., bookworm for Debian 12).

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
  1. Updating and Installing Terraform - Updates the package manager and installs the specific version of Terraform.

apt-get update && \
apt-get install -y terraform=${TERRAFORM_VERSION}
  1. Version Locking - Prevents the installed version of Terraform from being updated inadvertently.

apt-mark hold terraform
  1. Version Check - Verifies that the correct version of Terraform is installed.

terraform -v

If you would like to see the list of available versions you can do so here.


4. Installing Azure CLI: Step-by-Step Breakdown

Purpose:
The Azure CLI allows you to interact with Azure resources from the terminal. In this project, it’s essential for managing resources like Key Vaults and service principals.

Here’s the corresponding Dockerfile section:

ARG AZURE_CLI_VERSION=2.67.0

RUN wget -O- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft.asc.gpg && \
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.asc.gpg] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/azure-cli.list && \
    apt-get update && \
    apt-get install -y azure-cli=${AZURE_CLI_VERSION}-1~$(lsb_release -cs) && \
    apt-mark hold azure-cli && \
    az -v
What’s Happening?
  1. ARG AZURE_CLI_VERSION=2.67.0 - Defines the Azure CLI version as a build argument, providing flexibility to set different versions if needed.

  2. Adding Microsoft’s GPG Key - Downloads and stores Microsoft’s GPG key, used to verify the authenticity of Azure CLI packages.

wget -O- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft.asc.gpg
  1. Setting Up the Repository - Add Microsoft’s package repository to the system, scoped to the architecture (amd64) and the current Linux distribution ($(lsb_release -cs)).

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.asc.gpg] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/azure-cli.list
  1. Updating and Installing Azure CLI - Update the package list and installs the specified Azure CLI version.

apt-get update && \
apt-get install -y azure-cli=${AZURE_CLI_VERSION}-1~$(lsb_release -cs)
  1. Version Locking - Lock the installed Azure CLI version to prevent unwanted updates.

apt-mark hold azure-cli
  1. Version Check - Verify the installation by displaying the Azure CLI version.

az -v

If you would like to see the list of available versions you can do so here.


5. Clean Up

Finally, we clean up unnecessary files to reduce the image size:

RUN apt-get clean && rm -rf /var/lib/apt/lists/*

This step ensures the resulting image is as lightweight as possible.


Key Features of the Dockerfile

  1. Base Image:

    • Image - mcr.microsoft.com/devcontainers/dotnet:9.0-bookworm

    • Purpose-built for .NET 9 development, this image provides a stable foundation.

  2. Terraform Installation:

    • Version - 1.10.3

    • Ensures compatibility for Infrastructure-as-Code (IaC) tasks.

  3. Azure CLI Installation:

    • Version - 2.67.0

    • Allows us to manage Azure resources directly from the container.

  4. Reusable for Pipelines:

    • This Dockerfile isn’t just for local development. The same image can be built and pushed to a container registry (e.g., Azure Container Registry, Docker Hub) to power your pipeline.


Dev Container Configuration

Here’s how we connect the Dockerfile to our VS Code environment using devcontainer.json:

{
    "name": ".NET Key Vault Sample",
    "dockerFile": "Dockerfile",
    "customizations": {
        "vscode": {
            "extensions": [
                "ms-dotnettools.csdevkit",
                "ms-dotnettools.vscodeintellicode-csharp",
                "hashicorp.terraform"
            ]
        }
    }
}

What are we doing:

  • Custom Dockerfile Integration - The dockerFile field ensures VS Code uses our custom image.

  • Extensions - Adding tools like .NET Dev Kit, Intellicode, and Terraform support for an optimized development experience.


What We’ve Achieved

With this setup, we now have:

  1. A Consistent Environment - Same tools and configurations for development and CI/CD.

  2. Version Stability - Locked-down versions of .NET, Terraform and Azure CLI prevent unexpected updates and breaking changes, ensuring reliable workflows.

  3. Custom Control - Flexibility to add or modify tools as the project evolves.


What’s Next?

In our next post, we’ll explore how to get up and running with key vaults within the portal before then showing you how to do that in terraform.

Ready to try this out? Let me know your thoughts, and don’t forget to subscribe for the next installment!


If you liked this post, don’t forget to check out my YouTube channel and video here…

Tech Unfiltered is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.