Repository navigation
Closed
Labels
effort/mDaysDaysimpact/fewLimited audienceLimited audiencepriority/p3Polish / idea / may never shipPolish / idea / may never shipseverity/mediumModerate harmModerate harmtriagedItem has been triagedItem has been triagedtype/featNew capabilityNew capabilityurgency/eventuallyNo deadlineNo deadline
Description
Gap
Azure Reserved Instance purchases always bill upfront regardless of the requested payment_option. The purchase body builder (providers/azure/services/compute/client.go, buildReservationBody) is called with opts.Source and opts.IdempotencyToken but never receives opts.PaymentOption, so Azure's reservation API sends no billingPlanType and defaults to upfront.
Impact
- A caller requesting
no-upfrontorpartial-upfrontfor an Azure RI would silently get an upfront commitment (different cash-flow schedule than requested). - Currently guarded, not honored: the CUDly MCP server (PR feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure, GCP #1495) fails loud and rejects non-
all-upfrontAzure RI real purchases rather than silently mis-billing. So there is no active money-safety hole; this issue is to actually support the other billing plans.
Fix
- Thread
opts.PaymentOptionintobuildReservationBodyand map it to Azure'sbillingPlanType(UpfrontvsMonthly) on the reservation purchase request. Azure reservations supportUpfrontandMonthlybilling plans; mapall-upfront->Upfront,no-upfront/partial-upfront-> the closest supported plan (Monthly), or reject combinations Azure genuinely cannot express (fail loud, no silent default). - Use the SDK's typed billing-plan enum, not a raw string literal (project convention).
- Once wired, remove the MCP tool's real-purchase rejection of non-
all-upfrontinmcp/tools/azure_compute_ri.goand let the value flow through.
Scope note
This is a provider-layer change that also benefits the CLI and web (internal/api) purchase paths, not just MCP. Verify against Azure's reservation billing-plan API surface before wiring; do not assume the mapping.
Related
- PR feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure, GCP #1495 (MCP server) added the safe fail-loud guard this issue would lift.
- The retry-safety idempotency work for Azure landed earlier (issue bug(providers/azure): DoPurchaseTwoStep dropped IdempotencyToken threading; reintroduces double-purchase risk (regression of #641; blocks #639) #721).
Metadata
Metadata
Assignees
Labels
effort/mDaysDaysimpact/fewLimited audienceLimited audiencepriority/p3Polish / idea / may never shipPolish / idea / may never shipseverity/mediumModerate harmModerate harmtriagedItem has been triagedItem has been triagedtype/featNew capabilityNew capabilityurgency/eventuallyNo deadlineNo deadline
Activity
cristim commentedon Jul 23, 2026
Scope widened: the same billingPlan omission affects 6 sibling Azure services
The compute wiring is landing in PR #1495 (new helper
reservations.BillingPlanForPaymentOption+billingPlanset in compute'sbuildReservationBody). An adversarial review confirmed the other six Azure reservation-based services build their own request bodies and never setbillingPlan, so ano-upfront/monthlyrecommendation for any of them is still silently billed at Azure's Upfront default:providers/azure/services/databaseproviders/azure/services/cacheproviders/azure/services/cosmosdbproviders/azure/services/searchproviders/azure/services/managedredisproviders/azure/services/synapseRemaining work for this issue
Wire each of the six clients' reservation-body builders through the shared
reservations.BillingPlanForPaymentOptionhelper (same pattern PR #1495 applied to compute), with a per-service test asserting the marshaled body carriesbillingPlan: Monthlyfor no-upfront andUpfrontfor all-upfront, and fails loud on empty/partial-upfront. Compute is done in #1495; this issue now tracks the remaining six.bughunter-dotcom commentedon Jul 23, 2026
I can take the six remaining Azure service clients as an assignment-first fixed scope for 120 USDC, delivered within 48 hours after escrow funding.
Proposed acceptance checklist:
reservations.BillingPlanForPaymentOptionhelper fordatabase,cache,cosmosdb,search,managedredis, andsynapse;Upfrontforall-upfront,Monthlyforno-upfront;partial-upfrontvalues rather than allowing Azure's implicit Upfront default;go test ./..., and provide exact output;Before implementation, please confirm:
bughunter-dotcom;mainor be based on open PR feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure, GCP #1495 where the shared helper and compute reference implementation currently live;Escrow option (LaborX, Base USDC): https://laborx.com/gigs/i-will-debug-and-fix-your-api-ci-or-qa-automation-with-tests-109222
Relevant deterministic test-delivery samples: https://github.com/bughunter-dotcom/resilient-http-client-sample and https://github.com/bughunter-dotcom/playwright-ci-sample
If direct fixed-USDC contracting is not available, no problem — I will not submit a speculative implementation.
fix(azure): wire billingPlan into SQL Database reservation purchases
fix(azure): wire billingPlan into Cache for Redis reservation purchases
fix(azure): wire billingPlan into Cosmos DB reservation purchases
fix(azure): wire billingPlan into Search reservation purchases
9 remaining items
docs(config): correct azure payment-coercion rationale
docs(config): future-proof Azure payment-coercion comments for #1495
docs(config): correct azure payment-coercion rationale
fix(azure): wire billingPlan into SQL Database reservation purchases
fix(azure): wire billingPlan into Cache for Redis reservation purchases
fix(azure): wire billingPlan into Cosmos DB reservation purchases
fix(azure): wire billingPlan into Search reservation purchases
fix(azure): wire billingPlan into Managed Redis reservation purchases
fix(azure): wire billingPlan into Synapse reservation purchases
fix(azure): coerce web partial-upfront to monthly, not upfront (#1503) (
cristim commentedon Jul 28, 2026
Additional evidence from the 2026-07-28 full-repo review (reviewed commit
be11bdcb5, origin/main). Confirmed still live onorigin/main; filing here rather than as a duplicate issue.The gap is wider than "Azure RIs always bill upfront": the system actively offers and prices a billing schedule that the purchase path cannot request.
Where (all seven purchase bodies, none of which sets
billingPlan):providers/azure/services/cache/client.go:305-321providers/azure/services/compute/client.go:422-438providers/azure/services/cosmosdb/client.go:306-322providers/azure/services/database/client.go:336-352providers/azure/services/managedredis/client.go:273-281providers/azure/services/search/client.go:256-272providers/azure/services/synapse/client.go:279-287What:
rec.PaymentOptionis only consumed to build thedisplayNamestring.billingPlan(armreservations.ReservationBillingPlanMonthly/...Upfront) is never emitted in any of the seven.The aggravating detail:
ExpandPaymentVariants(providers/azure/internal/recommendations/converter.go:358-403) actively fans every recommendation into an "upfront" and a "monthly" variant, andGetOfferingDetails(e.g.cache/client.go) renders a monthly cashflow preview for the monthly variant that the purchase path cannot honor.Failure scenario: a user picks the "monthly" variant of a 3-year VM reservation quoted at roughly $X/month. Azure defaults the omitted
billingPlantoUpfrontand charges the full 3-year price on day one. The approved cashflow and the actual charge differ by the entire upfront amount.Fix direction (unchanged from this issue): map
rec.PaymentOptionto thearmreservations.ReservationBillingPlanenum constant, send it in both the calculatePrice and purchase bodies, and fail closed on an unrecognised value.Given that the monthly variant is surfaced and priced to the user, the severity and priority on this issue (currently
severity/medium,priority/p3) may be worth revisiting.feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure…
cristim commentedon Sep 2, 2026
Verified resolved at 3c0f8ac: rec.PaymentOption is mapped through reservations.BillingPlanForPaymentOption to the typed armreservations.ReservationBillingPlan enum and emitted as properties.billingPlan in the reservation body, with empty and partial-upfront values failing loud instead of silently defaulting to Upfront. Evidence: providers/azure/services/compute/client.go:499-505 and :436 (#1495, commit 887d51f). Residual axes checked: all seven sibling services (compute, cache, cosmosdb, database, managedredis, search, synapse) set billingPlan and each carries a billingPlan regression test; the same body is sent to both calculatePrice and purchase so the quoted and charged schedules agree; the MCP tool's blanket non-all-upfront rejection was lifted and only partial-upfront is now refused, which Azure genuinely cannot express; the RI-exchange path hardcodes Upfront but exposes no caller-supplied payment option, so nothing is silently overridden there. Closing as completed; reopen if the behaviour recurs.
feat(mcp): CUDly MCP server for RI/SP/CUD purchases across AWS, Azure…