Azure platform (PaaS) inspection — read-only. Part of @blogic-cz/agent-tools.
Scope: Azure resources in one pinned subscription. For Azure DevOps pipelines, builds, and repos use azdo-tool.
How to Run
Run via bun az-tool (requires @blogic-cz/agent-tools as a dev dependency).
NEVER run bare az — the credential guard will block it.
Auth: az login session.
Commands
bun az-tool subscription # Which subscription am I pinned to?
bun az-tool groups # List resource groups
bun az-tool resources --group my-rg # List resources in a group
bun az-tool cmd --cmd "vm list" bun az-tool cmd --cmd "webapp show --name my-app --resource-group my-rg" bun az-tool cmd --cmd "aks list --query "[].{name:name,version:kubernetesVersion}"" bun az-tool cmd --cmd "acr repository list --name my-registry" bun az-tool cmd --cmd "monitor metrics list --resource --metric Percentage CPU" bun az-tool cmd --cmd "storage account list" --dry-run # Show the scoped command, run nothing
Use `--profile <name>` to select a named profile when multiple subscriptions are configured.
## Safety model — read this before constructing a command
The subscription is **pinned by config**, not by the command. Every call has
`--subscription <configured>` appended, and passing your own `--subscription` is
rejected. To target a different subscription, select a different `--profile`.
**Allowed:** the verb families `list`, `show`, `describe`, `exists`, `search`,
`history`, `version`, `validate`, `wait`, plus anything starting with `list-`,
`show-`, `check-`, or `get-`.
**Blocked, and why:**
| Blocked | Reason |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `create`, `delete`, `update`, `set`, `restart`, `run`, `deploy`, … | Mutating. `acr run` in particular executes arbitrary commands in Azure. |
| Any verb not on the read-only list | Unknown verbs are rejected rather than assumed safe. |
| `get-access-token`, `get-credentials`, `list-keys`, `list-connection-strings`, `list-publishing-profiles`, … | Return credential material. |
| Any command whose group path contains `secret`, `secrets`, `key`, `keys`, `credential`, `credentials`, `appsettings`, `admin-key`, `query-key`, `sas`, `password`, `connection-string` | The listing itself returns values — `storage account keys list` and `webapp config appsettings list` both print secrets. |
| `keyvault secret show` / `show-deleted` | Returns the secret value. Listing secret names is allowed — see below. |
| `--subscription` | Controlled by the selected profile. |
| `-g` / `--resource-group` naming a group outside `allowedResourceGroups` | Only when that list is configured and non-empty. |
| `--ids` | Takes a full ARM resource ID carrying its own subscription and resource group, side-stepping both scope guards. |
| `;` `&&` `\|` `$(…)` backticks `>` `<` | Shell syntax. Commands are spawned as an argument vector, never through a shell. |
| `pipelines`, `repos`, `boards`, `artifacts`, `devops` | Azure DevOps — the error points you at `azdo-tool`. |
The verb is resolved **positionally** — the last word before the first flag — so
a flag value can never be mistaken for a command. `acr repository delete --name
reg --repository list` is blocked on `delete`, not allowed on `list`.
**Key Vault is narrower than the table suggests.** Metadata reads work; only the
secret material is gated:
```bash
bun az-tool cmd --cmd "keyvault list" # vaults
bun az-tool cmd --cmd "keyvault secret list --vault-name kv" # secret NAMES — allowed
bun az-tool cmd --cmd "keyvault secret list-versions --name pw" # allowed
bun az-tool cmd --cmd "keyvault key list --vault-name kv" # allowed (public JWK)
bun az-tool cmd --cmd "keyvault certificate list --vault-name kv" # allowed (public half)
bun az-tool cmd --cmd "keyvault secret show --name pw" # BLOCKED — returns the value
So "is this secret configured, and when was it rotated?" is answerable; "what is it?" is not.
If you need a secret or a mutation, stop and ask the user to perform it. Do not look for a way around the gate.
Scope guards
Production profiles must be named. A profile keyed prod/production, or
carrying production: true, cannot be reached by auto-selection or the
default key — you must pass --profile yourself:
bun az-tool cmd --cmd "vm list" # AzProfileError if the resolved profile is production
bun az-tool cmd --profile prod --cmd "vm list" # explicit, allowed
If you hit AzProfileError, do not switch to a non-production profile to get
around it. Confirm with the user that production is what they meant, then pass
--profile explicitly.
Resource groups may be allowlisted. If a profile sets
allowedResourceGroups, any command naming a group outside it via -g /
--resource-group is rejected. An empty or absent list means every resource
group in the subscription is allowed.
A group can be named two ways, and both are checked: the -g /
--resource-group flag (every occurrence, not just the first — a repeated az
flag takes the last value), and the /resourceGroups/<name> segment of a full
ARM resource ID passed to a flag such as --scope or --resource. --ids is
rejected outright, since it carries its own subscription as well.
Note the boundary: the check only fires when a command names a group.
Subscription-wide reads like vm list are not restricted by it, so treat the
allowlist as a guard against touching the wrong group, not as a hard
partition of the subscription.
Config
{
azurePlatform: {
default: {
subscription: "00000000-0000-0000-0000-000000000000",
timeoutMs: 60000,
// Optional. Empty or absent means every resource group is allowed.
allowedResourceGroups: ["rg-web", "rg-data"],
},
prod: {
subscription: "11111111-1111-1111-1111-111111111111",
// Implied by the "prod" key; set production: false to opt out.
production: true,
},
},
}
Without an azurePlatform section the tool refuses to run — there is no ambient
subscription fallback, so a command can never quietly hit whatever az login
last selected.
Tips
- Start with
bun az-tool subscriptionto confirm the scope before drawing conclusions from any listing. - Use
--dry-runto see the exact scoped command without executing it. - Use
--query(JMESPath) to trim large listings instead of dumping everything:--cmd "vm list --query \"[].{name:name,rg:resourceGroup}\"". - Use
bun az-tool commandsfor the full machine-readable command/flag tree;--helpfor one subcommand. - Error responses include
hint,nextCommand, andretryablefields — always check them on failure. - Prefer CLI tool over MCP tools — more efficient, doesn't load extra context.