Skip to content

Name the Azure roles a deploy needs and explain AuthorizationFailed - #52

Open
autoidm-jon wants to merge 2 commits into
mainfrom
azure-missing-role-guidance
Open

autoidm-jon wants to merge 2 commits into
mainfrom
azure-missing-role-guidance

Conversation

@autoidm-jon

Copy link
Copy Markdown
Contributor

What

A user whose account has no RBAC role on the subscription (common: a tenant Global Administrator on a subscription someone else created) gets (AuthorizationFailed) ... does not have authorization to perform action ... from every az call, with no guidance. A user who has Contributor gets the same error later, from az role assignment create, and is equally stuck.

Owner is not required. Deploy makes three role assignments (Key Vault Secrets Officer for the deployer, Key Vault Secrets User and AcrPull for the job identity), and Contributor explicitly excludes Microsoft.Authorization/*/Write. The built-in role Role Based Access Control Administrator adds exactly that right, so the least a deploy needs is Contributor plus Role Based Access Control Administrator. Owner works too, as one role instead of two.

  • README, Azure section: two new subsections. The Azure roles you need names the pair, says what each one is for, gives Owner as the one-role alternative, and shows pdt az commands to list the roles you hold. If Azure says AuthorizationFailed explains how to read the denied action in the error (roleAssignments/write means "add the grant role", anything else means "add both"), then walks the two grant paths: ask someone who can grant roles (with the PIM Assignment-type Eligible-vs-Active trap, marked as only appearing on subscriptions that use PIM), or the Global Administrator elevate-access toggle in Entra ID → Properties, plus the fresh sign-in (pdt login <app>) afterward.
  • deploy_azure.py: run_quiet detects the error on its failure path and prints the same steps in the terminal, per the guiding principle that a missing permission prints the exact role the user must add. denied_action reads the action out of the error so an account that already has Contributor is told to add only the grant role. The RBAC-propagation retry behavior is unchanged; the transient check reuses the new predicate instead of duplicating the strings.
  • tests: test_azure_auth_help.py fakes the subprocess result and covers the no-role message, the cannot-grant message, the action parser, that Owner is never stated as required, and that unrelated failures stay generic. No network, no cloud account.

The elevate-access path was verified by walking a real tenant (Global Admin, zero RBAC) through the portal. Role definitions are from the Azure built-in roles reference (Privileged category).

🤖 Generated with Claude Code


Note

Low Risk
Changes are limited to README guidance and clearer failure output in run_quiet; deploy’s role-assignment behavior is unchanged.

Overview
Documents the Contributor and Role Based Access Control Administrator roles Azure deploy needs (with Owner as an optional one-role alternative), plus pdt az commands to inspect your assignments. Adds an AuthorizationFailed troubleshooting section covering denied-action interpretation, IAM grant steps (including PIM Active/Permanent), and the Entra Global Administrator “Access management for Azure resources” elevation path.

When run_quiet hits an Azure authorization failure, deploy now parses the denied action and prints matching remediation (only the grant role if roleAssignments/write failed; both roles otherwise), reusing the same detection for RBAC-propagation retries. New unit tests cover the parser and both message paths without calling Azure.

Reviewed by Cursor Bugbot for commit 2486c640d63a1e9ac3b832708fac055bf398957e. Bugbot is set up for automated code reviews on this repo. Configure here.


Moved from GitLab autoidm/pdt!28 (https://gitlab.com/autoidm/pdt/-/merge_requests/28), opened by visch on 2026-09-01.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

PR tier: tier::review (rules and Claude agree)

  • README.md mentions secret
  • src/pdt/deploy_azure.py is a provider module
  • src/pdt/deploy_azure.py mentions secret
  • tests/test_azure_auth_help.py mentions credential
  • tests/test_azure_auth_help.py mentions subprocess
  • src/pdt/deploy_azure.py adds new control flow in run_quiet: an AuthorizationFailed stderr now routes to explain_missing_role and a different fail message, so it is behaviour change inside one provider module, not a constant edit
  • No architectural trigger: no config key, no new dependency (re already imported at deploy_azure.py:62), nothing in src/pdt/utils, and deploy.py dispatch is untouched
  • Parity holds: AWS already prints the exact missing policy via POLICY_SCOPES in src/pdt/deploy_aws.py:112, so Azure is catching up to the AGENTS.md rule rather than gaining a step the others lack

A changed diff is scored again. Job log: https://github.com/AutoIDM/pdt/actions/runs/37006898248

A Global Administrator with no RBAC role on the subscription gets
(AuthorizationFailed) from every az call, and nothing said what to do.
Now the README's Azure section names the required role (Owner, assigned
Active and Permanent), gives the pdt az commands that list the roles you
hold, and walks through the two grant paths, including the Entra
elevate-access toggle and the PIM Eligible-vs-Active trap. run_quiet
detects the error and prints the same steps in the terminal.

Claude-Session: https://claude.ai/code/session_01PAWicHmNWiso8hYuYK8AfA
@autoidm-jon
autoidm-jon force-pushed the azure-missing-role-guidance branch from 49aec96 to 8977724 Compare November 3, 2026 19:33
Contributor cannot create role assignments, and deploy makes three of them
(Key Vault Secrets Officer for the deployer, Key Vault Secrets User and
AcrPull for the job identity), but Owner is not the only role that can.
Role Based Access Control Administrator adds exactly that right. The README
and the terminal guidance now name Contributor plus Role Based Access
Control Administrator, with Owner as the one-role alternative, and the
terminal message reads the denied action out of the error so an account
that already has Contributor is told to add only the grant role. The PIM
Assignment type step is described as conditional, since it only appears on
subscriptions that use Privileged Identity Management.
@autoidm-jon
autoidm-jon force-pushed the azure-missing-role-guidance branch from 8977724 to 2d21d31 Compare November 3, 2026 19:38
@autoidm-jon

autoidm-jon commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Goal: when a deploy fails with AuthorizationFailed, pdt names the Azure role that is missing and explains how to get it, and the docs list the minimum roles.

Reviewer: Cursor gpt-5.6-luna-high.

# Severity Disposition Finding Action
1 should-fix fixed src/pdt/deploy_azure.py, docs/platforms.md: the denied-action table omitted Microsoft.Authorization/roleAssignments/delete. Deploy deletes a role assignment through run_quiet (deploy_azure.py:688, :701), so a Contributor user was told to add all three roles. Mapped the delete action to Role Based Access Control Administrator, named it in the docs, and added a test that fails without the mapping.

Rebase onto main (115 commits):

  • README.md: kept main's README. The two Azure subsections moved to docs/platforms.md under "Azure", because main moved the platform sections there.
  • src/pdt/deploy_azure.py: kept main's run_quiet (in-process az, wait lines, escaped output) and added the PR's helpers. The final failure goes through a new fail_az, which main() also uses for ARM read errors. A plan read that Azure refuses now prints the same role guidance as run_quiet.
  • Main now puts delete locks on the shared environment and the container registry, and Contributor plus Role Based Access Control Administrator cannot create a lock. The role list is now Contributor, Role Based Access Control Administrator, and Locks Contributor (the roles the verify service principal holds). A denied locks/write or locks/delete names Locks Contributor only.
  • Role names print with console.value, as main's console value test requires.
  • The test fakes deploy_azure.az in place of subprocess.run, and covers the lock and delete cases.

Checks that passed: uv run --group dev ruff check; uv run pytest -q (2539 passed, 9 skipped).

@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

PR tier: tier::review (rules and Claude agree)

  • docs/platforms.md mentions secret
  • src/pdt/deploy_azure.py is a provider module
  • src/pdt/deploy_azure.py mentions secret
  • tests/test_azure_auth_help.py mentions subprocess
  • tests/test_azure_auth_help.py mentions credential
  • src/pdt/deploy_azure.py adds error-explanation helpers (authorization_failed, denied_action, explain_missing_role, fail_az) inside one provider module; no config key, dependency, utils, or dispatch change
  • brings Azure to the permission-guidance parity AWS already has via print_permission_help (deploy_aws.py:1324) and Google via its PERMISSION_DENIED role help (deploy_google_cloud.py:603)
  • tests/test_azure_auth_help.py pins each branch of the new message, including that unrelated failures stay generic; the access_error rewrite at deploy_azure.py:253 keeps the same retry matching

A changed diff is scored again. Job log: https://github.com/AutoIDM/pdt/actions/runs/41847620266

This branch was successfully deployed

1 active (outdated) deployment
verify — 49aec967 Deployed Oct 7, 2026 by autoidm-jon via google-cloud #40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants