Build a Repeatable Dev Container for PowerShell, Terraform, and Azure CLI
Define PowerShell, Terraform, and Azure CLI as Dev Container Features on an Ubuntu 24.04 base so the toolchain can be rebuilt from configuration instead of hand-installed scripts.
Expected Outcome
A version-controlled .devcontainer/devcontainer.json that rebuilds a PowerShell, Terraform, and Azure CLI workspace with current Dev Container Features and modern VS Code customization syntax.
Assumptions
Visual Studio Code with the Dev Containers extension, or another tool that implements the Dev Container specification.
Docker or another supported container runtime available to the Dev Container tool.
Git and a project directory you can place under version control.
Network access to Microsoft Container Registry and GitHub Container Registry.
Bill of Materials
The Dev Container specification and Features documentation.
The official `devcontainers/features` collection.
A terminal inside the rebuilt container for version and authentication checks.
Build Steps
- Create the project and Dev Container directory
Keep the environment definition with the project so another workstation or CI runner can inspect the same configuration.
Changes system state: review before running
mkdir -p devcontainer-project/.devcontainer cd devcontainer-project git init
- Define the toolchain with Dev Container Features
Use the maintained Dev Container Features for Terraform, Azure CLI, and PowerShell instead of downloading old tool binaries or piping a remote Azure CLI installer into a Docker build. The Ubuntu 24.04 base is explicit because the moving ubuntu tag can change underneath the project; pin the image digest and Feature patch versions as well if you need bit-for-bit reproducibility.
Read-only command: verify target and scope
cat > .devcontainer/devcontainer.json <<'EOF' { "name": "Ops PowerShell Terraform Azure CLI", "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04", "features": { "ghcr.io/devcontainers/features/terraform:1": {}, "ghcr.io/devcontainers/features/azure-cli:1": {}, "ghcr.io/devcontainers/features/powershell:2": {} }, "customizations": { "vscode": { "extensions": [ "ms-vscode.powershell", "hashicorp.terraform" ] } }, "remoteUser": "vscode" } EOF cat .devcontainer/devcontainer.json - Commit the environment definition before building
Record the configuration before the first build. That makes later toolchain changes reviewable instead of letting workstation state become the source of truth.
Read-only command: verify target and scope
git add .devcontainer/devcontainer.json git diff --cached -- .devcontainer/devcontainer.json
- Build and open the Dev Container
Open the folder in Visual Studio Code and run Dev Containers: Reopen in Container, or use an equivalent Dev Container implementation. The build should obtain the declared base image and Features; there is no project Dockerfile that downloads and executes the Azure CLI installer.
Read-only command: verify target and scope
code .
- Verify all three tools inside the container
In the container terminal, verify that each declared tool is present and record the actual versions produced by the build. A successful container build alone is not enough evidence that the intended command-line tools are usable.
Read-only command: verify target and scope
pwsh --version terraform version az version
- Validate the workspace behavior without changing Azure
Exercise the local CLIs before authenticating or applying infrastructure. Terraform formatting/validation and Azure CLI help are safe first checks; a real Azure login or Terraform apply should be a separate, intentional step with credentials and target scope understood.
Read-only command: verify target and scope
pwsh -NoLogo -Command '$PSVersionTable.PSVersion' terraform -help > /dev/null && echo 'Terraform CLI available' az --help > /dev/null && echo 'Azure CLI available'
- Record stronger pins when the environment must be reproducible
Major Feature tags such as :1 and :2 intentionally receive compatible updates. For a regulated or long-lived build, record the known-good base image digest and pin Feature patch tags supported by the registry, then review those pins as dependencies are updated. Configuration-level repeatability and immutable reproducibility are different requirements.
Validation
Confirm the environment definition contains only the intended base image, Features, and editor customizations.
Read-only command: verify target and scope
cat .devcontainer/devcontainer.json
Confirm the rebuilt container exposes all required CLIs.
Read-only command: verify target and scope
pwsh --version terraform version az version
Rebuild the container once after saving the configuration and repeat the version checks to prove the environment can be recreated from the repository definition.
Troubleshooting
If a Feature fails to install, inspect the Dev Container build log and the Feature's current documentation before adding manual curl/install commands to the project. Feature compatibility can change with the base distribution.
If a host using a moving Ubuntu base begins failing after a distribution change, compare the explicit base release first. This Lab deliberately uses Ubuntu 24.04 rather than the moving `ubuntu` tag so an LTS transition does not silently change the base OS.
If a CLI exists on the host but not in the container, run the version checks from the Dev Container terminal and verify the Feature identifiers in `.devcontainer/devcontainer.json`.
Cleanup or Rollback
Close the Dev Container and remove its disposable container through your container runtime or Dev Containers tooling. The project definition remains in Git and can rebuild the environment later.
Do not delete Terraform state, cloud credentials, or mounted project data as part of generic container cleanup. Treat those as separate assets with their own retention and security requirements.
Next Improvements
Add organization-approved Azure authentication only after the local toolchain is validated.
Pin the base image digest and Feature patch versions when stronger reproducibility is required.
Add a small Terraform validation workflow or PowerShell test suite so CI can verify the same repository-defined toolchain.
