Skip to content

fix(azure-arm): grant SP the actual purchase actions via custom role (closes #731) - #732

Merged
cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/731-arm-roles
May 26, 2026
Merged

cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/731-arm-roles

Conversation

@cristim

@cristim cristim commented May 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Fixes #731 (P0). The Azure ARM template grants the CUDly SP the built-in Reservation Purchaser role (f7b75c60-3036-4b75-91c3-6b41c27c1689), which is misnamed: its actions cover only catalog/recommendations reads + register/action. None of calculatePrice/action, reservationOrders/write, reservationOrders/read, reservationOrders/purchase/action (the action that 403'd in the user-reported failure on Standard_D2d_v4) or any of the savings-plan billing-benefits actions are present.

Adds a Microsoft.Authorization/roleDefinitions custom role that enumerates every concrete action the executors call at runtime, and a role assignment for that custom role at subscription scope. Existing built-in assignments (Reader, Cost Management Reader, Reservation Purchaser, tenant-scope Capacity) are preserved so we still get recommendations reads and so existing tenants degrade gracefully on partial redeploy.

Actions enumerated (traced from SDK call sites)

Microsoft.Capacity:

  • calculateprice/action — internal/reservations/purchase.go step 1
  • reservationorders/write — internal/reservations/purchase.go step 2 (POST .../reservationOrders/{id}/purchase)
  • reservationorders/read, reservationorders/reservations/read — providers/azure/services/compute/exchange.go
  • register/action — ensureCapacityProviderRegistered
  • catalogs/read — recommendation discovery

Microsoft.BillingBenefits (savings plans):

Tenant upgrade requirement

Existing CUDly users MUST redeploy arm/CUDly-CrossSubscription/template.json to pick up the new custom role. known-issues.md updated with an Outstanding entry pointing at #731.

Test plan

  • az deployment sub validate returns error: null against a sandbox subscription
  • Manual: redeploy ARM into a target subscription, retry a previously-failing Standard_D2d_v4 purchase, confirm no 403
  • Re-run the user's exact failed purchase scenario end-to-end
  • Verify the existing built-in Reservation Purchaser assignment still resolves recommendations reads after the custom role is added

Summary by CodeRabbit

  • New Features

    • Added a custom Azure role enabling reservation and savings-plan purchase operations; clarified the existing built-in role remains for catalog reads and recommendations.
  • Documentation

    • Updated known issues with redeploy guidance to resolve Azure purchase API 403s and added a GCP permission failure example.

Review Change Stack

The built-in Reservation Purchaser role (f7b75c60) is missing
Microsoft.Capacity/calculateprice/action, reservationorders/write,
and the Microsoft.BillingBenefits actions needed for savings plans,
which caused 403 on live purchases (issue #731).

Add a custom role definition "CUDly Reservation and Savings Plan
Purchaser" with the exact actions observed in the SDK call sites:

Microsoft.Capacity:
  calculateprice/action, reservationorders/write,
  reservationorders/read, reservationorders/reservations/read,
  register/action, catalogs/read

Microsoft.BillingBenefits:
  savingsPlanOrderAliases/write, savingsPlanOrders/read,
  savingsPlanOrders/savingsPlans/read, savingsPlanOrders/action

The built-in Reservation Purchaser and Reader assignments are kept
unchanged. The custom role uses a deterministic GUID derived from the
subscription ID so re-deployments are idempotent.

Validated: az deployment sub validate exits cleanly.
Document re-deployment requirement in known-issues.md.
@cristim cristim added triaged Item has been triaged priority/p0 Drop everything; same-day fix severity/critical Major harm when it happens urgency/now Drop other things impact/all-users Affects every user labels May 26, 2026
@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8fd49bfa-a5a9-487f-acf7-65b495729de5

📥 Commits

Reviewing files that changed from the base of the PR and between af0068a and cca72f7.

📒 Files selected for processing (2)
  • arm/CUDly-CrossSubscription/template.json
  • known-issues.md
✅ Files skipped from review due to trivial changes (1)
  • known-issues.md

📝 Walkthrough

Walkthrough

The PR extends an ARM subscription deployment template to create and assign a subscription-scoped custom role that grants Microsoft.Capacity and Microsoft.BillingBenefits actions for reservation and savings-plan purchases, exposes the custom role ID as an output, and updates known-issues documentation with redeployment instructions and an added GCP log excerpt.

Changes

ARM Custom Role and Savings Plan Purchase Support

Layer / File(s) Summary
Custom Role Definition and Assignment
arm/CUDly-CrossSubscription/template.json
Introduces customRoleName and customRoleDefinitionId variables, adds a Microsoft.Authorization/roleDefinitions resource defining "CUDly Reservation and Savings Plan Purchaser", and creates a Microsoft.Authorization/roleAssignments entry assigning the custom role to the service principal at subscription scope. Minor spacing adjustments and clarification that the built-in reservationPurchaser assignment is retained for catalog reads and recommendations.
Custom Role Output Contract
arm/CUDly-CrossSubscription/template.json
Adds customRoleDefinitionId output returning the resource ID of the deployed custom role definition.
Known Issues and Remediation Guidance
known-issues.md
Replaces the intro with an "Outstanding" section describing Azure purchase-API 403s caused by missing ARM actions, documents the updated ARM template fix and az deployment sub create redeploy instructions, and adds a GCP Lambda log snippet showing compute.regions.list 403 for serene-bazaar-666.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Suggested labels

type/security

Poem

🐰 A custom role hops into place,
With ARM-defined permissions and gentle pace,
Reservations and savings now reachable by name,
Redeploy to grant rights and end the 403 shame,
Hop on, CUDly—let purchases play the game!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: granting a service principal purchase actions via a custom RBAC role.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/731-arm-roles

Comment @coderabbitai help to get the list of available commands and usage tips.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
known-issues.md (1)

22-27: ⚡ Quick win

Consider adding --no-prompt flag for consistency and automation-friendliness.

The deployment command is functionally correct but omits the --no-prompt flag used in the actual setup script. Adding it prevents unexpected interactive prompts during re-deployment, which is especially important for scripted or automated execution contexts.

📝 Suggested addition for better operator experience
 az deployment sub create \
   --location eastus \
   --template-file arm/CUDly-CrossSubscription/template.json \
   --parameters servicePrincipalObjectId=<SP-object-id> \
-  --name CUDly-CrossSubscription
+  --name CUDly-CrossSubscription \
+  --no-prompt

As per relevant code snippet from arm/CUDly-CrossSubscription/setup.sh:123, the setup script includes --no-prompt to ensure non-interactive execution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@known-issues.md` around lines 22 - 27, Update the az deployment sub create
invocation shown (the command using --template-file
arm/CUDly-CrossSubscription/template.json and --parameters
servicePrincipalObjectId=<SP-object-id>) to include the --no-prompt flag so the
command becomes non-interactive and matches the setup.sh behavior; this ensures
scripted or automated runs won’t hang on prompts during re-deployment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@arm/CUDly-CrossSubscription/template.json`:
- Around line 61-63: The assignableScopes entry uses
subscriptionResourceId('Microsoft.Resources/subscriptions',
subscription().subscriptionId) which expands to the provider-scoped path;
replace that expression with the canonical subscription scope string such as
subscription().id (or concat('/subscriptions/', subscription().subscriptionId))
so assignableScopes contains "/subscriptions/{subscriptionId}" — update the
assignableScopes array value where the template defines assignableScopes.

---

Nitpick comments:
In `@known-issues.md`:
- Around line 22-27: Update the az deployment sub create invocation shown (the
command using --template-file arm/CUDly-CrossSubscription/template.json and
--parameters servicePrincipalObjectId=<SP-object-id>) to include the --no-prompt
flag so the command becomes non-interactive and matches the setup.sh behavior;
this ensures scripted or automated runs won’t hang on prompts during
re-deployment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93b51fd5-385d-4c92-a7d9-56ace5b0c7f7

📥 Commits

Reviewing files that changed from the base of the PR and between 1200e08 and af0068a.

📒 Files selected for processing (2)
  • arm/CUDly-CrossSubscription/template.json
  • known-issues.md

Comment thread arm/CUDly-CrossSubscription/template.json
…rompt to known-issues example

- assignableScopes was using subscriptionResourceId('Microsoft.Resources/subscriptions',
  subscription().subscriptionId) which expands to the provider-scoped path
  /subscriptions/{id}/providers/Microsoft.Resources/subscriptions/{id} -- not the canonical
  subscription scope /subscriptions/{id} that ARM RBAC expects. Switch to subscription().id
  which returns exactly the right value. ARM validate returns error:null after the fix.
- known-issues.md redeploy command now includes --no-prompt, matching setup.sh line 122
  and preventing interactive hangs in automated/scripted re-deployments.

Addresses CR findings on PR #732 (review 4359641918).
@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

CR fixes (cca72f7)

Two findings from the 2026-05-26 CodeRabbit review addressed:

Actionable - arm/CUDly-CrossSubscription/template.json line 62-63
Replaced subscriptionResourceId('Microsoft.Resources/subscriptions', subscription().subscriptionId) with subscription().id in assignableScopes. The previous expression expands to /subscriptions/{id}/providers/Microsoft.Resources/subscriptions/{id} -- a provider-scoped path -- not the /subscriptions/{id} canonical subscription scope that Azure RBAC assignableScopes requires. After the fix, az deployment sub validate returns error: null.

Nitpick - known-issues.md lines 22-27
Added --no-prompt to the az deployment sub create redeploy command shown in the Outstanding issue entry. The setup script (arm/CUDly-CrossSubscription/setup.sh line 122) already uses this flag; the known-issues example now matches, preventing interactive hangs in automated re-deployments.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

@cristim: Thanks for the clear summary and for addressing both findings from the previous review!

  • assignableScopes fix — Correct call. subscriptionResourceId('Microsoft.Resources/subscriptions', subscription().subscriptionId) expands to the provider-scoped path /subscriptions/{id}/providers/Microsoft.Resources/subscriptions/{id}, which Azure RBAC rejects for assignableScopes. Switching to subscription().id yields the canonical /subscriptions/{id} form as required.
  • --no-prompt in known-issues.md — Good consistency fix; the redeploy example now matches what setup.sh does and won't hang in CI/automation contexts.

Triggering a fresh review of the updated commits.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 26, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

impact/all-users Affects every user priority/p0 Drop everything; same-day fix severity/critical Major harm when it happens triaged Item has been triaged urgency/now Drop other things

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant