Skip to content
nimat-devPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

DevOps Toolbox (containerized CLIs for a locked-down Windows box)

CI

One Docker image with every senior-DevOps CLI baked in, plus PowerShell wrappers (and bash wrappers for Git Bash, WSL, Linux and macOS) so az, kubectl, terraform, flux, helm, kustomize and azd run inside the container but feel local. Nothing to install on the host except Docker Desktop, which you already have. Credentials and config persist in a named volume, so you log in once, from either shell. Short commands (k, kgp, tfp, azl ...), history suggestions and Tab completion come with it, in PowerShell, in bash and in the toolbox shell.

Tools included: Azure CLI, azd, kubectl, kubelogin, Helm, Kustomize, Terraform, Flux CLI, AWS CLI v2, GitHub CLI (gh), kubectx/kubens, plus jq, yq, git, ssh, make.


Prerequisites

  • Docker Desktop running (WSL2 or Hyper-V backend, either is fine).
  • PowerShell (Windows PowerShell 5.1 or PowerShell 7+), or bash 4.2 or newer (Git Bash, WSL, Linux, macOS with a newer bash). The two work side by side and share the image and the logins.
  • No admin rights or other installs required.

Quick start: one command

Open PowerShell in this folder and run:

powershell -NoProfile -ExecutionPolicy Bypass -File .\setup.ps1

setup.ps1 runs every command from the steps below, in order, and then checks the tools:

  1. checks that Docker Desktop is running with Linux containers
  2. creates .env from .env.example (an existing .env is kept)
  3. builds the image (docker compose build)
  4. adds the Import-Module line to your PowerShell profile (a line left over from a moved folder is updated, and a .bak copy of the profile is saved first)
  5. loads the wrappers, the shortcuts, Tab completion and history suggestions (see Shortcuts, history suggestions and Tab completion), and makes ~/.devtools-aliases.ps1 for your own shortcuts (never overwritten)
  6. runs az, kubectl, terraform, helm, flux, kustomize, azd, gh, kubectx and kubens once each, in the container, to prove they work, and checks that the image has the Tab completion helper

Then it offers the Azure device-code login and the Docker Desktop Kubernetes import. It is safe to run again whenever you like: every step checks before it changes anything.

Option What it does
-SkipBuild the image is already built
-SkipProfile leave your PowerShell profile alone
-SkipVerify skip the tool checks
-Login run az login --use-device-code at the end without asking
-ImportKube import Docker Desktop's Kubernetes context at the end without asking
-NoPrompt never ask a question (for automation)
-ProfilePath <file> edit this profile instead of your all-hosts profile

Exit code: 0 all good, 1 setup could not finish, 2 setup finished but a tool check failed.

That command runs the script in its own PowerShell process, so open a new PowerShell window afterwards: your profile loads the tools there. (If you start it from your own prompt as .\setup.ps1, after Set-ExecutionPolicy -Scope Process Bypass, the tools are loaded in that window too.) If Group Policy blocks scripts altogether, or PowerShell runs in Constrained Language Mode, the wrappers cannot work there: build with docker compose build and use docker compose run --rm dev for a shell inside the toolbox.

To undo all of this later, run uninstall.ps1 (see Stop and remove it).

Prefer bash (Git Bash, WSL, Linux, macOS)? Run ./setup.sh instead. The Git Bash walkthrough goes through it one step at a time. A few helper commands are named differently in the two shells, and a command typed in the wrong one fails: see PowerShell or bash: which commands go where.

The manual steps, one at a time:

1. Build the image

cd devtool
copy .env.example .env          # optional; edit versions / proxy / repos path
docker compose build            # or: docker build -t devtools:latest .

First build pulls a lot (Azure CLI, Terraform, AWS CLI, etc.) and takes a few minutes. Rebuilds are cached and fast.

On a corporate network with TLS interception, the build will fail on certificate errors during the HTTPS downloads. See Troubleshooting.

2. Wire up the PowerShell wrappers

Add one line to your profile so the wrappers load in every new shell:

if (!(Test-Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force }
notepad $PROFILE

Add (adjust the path to where this folder lives):

Import-Module 'C:\path\to\devtool\Devtools.psm1' -Force

Reopen PowerShell. Now az, kubectl, terraform, etc. are the container versions. Because there's no local az on this machine, these functions simply become the default, no PATH changes needed. Check what they point at:

Get-DevToolsInfo

3. First login (device code, no browser in the container)

az login --use-device-code

Opens a code + URL; complete it in any browser. The token lands in ~/.azure on the persistent volume, so it survives across every future command and shell.

Everyday use

az account show
terraform init           # runs against the folder you're standing in
terraform plan -out tf.plan
kubectl get pods -A
helm upgrade --install myapp ./chart
flux get kustomizations

These lines are typed the same way in bash.

Your current directory is mounted at /work inside the container, so file-based commands (terraform, kubectl apply -f, helm ./chart) act on the folder you're in. For a full interactive shell in the toolbox:

dev                      # drops you into zsh with the same mounts (dev bash for bash)

AKS + kubelogin flow

az aks get-credentials --name <cluster> -g <rg> --overwrite-existing
kubectl get nodes        # kubelogin handles the AAD token automatically

Both az and kubectl share the same /root volume, so the kubeconfig written by get-credentials and the AAD token cache from kubelogin are right there for every later kubectl call.

PowerShell or bash: which commands go where

PowerShell and bash run the same toolbox: the same image, the same volume, the same logins. The tool names (az, kubectl, terraform ...) and the shortcuts (k, kgp, tfp, azl ...) are typed the same way in both. A few helper commands are named differently, and each shell only knows its own. The prompt tells you which one you are in:

PowerShell (Windows PowerShell 5.1 or 7) bash (Git Bash, WSL, Linux, macOS)
The prompt looks like PS C:\Users\you> you@PC MINGW64 ~ in Git Bash, you@host:~$ elsewhere
Set it up, once powershell -NoProfile -ExecutionPolicy Bypass -File .\setup.ps1 ./setup.sh
Is az the toolbox's? Get-Command az (CommandType says Function) type az (says az is a function)
Image, volume, tools Get-DevToolsInfo devtools-info
List the shortcuts Get-DevToolsAlias or Get-DevToolsAlias 'kg*' devtools-alias or devtools-alias 'kg*'
Import Docker Desktop's Kubernetes Import-DockerDesktopKube devtools-import-kube
A shell inside the toolbox dev dev
Your own shortcuts ~/.devtools-aliases.ps1 ~/.devtools-aliases.sh
Switch the tools off in this window Remove-Module Devtools devtools-unload
Remove it for good .\uninstall.ps1 ./uninstall.sh

A command from the other column fails with an error that looks like a broken install, but nothing is broken. devtools-alias typed in PowerShell says not recognized as the name of a cmdlet, and Get-DevToolsAlias typed in bash says command not found. type az in PowerShell says Cannot find path 'C:\Users\you\az', because there type is short for Get-Content and it looks for a file called az. If Get-DevToolsAlias is not recognized in PowerShell either, see Troubleshooting.

Using it from bash (Git Bash, WSL, Linux, macOS)

The same toolbox for bash. If Git for Windows is installed, Git Bash is the natural place for it on this laptop. az, kubectl, terraform, flux, helm, kustomize, azd, gh, kubectx and kubens become shell functions that run the container, so they win over any program of the same name that is installed (command az still reaches the installed one). The image and the volume with your logins are the ones PowerShell uses: log in from either, it shows in both.

Step by step in Git Bash on Windows

Git Bash comes with Git for Windows. If neither Git nor WSL is installed on the PC, use the PowerShell version: nothing in the toolbox needs bash.

  1. Unzip the project, for example to C:\Users\<you>\devtool (right-click the zip, Extract All).
  2. Start Docker Desktop and wait until it says "Engine running". It has to use Linux containers, which is the default.
  3. Open Git Bash in the project folder. In File Explorer open the folder, right-click an empty spot and choose Open Git Bash here (on Windows 11 it is under Show more options). Without that menu entry, start Git Bash from the Start menu and cd there. In bash C:\ is written /c/, and the slashes lean the other way:
    cd /c/Users/<you>/devtool
    ~ is your Windows user folder, so a folder in Downloads is cd ~/Downloads/devtool. Put quotes around a path that has a space in it: cd "/c/Users/<you>/my tools/devtool".
  4. Run the setup.
    ./setup.sh
    If that says "Permission denied", run bash setup.sh. The first build takes a few minutes. If you already ran setup.ps1 the image exists and this step is quick. You should see something like this (the versions and the times differ):
    DevOps toolbox setup  (/c/Users/<you>/devtool)
    
    == 1/6  Docker
      OK    Docker engine 28.0.4, Linux containers
    
    == 2/6  Settings (.env)
      OK    created .env from .env.example (edit it to set a proxy or pin tool versions)
    
    == 3/6  Build the image (the first build takes a few minutes)
      OK    image devtools:latest built in 148s
    
    == 4/6  bash startup file
      OK    added the devtools.sh line to /c/Users/<you>/.bashrc
      OK    /c/Users/<you>/.bash_profile now loads ~/.bashrc, so login shells (Git Bash windows, ...) get the toolbox too
    
    == 5/6  Load the wrappers
      OK    az, kubectl, terraform, flux, helm, kustomize, azd, gh, kubectx, kubens and dev now run in the container
      OK    38 shortcuts: k, kgp, tfp, azl, aksx ...  (devtools-alias lists them all)
      OK    made a file for your own shortcuts: /c/Users/<you>/.devtools-aliases.sh
      OK    Tab completion for az, kubectl, terraform, helm, flux ... (the real tool answers from the container, ...)
      OK    history: type the start of a command, then Up or Down finds it
    
    == 6/6  Verify every tool (one container per tool)
      OK    az         azure-cli                         2.91.0
      OK    kubectl    Client Version: v1.31.4
      OK    terraform  Terraform v1.16.5
      ...
      OK    complete   Tab completion helper is in the image
    
    All set.
    
    Near the end it asks two questions, each [y/N] (Enter means no): sign in to Azure now, and import Docker Desktop's Kubernetes context now. Both can wait: step 6 shows the sign-in, and devtools-import-kube does the import whenever you like. To answer them up front, run ./setup.sh --login --import-kube; to never be asked, ./setup.sh --no-prompt.
  5. Open a new Git Bash window. A script cannot load anything into the window it was started from. The new window loads the toolbox from ~/.bashrc (setup also made sure Git Bash reads that file). To use it in the current window instead, run the line setup printed at the end, for example . '/c/Users/<you>/devtool/devtools.sh'.
  6. Check it.
    type az                       # az is a function
    devtools-info                 # the image, the volume, the tools
    devtools-alias                # the shortcuts
    az login --use-device-code    # sign in (the shortcut is azl); the token stays in the volume
    type az has to say az is a function. If it says az is /c/... or not found, the toolbox is not loaded in this window: open a new one, or run the line from step 5.

In Git Bash ~ is your Windows user folder (echo ~ shows it; some company set-ups point it elsewhere), so ~/.bashrc is C:\Users\<you>\.bashrc and your own shortcuts file, ~/.devtools-aliases.sh, is next to it. Your logins live in the Docker volume devtools-home, which PowerShell uses too, so you can sign in from either shell.

What setup.sh does, and its options

Same steps as setup.ps1, in the same order: checks that Docker runs Linux containers, makes .env, builds the image, adds one line to ~/.bashrc (a .bak copy is saved when it has to change an older line), loads the wrappers, makes ~/.devtools-aliases.sh for your own shortcuts (never overwritten) and runs every tool once. Git Bash windows and macOS Terminal start login shells, which read ~/.bash_profile and not ~/.bashrc, so if your login file does not load ~/.bashrc setup adds the two lines that do. It is safe to run again.

The line it adds to ~/.bashrc is . '/path/to/devtool/devtools.sh'. A script cannot load anything into the terminal it was started from, so open a new terminal afterwards, or run that line.

Option What it does
--skip-build the image is already built
--skip-profile leave your startup files alone
--skip-verify skip the tool checks
--login run az login --use-device-code at the end without asking
--import-kube import Docker Desktop's Kubernetes context at the end without asking
--no-prompt never ask a question (for automation)
--rc-file FILE edit this file instead of ~/.bashrc

Exit code: 0 all good, 1 setup could not finish, 2 setup finished but something above failed.

What is called differently from PowerShell

The tools and the shortcuts are typed the same way. The helper commands have bash names: devtools-info, devtools-alias, devtools-import-kube [CONTEXT] [KUBECONFIG] and devtools-unload. They are listed next to their PowerShell names in PowerShell or bash. devtools-alias 'kg*' filters the list and devtools-alias --count counts it. dev opens zsh inside the toolbox (dev bash for bash). Your own shortcuts go in ~/.devtools-aliases.sh: aliases or functions, read last, so it wins.

The shortcuts are the same list (scripts/shell/aliases.sh is the file both the toolbox shell and your bash read), and they are aliases, so kgp -n kube-system runs kubectl get pods -n kube-system and kex web -- sh keeps its --. Aliases only expand in an interactive shell; in a script call the tool itself, or run shopt -s expand_aliases first. Set AKS_RG and AKS_NAME once (for example export AKS_RG=my-rg AKS_NAME=my-cluster in ~/.devtools-aliases.sh) for aksc, aksup and aksx. (The shell inside the toolbox reads a ~/.devtools-aliases.sh of its own, kept on the volume. It is not the file in your home folder.)

Two things bash does differently. History: bash cannot draw a grey suggestion while you type, so the Up and Down arrows search the history for what you have typed instead (type kgp, press Up). Only the default arrow bindings are replaced; a binding of your own is left alone. The shell inside the toolbox (dev) has the grey suggestions. Tab completion: az, kubectl, terraform, helm, flux, kustomize, azd and gh, and the shortcuts for them, complete through the real tool in a short-lived container, about a second per press (a few for az), exactly as in PowerShell. The first Tab lists the choices when there are several.

On Git Bash, devtools.sh stops Git Bash from rewriting /work, /subscriptions/... or any argument that starts with a slash into a Windows path, and mounts the current folder in the form Docker for Windows wants (C:/Users/you/project). In the plain Git Bash window (mintty) a program that needs a terminal, such as az login, kubectl exec -it or dev, only works through winpty, which Git for Windows ships; devtools.sh uses it there automatically. winpty rewrites every argument that looks like a path on its own account (-w /work became -w C:/Users/you/AppData/Local/Programs/Git/work) and cannot be told not to, so docker's arguments never go past it as they are. They go as one string of hex digits to scripts/devtools-pty-run.sh, which winpty starts, and which turns them back into arguments and runs docker.exe. That file has to stay in the scripts folder next to devtools.sh. The Windows Terminal and the VS Code terminal do not need winpty. In WSL, Docker Desktop must have WSL integration switched on for your distribution; Docker Desktop's kubeconfig is the Windows one, so use devtools-import-kube docker-desktop /mnt/c/Users/NAME/.kube/config there.

Variable Effect
DEVTOOLS_IMAGE, DEVTOOLS_VOLUME the image and the volume (defaults devtools:latest, devtools-home)
DEVTOOLS_BASH_ALIASES the file for your own shortcuts (default ~/.devtools-aliases.sh)
DEVTOOLS_NO_READLINE 1 leaves the arrow keys and readline settings alone
DEVTOOLS_WINPTY auto (default), 1 or 0: whether docker runs through winpty in Git Bash
AKS_RG, AKS_NAME your cluster, for aksc, aksup, aksx; dev hands them to the shell inside

Set them before the line in ~/.bashrc that loads the toolbox. devtools.sh needs bash 4.2 or newer: Git Bash and every current Linux have it; the bash that macOS ships is 3.2, so install a newer one (brew install bash). zsh is not supported by the host script; dev gives you zsh inside the toolbox. To undo it, run ./uninstall.sh (see Stop and remove it).

Shortcuts, history suggestions and Tab completion

All of this loads with the module, so setup.ps1 turns it on and uninstall.ps1 turns it off. The parts that run inside the toolbox come with the image, which setup.ps1 rebuilds. (This section is about PowerShell; bash has the same shortcuts and completion, see above.)

Shortcuts

Get-DevToolsAlias lists them. Whatever you type after a shortcut is added at the end, so kgp -n kube-system runs kubectl get pods -n kube-system.

Azure Runs
azl az login --use-device-code
azwho az account show -o table
azsubs az account list -o table
azsub NAME az account set --subscription NAME
azrg az group list -o table
azres RG az resource list -o table -g RG
acrls REGISTRY az acr repository list -o table -n REGISTRY
AKS and kubectl Runs
aksl az aks list -o table
k kubectl
kx, kn kubectx, kubens
kgp, kgpa kubectl get pods, kubectl get pods -A
kgn, kgd, kgs kubectl get nodes, deployments, services
kd kubectl describe
kl, klf kubectl logs, kubectl logs -f
kex POD -- sh kubectl exec -it POD -- sh
kaf FILE kubectl apply -f FILE
krr deploy/NAME kubectl rollout restart deploy/NAME
ktop kubectl top pods
Terraform Runs
tf terraform
tfi, tfv, tff terraform init, validate, fmt -recursive
tfp terraform plan -out=tfplan
tfa terraform apply tfplan (applies exactly the plan you just read)
tfo terraform output
tfs, tfss ADDRESS terraform state list, state show ADDRESS
tfw, tfws NAME terraform workspace list, workspace select NAME
tfdestroy terraform destroy (it still asks you to confirm)

There is no -auto-approve shortcut, on purpose.

Three AKS helpers take the resource group and the cluster name, either typed (aksc my-rg my-cluster) or from two variables that you set once. They stay on your laptop and are not in this repository:

$env:AKS_RG = 'my-resource-group'; $env:AKS_NAME = 'my-cluster'
Helper Runs
aksc az aks get-credentials -g RG -n NAME --overwrite-existing
aksup az aks get-upgrades -g RG -n NAME -o table
aksx kubectl get nodes az aks command invoke -g RG -n NAME --command "kubectl get nodes", which reaches a private cluster through Azure

PowerShell removes a bare -- before a function sees it. The wrappers put it back where you typed it, so kubectl exec -it web -- ls -la and kex web -- sh work as written.

Your own shortcuts

setup.ps1 makes ~/.devtools-aliases.ps1 once and never overwrites it. It is read after the built-in shortcuts, so what you define there wins:

function kgj  { kubectl get pods -o json @args }
function tfpl { terraform plan -out=tfplan @args }
$env:AKS_RG   = 'my-resource-group'
$env:AKS_NAME = 'my-cluster'

A mistake in the file shows as a warning and the toolbox still loads. Get-DevToolsAlias marks the shortcuts that are yours. Keep the file somewhere else by setting $env:DEVTOOLS_ALIASES before the import. Inside the toolbox shell the same list exists as shell aliases, and your own go in ~/.devtools-aliases.sh (that is on the volume, so it stays).

History suggestions

As you type, PowerShell shows the rest of an earlier command in grey; the Right arrow accepts it. That needs PSReadLine 2.1 or newer, which PowerShell 7 has. Windows PowerShell 5.1 ships 2.0, which cannot show grey text, so there the Up and Down arrows search your history for what you have typed instead. To get the grey text on 5.1 as well, update PSReadLine once (no admin rights; it needs the PowerShell Gallery, which some companies block), then open a new window:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-Module PSReadLine -Scope CurrentUser -Force -SkipPublisherCheck

Tab opens a menu of the choices; arrows pick, Enter accepts. Only defaults are replaced: a Tab binding or a suggestion source you set yourself is left alone, and everything is handed back when the module is removed. $env:DEVTOOLS_NO_READLINE = '1' before the import skips all of it.

Tab completion

az, kubectl, terraform, helm, flux (and kustomize, azd, gh) complete their commands and flags, and so do the shortcuts for them: kgp -n <Tab>, tf wor<Tab>. In PowerShell the real tool answers from a short-lived container, so a Tab press takes about a second (a few for az). Names that live in a system, such as pods or resource groups, are offered when the tool can reach it and you are logged in. If Docker is not running, the image is older than this feature or the tool takes too long, nothing is offered and file names complete as usual.

The toolbox shell

dev (and docker compose run --rm dev) opens zsh: grey suggestions from your history, colours (green when the command exists, red when it does not), a Tab menu, and the shortcuts above. dev bash opens bash with the same shortcuts and completion. The history is kept in the volume (~/.zsh_history), so it survives between sessions.

Variable Effect
DEVTOOLS_ALIASES the file for your own shortcuts (default ~/.devtools-aliases.ps1)
DEVTOOLS_NO_READLINE 1 leaves PSReadLine alone
AKS_RG, AKS_NAME your cluster, for aksc, aksup, aksx; dev hands them to the shell inside

Use Docker Desktop's Kubernetes (local cluster)

Instead of AKS, point the toolbox at the cluster built into Docker Desktop.

  1. Docker Desktop > Settings > Kubernetes > Enable Kubernetes > Apply. Wait until it shows Running. (If the option is missing or greyed out, an admin policy has it locked.)
  2. Rebuild once so the image has the helper scripts. Only the last layers change:
    docker compose build
  3. Reload the module and import the context:
    Import-Module 'C:\path\to\devtool\Devtools.psm1' -Force
    Import-DockerDesktopKube
    It ends with Connected to Kubernetes v1.xx when the container can reach the cluster.
  4. Use it like any other context:
    kubectx docker-desktop
    kubectl get nodes
    kubens
    (Prefer another name? kubectl config rename-context docker-desktop local.)

Why a helper instead of just copying the kubeconfig? Two things break inside a container:

  • The toolbox keeps its own kubeconfig on the volume and can't see the Windows one.
  • Docker Desktop's context points at kubernetes.docker.internal / localhost, which inside a container is the container itself. Import-DockerDesktopKube copies only the docker-desktop context (the rest of your host kubeconfig is never read into the volume), repoints it at host.docker.internal, and keeps TLS verification on by verifying against the original name (tls-server-name).

Behind a corporate proxy there is a third trap: kubectl would send host.docker.internal to the proxy, which can't reach your laptop. The image entrypoint appends the Docker Desktop host names to NO_PROXY (existing entries are kept), so this just works.

Re-run Import-DockerDesktopKube after Reset Kubernetes Cluster (the certificates change). It is safe to re-run any time; it replaces its own entries and leaves everything else (AKS contexts, etc.) alone. A backup of the previous file is kept as ~/.kube/config.bak on the volume.

If the import says the API server did not answer, it prints a direct probe result:

  • HTTP 000: nothing reachable. Check Kubernetes shows Running, and any VPN / firewall.
  • Any other code: the host is reachable, so the kubectl error above it is TLS or a proxy.

Adding a new tool

Edit the # ---- EXTRA TOOLS ---- block at the bottom of the Dockerfile (k9s, terragrunt, tflint, trivy examples are there, commented), then:

docker compose build

It's at the bottom on purpose: everything above stays cached, so adding a tool is a quick rebuild. To upgrade a pinned tool, bump its version in .env (or the ARG default) and rebuild.

Using the compose dev service instead of per-command wrappers

docker compose run --rm dev        # one-off shell
# or keep it up and exec in repeatedly:
docker compose up -d dev
docker compose exec dev zsh

Set REPOS_ROOT in .env to an absolute path (e.g. C:/Users/you/repos) to mount your real code at /repos; otherwise the project's ./repos folder is used.

Putting this on GitHub

This project is already a git repo with an initial commit. Since you can't install gh on the host, use the one baked into the toolbox. From the project folder:

docker compose build                 # if you haven't already
gh auth login                        # choose "Paste an authentication token" (a PAT), no browser needed
gh repo create devtool --private --source . --remote origin --push

That creates the repo under your account and pushes main in one shot. gh prints the URL. Auth persists in the volume, so you only log in once.

Plain-git alternative (if you'd rather create the empty repo on github.com first):

dev                                  # bash shell in the toolbox, project mounted at /work
# inside the container:
git remote add origin https://github.com/<you>/devtool.git
git push -u origin main              # use a PAT as the password when prompted

Stop and remove it: one command

uninstall.ps1 is the undo for setup.ps1. In the PowerShell window you use the tools in:

.\uninstall.ps1

If scripts are blocked, run powershell -NoProfile -ExecutionPolicy Bypass -File .\uninstall.ps1 instead, then Remove-Module Devtools in the window you were using. It does this, in order:

  1. removes the Import-Module line from your PowerShell profile (a .bak copy is saved first). It looks in the profiles of Windows PowerShell 5.1 and of PowerShell 7, which keep theirs in different folders
  2. unloads the wrappers from the window it runs in, so az, kubectl and the rest are the programs installed on the PC again (or "not recognized" when there are none)
  3. stops and removes the toolbox containers: docker compose down, then anything still running from the image, such as a dev shell open in another window
  4. only when you ask: deletes the image and your saved logins

The image takes minutes to build and the volume holds your Azure, GitHub and Kubernetes logins, so both are kept unless you ask. Your project folder and .env are never touched, and neither is your own shortcuts file (~/.devtools-aliases.ps1): it holds what you wrote, so the script only tells you where it is. With the profile line gone, new windows have no shortcuts, Tab completion or history settings from the toolbox. It works with Docker Desktop stopped (the profile and the window are cleaned first) and is safe to run again.

Option What it does
-RemoveImage also delete the image devtools:latest (setup.ps1 builds it again)
-RemoveVolume also delete the volume with your saved logins. Asks first
-Force do not ask before deleting the volume
-SkipProfile leave your PowerShell profile alone
-NoPrompt never ask a question (for automation); the volume is then deleted only with -Force
-ProfilePath <file> clean this profile instead of looking for yours

Exit code: 0 done, 1 could not run (Constrained Language Mode), 2 finished but something above failed. For everything at once: .\uninstall.ps1 -RemoveImage -RemoveVolume.

From bash it is ./uninstall.sh, with the same steps and the same exit codes. It takes the devtools.sh line (and the login-shell line setup.sh added) out of ~/.bashrc, ~/.bash_profile, ~/.bash_login and ~/.profile, saving a .bak copy of each file it changes, and leaves the rest of those files byte for byte as they were. Its options are --remove-image, --remove-volume, --force, --skip-profile, --no-prompt and --rc-file FILE. A terminal that is open keeps the functions until you close it or run devtools-unload in it. Your ~/.devtools-aliases.sh is kept.

PowerShell windows that were already open keep the wrappers until you close them or run Remove-Module Devtools in them. Only the default names are handled: the image devtools:latest and the volumes devtools-home (used by the wrappers) and devtools_devtools-home (the one docker compose run creates, because Compose prefixes volume names with the project name). A name you chose with DEVTOOLS_IMAGE or DEVTOOLS_VOLUME is left alone.

Tests and CI

Every push runs .github/workflows/ci.yml on GitHub's runners (the badge at the top shows the latest result). It runs three jobs:

  • Lint and wrapper tests (Linux): hadolint on the Dockerfile, shellcheck on every shell script, docker compose config, and the test scripts for the PowerShell and the bash wrappers, which run against a fake docker:
    • tests/Test-Devtools.ps1 runs the real Devtools.psm1 and checks the exact docker run line each wrapper produces (mounts, working directory, -out / -o pass-through, exit codes, env overrides), every shortcut against its table, the bare -- that PowerShell removes, your own shortcuts file (including a broken one), Tab completion against canned answers, and what is done to PSReadLine (new and old versions) and handed back.
    • tests/Test-Setup.ps1 runs setup.ps1 in a scratch copy of the project, once per situation it has to handle: first run, second run, project folder moved, Docker not running, Windows containers, docker missing, wrong folder, build failure, no docker compose, a broken tool, and each option. It checks the exit code, the messages, the docker calls and the profile file.
    • tests/Test-Uninstall.ps1 does the same for uninstall.ps1, and also runs setup.ps1 first to prove the profile comes back byte for byte as it was. It covers odd profile lines, a locked profile, wrappers loaded in the session, containers that will not go, a missing or stopped Docker, and each option including the question before the volume is deleted.
    • tests/pty_shell_test.py pwsh starts an interactive PowerShell 7 in a pseudo terminal with the profile line setup.ps1 writes, then types like a person: the Tab menu, Tab completion through a fake docker, grey suggestions from the history, kex pod -- ls -la, and the keys coming back when the module is removed.
    • tests/test-devtools-sh.sh runs the real devtools.sh in a child bash and checks the exact docker run line each function produces (volume, current folder, image, dev and the AKS variables, arguments with quotes, spaces and slashes, exit codes), the shortcuts, your own shortcuts file (including a broken one), Tab completion against canned answers, and that devtools-unload leaves nothing behind. tests/test-setup-sh.sh and tests/test-uninstall-sh.sh do for setup.sh and uninstall.sh what the PowerShell scripts above do for theirs, including that ~/.bashrc comes back byte for byte after setup and uninstall.
    • tests/pty_shell_test.py hostbash starts an interactive bash in a pseudo terminal with the line setup.sh writes and types like a person: Tab completion through a fake docker, the Up arrow finding earlier commands, -t reaching docker at a terminal, and the keys coming back after devtools-unload. A third session plays the plain Git Bash window: a stand-in winpty rewrites path arguments the way Git for Windows' does, and docker must still get -w /work and every other argument as typed.
    • The three bash test scripts run once more inside the official bash:4.2 and bash:4.3 images, the oldest bash the scripts promise to work with.
  • On Windows: all three PowerShell test scripts in Windows PowerShell 5.1 and in PowerShell 7 on a Windows runner, with a fake docker.exe. Windows runners cannot run Linux containers, so this covers the PowerShell side only: 5.1 syntax, parameter binding, native argument passing and Windows paths. The runner is thrown away after the job, so it is also where the real default profile folders of both PowerShell editions are tested (and put back afterwards). The three bash test scripts run there too, in Git Bash against the fake docker.exe, which is what proves that Git Bash does not rewrite /work or /subscriptions/... and that the folder is mounted in its Windows form. The runner also opens a hidden Git Bash window (mintty, which gives winpty the terminal it needs), and in it the tests call kubectx, kubectl exec and a few awkward argument lists through Git's real winpty: docker.exe must get -w /work and every other argument as typed, and the exit code must come back. They also print what winpty did to a few arguments.
  • Build the image and test it (Linux): a real docker build, then
    • tests/smoke.sh runs inside the image: every tool starts and reports the version pinned in the Dockerfile, the entrypoint extends NO_PROXY, the Terraform plugin cache is set up, and the interactive shells are in place (zsh and its plugins, the completion scripts, the aliases, and the answers devtools-complete gives for each tool).
    • tests/pty_shell_test.py types into zsh and into bash inside the image, in a pseudo terminal: the aliases, the grey suggestion, the colours and Tab completion for flux, gh, terraform, kubectl, helm and az.
    • tests/e2e-import.sh starts a fake Kubernetes API on the host with a certificate valid only for kubernetes.docker.internal, then checks that the container reaches it with TLS verified, that only the docker-desktop context is imported (a second context's secret never reaches the volume), that kubectx and kubens work against it, that a re-import is idempotent, and that a dead corporate proxy cannot swallow the host traffic.
    • setup.ps1 runs for real against the Docker engine of the runner (build, profile, every tool in its own container), and then once more to prove a second run changes nothing. So does setup.sh, followed by a new bash that loads the line it wrote: the tools are the container's, standard input and the current folder reach the container, and Tab completion gets real answers from kubectl, terraform, helm and az.
    • uninstall.ps1 runs for real against the same engine, with a container still running from the image and both volumes present: it stops the container and takes the profile line out, keeps the image and the logins, and then with -RemoveImage -RemoveVolume -Force deletes them. uninstall.sh does the first part (container and startup file) the same way.

Run the bash tests on your own machine (no Docker needed, and your own ~/.bashrc is not touched; Linux, macOS or Git Bash):

bash tests/test-devtools-sh.sh
bash tests/test-setup-sh.sh
bash tests/test-uninstall-sh.sh
python3 tests/pty_shell_test.py hostbash -- bash                     # Linux or macOS

Run the PowerShell tests on your own machine (no Docker needed, and your own profile is not touched):

powershell -NoProfile -ExecutionPolicy Bypass -File tests\Test-Devtools.ps1   # Windows PowerShell 5.1
powershell -NoProfile -ExecutionPolicy Bypass -File tests\Test-Setup.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File tests\Test-Uninstall.ps1
pwsh -NoProfile -File tests/Test-Devtools.ps1                                 # PowerShell 7, any OS
pwsh -NoProfile -File tests/Test-Setup.ps1
pwsh -NoProfile -File tests/Test-Uninstall.ps1

The image tests need Linux, macOS or WSL with Docker:

docker build -t devtools:ci .
docker run --rm -v "$PWD/tests:/tests:ro" devtools:ci bash /tests/smoke.sh
tests/e2e-import.sh devtools:ci                                    # needs openssl and python3
python3 tests/pty_shell_test.py zsh --full -- docker run --rm -it devtools:ci zsh
python3 tests/pty_shell_test.py bash --full -- docker run --rm -it devtools:ci bash
python3 tests/pty_shell_test.py pwsh -- pwsh -NoLogo                 # PowerShell 7 on Linux or macOS

What CI cannot prove: how your corporate proxy and TLS inspection behave, Docker Desktop's real Kubernetes (the end-to-end test uses a fake API server on Linux), and how Windows PowerShell 5.1 behaves at a live prompt (CI has no console there, so the grey suggestions, the Tab menu and the Up and Down search are tested with PowerShell 7 and with stand-ins for PSReadLine 2.0). The same goes for bash: it is tested on Linux, in Git Bash (also in a real mintty window with Git's real winpty, but one that no person types into) and on bash 4.2 to 5.3, always against a fake docker. A WSL distribution, a Mac and a live Git Bash window with Docker Desktop behind it follow the notes in the bash section. Run Import-DockerDesktopKube (or devtools-import-kube) on the laptop for the Kubernetes last mile.


Troubleshooting

Build fails with x509 / certificate errors (corporate proxy or TLS inspection). Two things, usually both:

  1. Drop your corporate root CA into certs/ as a *.crt (PEM). The Dockerfile trusts it before any download. See certs/README.md.
  2. Set the proxy in .env (HTTP_PROXY / HTTPS_PROXY) and in Docker Desktop under Settings → Resources → Proxies. Then docker compose build.

Build fails with "429 Too Many Requests", or Docker Hub is blocked. The image starts from debian:bookworm-slim on Docker Hub. Put a mirror of the same image in .env and rebuild (.\setup.ps1):

BASE_IMAGE=registry.corp.example/library/debian:bookworm-slim

It has to be Debian 12; the install steps use apt.

Import-Module fails with "running scripts is disabled on this system" (or "not digitally signed"). PowerShell's execution policy is blocking the module. For your user only, no admin needed:

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Unblock-File 'C:\path\to\devtool\Devtools.psm1'     # needed if the folder came from a downloaded ZIP

If a Group Policy enforces the policy, PowerShell will refuse the change. The module can't load then; use docker compose run --rm dev instead, which gives you the same toolbox in a shell.

Garbled characters (â€", Γöé, broken box lines) in Terraform or gh output. Windows PowerShell decodes native output with the legacy console code page, but the container prints UTF-8. Add this line to your $PROFILE:

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

Quotes in an argument (JSON for kubectl patch -p, az --tags, terraform -var). Type them naturally, e.g. kubectl patch deploy web -p '{"spec":{"replicas":3}}'. Windows PowerShell 5.1 normally strips the double quotes from native command arguments; the wrappers escape them for you, so don't add backslashes. CI checks this in both Windows PowerShell 5.1 and PowerShell 7.

Tab does nothing, or is slow, in PowerShell. Each Tab asks a short-lived container, so it takes about a second, and the first one after Docker Desktop starts can take longer. Nothing at all means the answer was empty or late: check that Docker Desktop is running and that the image is current (.\setup.ps1 rebuilds it; Get-DevToolsInfo shows which image the wrappers use). Names that need a login or a cluster (pods, resource groups) only complete when the tool can reach them.

No grey suggestions in Windows PowerShell 5.1. It ships PSReadLine 2.0, which cannot draw them. The Up and Down arrows search the history instead; to get the grey text, update PSReadLine as shown under History suggestions. The shell inside the toolbox (dev) has the grey suggestions either way.

A warning about your shortcuts file when PowerShell starts. The module could not read ~/.devtools-aliases.ps1 (or the file named by DEVTOOLS_ALIASES) and went on without the rest of it. The warning names the file and the error; fix that line, or move the file away.

az login opens nothing / hangs. Use az login --use-device-code. The container has no browser, so the normal interactive login can't work.

In PowerShell, devtools-alias is "not recognized" and type az says "Cannot find path". Those are bash commands. In PowerShell write Get-DevToolsAlias and Get-Command az (there type is short for Get-Content, which looks for a file called az). Every pair is in PowerShell or bash. If Get-DevToolsAlias is not recognized either, your profile loads an older copy of the module, from before the shortcuts were added. Run setup.ps1 again from the newest project folder and open a new PowerShell window.

In Git Bash, Get-DevToolsAlias or Get-Command says "command not found". The other way round: those are PowerShell commands. Use devtools-alias and type az.

A new Git Bash (or macOS Terminal) window has no tools, but . ~/.bashrc fixes it. That window starts a login shell, which reads ~/.bash_profile and not ~/.bashrc. setup.sh adds a line to your login file that loads ~/.bashrc; if you have since replaced that file, put this in it: [ -r ~/.bashrc ] && . ~/.bashrc.

./setup.sh says "Permission denied". Run it as bash setup.sh.

cd C:\Users\you\devtool says "No such file or directory" in Git Bash. Bash reads a backslash as an escape and drops it. Write cd /c/Users/you/devtool, or put the Windows form in quotes with forward slashes: cd "C:/Users/you/devtool".

In Git Bash, az login (or dev) says "the input device is not a TTY". The plain Git Bash window (mintty) cannot hand its terminal to docker.exe. devtools.sh goes through winpty there by itself; if that was switched off (DEVTOOLS_WINPTY=0) or winpty is missing, use the Windows Terminal or the VS Code terminal, or set DEVTOOLS_WINPTY=1 and install winpty.

In Git Bash, a tool says the working directory 'C:/.../Git/work' is invalid, it needs to be an absolute path. winpty (see the bash section) rewrote /work into a Windows path before docker saw it. Copies of devtools.sh from before this was fixed handed winpty docker's arguments as they were. Update the whole project folder (extract the newest zip over it, so that scripts/devtools-pty-run.sh is there too), open a new Git Bash window and try again. devtools-info prints a Terminal line: winpty (...) in the plain Git Bash window and direct anywhere else. If it still fails, open an issue with the output of devtools-info and of the failing command, after taking your user name and any cluster names out of it.

In WSL, docker was not found on PATH. Docker Desktop > Settings > Resources > WSL integration: switch on your distribution. A docker.exe from Windows is not used on purpose.

devtools.sh needs bash 4.2 or newer. macOS ships bash 3.2. Install a newer one (brew install bash) and run setup.sh with it, or use dev for zsh inside the toolbox.

Tab does nothing, or is slow, in bash. Same as in PowerShell: each press asks a short-lived container (about a second, longer for az). Nothing at all means the answer was empty or late, or that the image is older than this feature (./setup.sh rebuilds it). Another completion for the same command that you registered after the toolbox loaded replaces ours; load the toolbox last.

$'\r': command not found, or a ^M in a "bad interpreter" message. A script, or your shortcuts file, was saved with Windows line endings (CR LF). Convert it with sed -i 's/\r$//' FILE, or in VS Code click CRLF in the status bar and choose LF. The files in this repository are kept at LF, also when git on Windows checks them out.

A shortcut such as kgp says "command not found" in a script. Aliases expand only in an interactive shell. In a script write the command out, or put shopt -s expand_aliases before it.

Output looks garbled, or piping/redirection breaks a command. The wrappers only allocate a TTY (-t) for genuine interactive use and drop it when output is piped or captured, which is correct. If you hit an edge case, run the command inside dev instead.

A version pin 404s during build (e.g. a kubectl patch that doesn't exist). Pick a valid version and set it in .env, then rebuild.

docker was not found on PATH from a wrapper. Docker Desktop isn't running, or docker doesn't resolve in a plain shell. Start Docker Desktop and retry.

Bind mount path issues on Windows. docker compose handles Windows paths in REPOS_ROOT. For the wrappers, the current directory is auto-detected; if a drive-letter path ever misbehaves, cd into the folder and run from there (the wrappers mount $PWD).

Layout

devtool/
├─ Dockerfile            # the image; EXTRA TOOLS block at the bottom
├─ docker-compose.yml    # build + persistent volume + dev service
├─ .env.example          # versions, proxy, repos path (copy to .env)
├─ setup.ps1             # one command: check Docker, build, wire the profile, verify every tool
├─ uninstall.ps1         # the undo: stop the containers, remove the wrappers (image and logins only on request)
├─ Devtools.psm1         # PowerShell wrappers (az/kubectl/terraform/... + dev + Import-DockerDesktopKube),
│                        # shortcuts, Tab completion, history suggestions
├─ setup.sh              # the same as setup.ps1, for bash (Git Bash, WSL, Linux, macOS)
├─ uninstall.sh          # the same as uninstall.ps1, for bash
├─ devtools.sh           # bash wrappers (the twin of Devtools.psm1): functions, shortcuts, Tab completion,
│                        # history search; sourced from ~/.bashrc
├─ scripts/
│  ├─ devtools-entrypoint.sh         # keeps Docker Desktop host traffic off the proxy
│  ├─ import-docker-desktop-kube.sh  # copies the docker-desktop kube context into the toolbox
│  ├─ devtools-complete.sh           # answers the Tab presses of PowerShell and host bash with the real tool's completions
│  └─ shell/                         # the toolbox shell: zshrc, bashrc, aliases.sh, az-completion.sh
├─ tests/                # smoke test, importer end-to-end test, terminal (pty) tests, PowerShell and bash tests
├─ .github/workflows/    # CI: lint, build the image, run the tests above
├─ certs/                # drop corporate root CA here (optional)
└─ repos/                # default mount point if REPOS_ROOT is unset

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages