azctx is a command-line tool designed to streamline the management of Azure tenant and subscription contexts. It provides an intuitive fuzzy-finder interface for switching between Azure subscriptions and tenants, making it easier to work with multiple Azure environments.
Note
Unlike plain az account set, azctx makes per-shell isolation the default mode of operation: switching context never mutates ~/.azure. See Origins.
- 🐚 Per-shell isolated contexts — each
azctxinvocation copies~/.azureto a private tempdir, setsAZURE_CONFIG_DIR, and drops you into a subshell; the master~/.azureis never touched - 🔍 Real fzf picker, embedded — inline (no full-screen takeover), themed by your FZF_DEFAULT_OPTS and any fzf options via config
- ⚡ Quick context switching between subscriptions
- 🏷️ Short aliases for subscriptions, in the picker and
--subscription - 🔄 Easy switching to previous context (similar to
cd -) - 🎯 Tenant-first selection mode
- 🔧 Configurable logging levels
- azure-cli >= 2.22.1 (azctx manages its profile; the fuzzy finder itself is built in)
- go >= 1.26 (building from source only)
Linux and macOS only — the per-shell isolation relies on Unix process
semantics ($SHELL, signals), so there are no Windows builds.
Brew (macOS/Linux)
brew install iul1an/azctx/azctx# from a checkout: builds a package from the latest release tag and
# installs it with pacman (binary, completions, license)
make arch-installDownload the latest release from the releases page and add it to your PATH.
go install github.com/iul1an/azctx@latest
# or from a checkout (default /usr/local/bin; override e.g. PREFIX=$HOME for ~/bin):
make install PREFIX=$HOMECompletions for bash, zsh, fish, and powershell are built in:
# zsh: write into any directory on your $fpath
azctx completion zsh > ~/.zsh/completions/_azctx
# bash
azctx completion bash > /etc/bash_completion.d/azctxFlag values complete too: --subscription <Tab> offers the live
subscription names from your active profile, and --log-level <Tab> its
four levels. The generated script delegates to the binary at runtime, so
new subcommands and flags are picked up without regenerating the file.
# Pick a subscription and drop into a subshell scoped to it.
# ~/.azure is copied to a tempdir and AZURE_CONFIG_DIR points at the copy,
# so the pick never mutates your master config.
azctx
# Inside the isolated shell:
echo $AZURE_CONFIG_DIR # /tmp/azctx.XXXXXXX
az account show # shows the picked subscription
# An isolated shell is bound to its subscription for its whole lifetime:
# re-running azctx inside one is refused (it couldn't update the shell's
# AZCTX_SUBSCRIPTION). Exit the shell and re-run azctx, or use azctx exec.
# Exit the subshell; the tempdir is cleaned up automatically.
exit
# Switch to previous subscription context
azctx -
# Clear the default subscription in the master ~/.azure (no picker,
# no subshell). Refused inside an isolated shell.
azctx --unset
# Start from a completely empty Azure config: nothing copied from
# ~/.azure, no picker. az behaves as never-logged-in inside; an
# `az login` there vanishes with the shell. Also works with exec.
azctx --fresh
azctx exec --fresh -- az login --use-device-code
# List subscriptions ('*' = default) and isolated contexts (config
# dir, subscription, owning PID, age; '*' = current shell's context).
# --json emits both as indented JSON.
azctx list
# Show the current context as indented JSON — subscription, tenant,
# PID, env consistency. The `isolated` field tells you whether you are
# inside an isolated shell.
azctx statusEvery azctx invocation garbage-collects orphaned contexts: each tempdir records its owning process, and dirs whose owner is gone (e.g. after a SIGKILL) are removed automatically on the next run.
# Select tenant before choosing subscription
azctx --by-tenantThe profile carries no tenant names, so the picker labels tenants with the signed-in account. Name them yourself in the config, keyed by tenant ID:
tenants:
33333333-3333-3333-3333-333333333333: "Contoso"# Pick a subscription, run a single command in the isolated context,
# then clean up — similar to aws-vault exec. The command's exit code
# is propagated.
azctx exec -- kubectl get pods
azctx exec --by-tenant -- kubie ctx my-aks-cluster
# Skip the picker entirely with --subscription (alias, name, or ID;
# names and aliases are case-insensitive). Also works on bare azctx.
azctx exec --subscription "My Subscription" -- kubectl get pods
# 'prod' below is an alias, see Subscription Aliases
azctx exec --subscription prod -- az aks list --query '[].name' -o tsv
# With no command, exec drops into an isolated subshell instead of
# running something and exiting — so `exec --subscription` is a
# non-interactive way into a shell, skipping the picker.
azctx exec --subscription "My Subscription"Like bare azctx, exec is refused inside an isolated shell: exit it
first and re-run. Nesting contexts is confusing and buys nothing.
Optional short names for subscriptions, defined in the config file as an alias to a subscription ID or name:
aliases:
prod: 11111111-2222-3333-4444-555555555555
dev: "My Dev Subscription"Use them anywhere --subscription is accepted; <Tab> completes them.
azctx --subscription prod
azctx exec --subscription dev -- az aks list --query '[].name' -o tsvAliases are case-insensitive, appear in the picker as My Prod Subscription [prod] (1111-…), and show up in azctx list and azctx status. An alias takes precedence over a subscription of the same name,
and one pointing at nothing is an error rather than a fallback.
# Mutate the master ~/.azure directly: no tempdir
# copy, no subshell. Refused inside an isolated shell like bare azctx.
azctx --in-placeNotes on isolation:
- The subshell is
$SHELL(fallback/bin/zsh). - The spawned shell/command gets
AZCTX_SUBSCRIPTIONset to the picked subscription's name (like aws-vault'sAWS_VAULT), handy for prompts and wrapper scripts. It is always accurate because a shell's subscription is immutable — re-picking inside an isolated shell is refused, with no override. - Each isolated context gets its own copy of the token cache. If tokens
expire,
az logininside the subshell only affects that context. kubelogin/kubectlhonorAZURE_CONFIG_DIR, so AKS access works inside the isolated shell.- az telemetry is off inside contexts (
AZURE_CORE_COLLECT_TELEMETRY=0): its uploader outlives the command and would recreate the context dir after cleanup.
Configuration is stored in ~/.azctx.yml, or wherever AZCTX_CONFIG_FILE
points (any extension, parsed as YAML; naming a file that does not exist is
an error). Every flag can be set there (precedence: flag > AZCTX_*
environment variable > config file):
# Log level: debug, info, warn, error
log-level: info
# Always pick the tenant before the subscription
by-tenant: false
# Always select this subscription (alias, name, or ID) — disables the picker
#subscription: "My Subscription"
# Names for tenants in the --by-tenant picker, keyed by tenant ID
#tenants:
# 33333333-3333-3333-3333-333333333333: "Contoso"
# Short aliases for subscriptions (value is a subscription ID or name)
#aliases:
# prod: 11111111-2222-3333-4444-555555555555
# dev: "My Dev Subscription"
# Always start from an empty config (ephemeral-by-default workflow)
#fresh: false
# Suppress the "switched context to" confirmation (also -q on the CLI)
#quiet: false
# Careful with these two as persistent settings:
# in-place: true makes bare azctx mutate ~/.azure directly;
# unset: true makes every bare azctx run clear the default and exit.
#in-place: false
#unset: false
# Picker look and feel: the picker is real fzf embedded as a library, so
# it accepts any fzf options (defaults: --height=~40% --layout=reverse).
# FZF_DEFAULT_OPTS is honored too, so an existing fzf theme just works.
#picker:
# options: ["--height=~60%", "--border=rounded", "--prompt=azctx> "]
# preview: false # true shows details for the highlighted entry:
# # subscription fields, or a tenant's subscriptionsYou can also set configuration via environment variables:
AZCTX_LOG_LEVEL: Set logging levelAZCTX_BY_TENANT: Enable tenant-first selection modeAZCTX_CONFIG_FILE: Use this config file instead of~/.azctx.ymlAZCTX_SUBSCRIPTION: Same as--subscription, aliases included. Note the dual role: azctx also exports this into isolated shells, which is what makes nestedazctx execinherit the shell's subscription.
azctx began as a fork of riweston/aztx
by Richard Weston (MIT) and retains its full git history. The fuzzy-finder
picker and the azureProfile.json handling descend from that project; the
per-shell isolation model, exec/list/status, --fresh, the
subscription-binding semantics, and orphaned-context GC are original to
azctx. Unlike the original project, azctx does not support Windows: the
isolation model is built on Unix process semantics ($SHELL, signals),
so only Linux and macOS binaries are published. azctx is not affiliated
with or endorsed by the original author.
This is an opinionated tool; issues and PRs are welcome, but features that
reintroduce mutation of ~/.azure as a default won't be accepted.
This project is licensed under the MIT License - see the LICENSE file for details, which carries both the original author's copyright and this project's.