Services → Azure
Connect Azure
Subscriptions, the activity log, Resource Health, alerts, VMs, App Service, Container Apps, AKS, databases, networking, Key Vault metadata, metrics and budgets — forty-one tools, one Reader service principal, nothing to install. Prodpeek asks Azure every hour whether the principal is still read-only.
Setting this up with your own agent? Give it these instructions.
The whole setup
With the Azure CLI, one command:
az ad sp create-for-rbac --name prodpeek --role Reader \
--scopes /subscriptions/<subscription-id>
It prints a small JSON object with appId, password and tenant. That JSON, pasted whole, is the credential. Add --scopes more than once to cover several subscriptions.
Or in the portal:
- Microsoft Entra ID → App registrations → New registration. Name it
prodpeek; nothing else needs setting. - In the new app, Certificates & secrets → New client secret, with an expiry. Copy the secret value (not its id), the Application (client) ID and the Directory (tenant) ID.
- Subscriptions → your subscription → Access control (IAM) → Add role assignment → Reader, and pick the
prodpeekapp as the member. - The credential is
tenant-id:client-id:secret, or the three on separate lines.
Then in Prodpeek: Services → Add a service, pick azure/read-only, leave the upstream as https://management.azure.com, paste the credential, Test connection. On Azure Government or Azure China, use https://management.usgovcloudapi.net or https://management.chinacloudapi.cn; Prodpeek picks the matching sign-in authority itself.
Reader. Never Contributor, never Owner.
Contributor is the role tutorials reach for, and it can restart, scale or delete anything in the subscription. The scope probe will catch it — prove reads the principal's own role assignments every hour and demotes a service that can write — but by then it has been sitting in your gateway with write.
For a five-minute test you can paste a raw token from az account get-access-token --query accessToken -o tsv instead. It is your own identity and it expires within the hour; do not leave it there.
Why there is no MCP server to install
There is no hosted Azure MCP server a server-side gateway can hold a stored team credential for. Azure Resource Manager takes a bearer token issued to a service principal — the shape Prodpeek is built around — so it speaks ARM directly. The adapter exchanges the principal for a token at Entra ID, caches it until shortly before it expires, and uses it for GET requests to ARM. That exchange is the only request it makes that is not a GET, and it goes only to Entra.
What your agent can then do
azure__list_activity_log what changed just before this broke, and who did it
azure__list_resource_health whether Azure says the fault is on its side
azure__list_alerts which Azure Monitor alerts fired, and when
azure__get_web_app is the app running, and when did it last change
azure__list_web_app_deployments what was deployed to it, when, by whom
azure__list_container_app_revisions did the new revision come up, and how is traffic split
azure__get_aks_cluster cluster version, power state, node pools
azure__get_metrics CPU, requests, errors and latency over a window
azure__list_deployments which ARM deployment failed, and its error
azure__list_network_security_groups why the app cannot reach the database
azure__list_dns_records why it still resolves to the old address
azure__list_subscriptions where every subscription id comes from
list_activity_log is the one nobody thinks to ask for and the one that most often ends the discussion: a scale-down, a deleted rule or a redeploy, with the caller's name and a timestamp. list_resource_health is second — it is Azure saying, per resource, whether the problem is theirs.
What it refuses, and the ones that matter
Every credential dispenser is a POST, and none has a tool. Storage account keys (listKeys), an AKS kubeconfig (listClusterUserCredential), a web app's app settings and connection strings (config/appsettings/list), deployment credentials (publishxml). There is no code path in the adapter that builds those paths, and a Reader principal cannot call them either.
Key Vault secret values are out of reach. They live on *.vault.azure.net, a host the adapter never talks to. list_key_vaults returns vault metadata, network rules and access policies.
Inline secrets are removed. Some ARM answers carry a secret under a predictable name — a VM's customData (cloud-init, often full of tokens), an adminPassword, a connectionString. The adapter replaces the value under every such key, at any depth, and leaves names and diagnostics alone. ARM deployment parameter and output values go the same way; their names stay.
Restarting a web app has no tool, and it is the one people ask for. "Turn it off and on again" is still a production change, and during an incident it destroys the state that explains the incident.
Why Tier 2
The Tier 1 half is real: Reader refuses every write in this profile *at Azure*, independently of the gateway. Keep it that way.
It is Tier 2 anyway, because Reader also reads every network security group rule, every role assignment and every Key Vault access policy in its scope — a complete map of what is exposed and who may reach it — and Azure has no role meaning "may list resources but not their network rules". The roll-up decides the label.
Unlike most vendors, Azure can be asked. This profile carries the azure_rbac scope probe: [prove](../use/prove.md) reads the principal's own role assignments on every subscription it can see, and each role's permissions, every hour. A write permission on anything this adapter reads — or * — demotes the service and names the role. Write permissions elsewhere (Cost Management Reader can open support tickets) are reported, not counted, and any data-plane permission is reported.
Checking the credential really cannot write
APP_ID=<the appId az printed>
# Should list Reader, and only Reader
az role assignment list --assignee "$APP_ID" --all -o table
# Should list no data-plane role either
az role assignment list --assignee "$APP_ID" --all \
--query "[].roleDefinitionName" -o tsv | sort -u
Only Reader in both is the vendor fence doing its half. In Prodpeek, Prove shows scope: read_only for the service once the hourly run has happened — or run it by hand from the console. Anything else there names the role that is too much; remove that assignment.