[Profile] Add a native profile concept (az -p <profile> / AZURE_PROFILE) for working across multiple tenants
- Dominant language
- Python
- Stars
- 4.6k
- Forks
- 3.5k
- Avg merge
- 3d 2h
- Merged PRs (30d)
- 60
Description
**Related command**
`az` (global argument) / `az account` / `azure.cli.core._profile`
**Is your feature request related to a problem? Please describe.**
I work across a number of separate Azure tenants, each with a distinct sign-in identity (different UPNs, different MFA, some guest invitations). The CLI stores all of its authentication state — MSAL token cache, `azureProfile.json`, the active subscription, config and installed extensions — in a single directory, `~/.azure`.
That means the CLI only has one "current" identity/subscription at a time, and it is *global mutable state*:
- `az login` for a second tenant does not isolate anything; it merges into the same profile and token cache.
- `az account set --subscription ...` changes behaviour for every shell, script and tool on the machine that shells out to `az`. Two terminals working on two clients race with each other.
- Passing `--subscription` per command mitigates the race but does not address identity isolation, and it is easy to forget on a destructive command.
For anyone consulting across multiple customers — or just separating prod from dev tenants — there is no supported way to say "run this command as *that* identity" without mutating shared state.
**Describe the solution you'd like**
A first-class named-profile concept, selectable per invocation:
```bash
az -p contoso group list # or --profile
AZURE_PROFILE=contoso az group list
```
Where a profile is simply an isolated set of the state the CLI already keeps per config dir (token cache, subscription list, active subscription, config), stored under something like `~/.azure/profiles//`.
Suggested shape:
- Global argument `-p/--profile `, plus an `AZURE_PROFILE` environment variable.
- Resolution precedence: `--profile` > `AZURE_PROFILE` > `AZURE_CONFIG_DIR` > default.
- Management commands: `az profile list`, `az profile show`, `az profile create/delete/rename`, so profiles are discoverable rather than filesystem trivia.
- `az login --profile ` to bootstrap one without pre-creating directories.
**Naming caveat:** "profile" is already overloaded in the CLI — `az cloud set --profile 2020-09-01-hybrid` means an *API version profile*, and `azureProfile.json` means the subscription cache. If that collision is a blocker, `--context` would avoid it and would align with Azure PowerShell's `Select-AzContext` / `-AzContext` and with `kubectl` contexts.
**Describe alternatives you've considered**
*`AZURE_CONFIG_DIR` + shell aliases* — what I do today, and it works:
```zsh
alias az-clienta='AZURE_CONFIG_DIR=~/.azure-clienta az'
alias az-clientb='AZURE_CONFIG_DIR=~/.azure-clientb az'
```
Each directory holds its own MSAL cache, so switching tenants is a different command rather than a re-login, and nothing leaks between shells. But as a substitute for a native feature it has real costs:
- Extensions, `commandIndex.json`, help indexes and `config` are duplicated per directory. Installing or upgrading an extension has to be repeated N times, and it is easy to end up with drifted extension versions between tenants.
- Profiles are not discoverable. There is no `az profile list`; the only inventory is `ls ~/.azure-*` plus whatever the aliases happen to say.
- Aliases are shell-specific and invisible to scripts, Makefiles, CI, and any tool that shells out to `az` — those all have to know to set `AZURE_CONFIG_DIR` themselves.
- Nothing surfaces which profile is active, so there is no reliable way to show it in a shell prompt or guard a destructive command.
- The default `~/.azure` silently accumulates identities from any `az login` that predates or bypasses the convention, at which point the isolation is only nominal and you have to audit it by hand (`az account list --query "[].user.name" -o tsv | sort -u`).
*Multiple logins in one config dir + `az account set`* — supported today, but shares one token cache and one global current subscription; that is the problem being described.
*Service principals per tenant* — does not help where interactive/MFA sign-in is required, and still lands in one shared cache.
*A container or devcontainer per client* — effective isolation but heavyweight for what is a config-directory concern.
**Additional context**
Prior art for exactly this pattern in comparable CLIs:
| Tool | Mechanism |
|---|---|
| AWS CLI | `--profile` / `AWS_PROFILE` |
| gcloud | named configurations: `gcloud config configurations activate`, `--configuration` |
| kubectl | contexts: `--context`, `kubectl config use-context` |
| Azure PowerShell | contexts: `Select-AzContext`, per-cmdlet `-AzContext` |
| azd | environments |
Notably Azure PowerShell already has the concept, so this would bring the CLI in line with the rest of the Azure toolchain rather than introducing a new idea.
Since `AZURE_CONFIG_DIR` already provides the underlying isolation, this could plausibly be implemented largely as resolution and ergonomics on top of existing behaviour in `azure/cli/core/_environment.py` and `_profile.py`, rather than as a new storage model.
Contributor guide
Assessment
This issue has not been assessed yet.