Skip to content

Commit cea0f00

Browse files
committed
fix(iac/azure): register Microsoft.BillingBenefits RP via custom role (closes #752)
SP-virgin subscriptions 409 with MissingSubscriptionRegistration on the first Savings Plan order because the Microsoft.BillingBenefits resource provider is not auto-registered. Adding Microsoft.BillingBenefits/register/action to the custom role lets CUDly register the RP on first use without requiring the customer to pre-register it manually via az provider register.
1 parent f5b76eb commit cea0f00

5 files changed

Lines changed: 87 additions & 0 deletions

File tree

‎arm/CUDly-CrossSubscription/template.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"Microsoft.Capacity/reservationorders/reservations/read",
5151
"Microsoft.Capacity/register/action",
5252
"Microsoft.Capacity/catalogs/read",
53+
"Microsoft.BillingBenefits/register/action",
5354
"Microsoft.BillingBenefits/savingsPlanOrderAliases/write",
5455
"Microsoft.BillingBenefits/savingsPlanOrders/read",
5556
"Microsoft.BillingBenefits/savingsPlanOrders/savingsPlans/read",

‎terraform/modules/iam/azure/cudly-reservation-role/.terraform.lock.hcl‎

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
terraform {
2+
required_version = ">= 1.5"
3+
4+
required_providers {
5+
azurerm = {
6+
source = "hashicorp/azurerm"
7+
version = ">= 3.0"
8+
}
9+
}
10+
}
11+
12+
# Custom role: grants the exact Microsoft.Capacity and Microsoft.BillingBenefits
13+
# actions used by the calculatePrice -> purchase flow. The built-in Reservation
14+
# Purchaser role lacks reservationOrders/purchase/action, which causes 403 on
15+
# production reservation purchases.
16+
#
17+
# Used by both:
18+
# - customer-side IaC (iac/federation/azure-target/terraform) for the
19+
# customer SP that CUDly authenticates with via workload identity federation
20+
# - host-side IaC (terraform/modules/compute/azure/container-apps) for the
21+
# container-app user-assigned identity running the CUDly process itself
22+
#
23+
# Keep the actions list here in sync with arm/CUDly-CrossSubscription/template.json.
24+
resource "azurerm_role_definition" "cudly_reservation_purchaser" {
25+
name = "CUDly Reservation Purchaser (custom) - ${var.name_suffix}"
26+
scope = var.scope
27+
description = "Custom role granting CUDly exactly the Microsoft.Capacity and Microsoft.BillingBenefits actions required by the calculatePrice -> purchase flow. Replaces the built-in Reservation Purchaser, which lacks reservationOrders/purchase/action."
28+
29+
permissions {
30+
actions = [
31+
"Microsoft.Capacity/register/action",
32+
"Microsoft.Capacity/calculatePrice/action",
33+
"Microsoft.Capacity/catalogs/read",
34+
"Microsoft.Capacity/reservationOrders/read",
35+
"Microsoft.Capacity/reservationOrders/write",
36+
"Microsoft.Capacity/reservationOrders/purchase/action",
37+
"Microsoft.Capacity/reservationOrders/reservations/read",
38+
"Microsoft.BillingBenefits/register/action",
39+
"Microsoft.BillingBenefits/savingsPlanOrderAliases/write",
40+
"Microsoft.BillingBenefits/savingsPlanOrders/read",
41+
"Microsoft.BillingBenefits/savingsPlanOrders/savingsPlans/read",
42+
"Microsoft.BillingBenefits/savingsPlanOrders/action",
43+
]
44+
not_actions = []
45+
}
46+
47+
assignable_scopes = [
48+
var.scope,
49+
"/providers/Microsoft.Capacity",
50+
]
51+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
output "role_definition_resource_id" {
2+
description = "Full ARM resource ID of the custom role definition. Use as role_definition_id in azurerm_role_assignment blocks."
3+
value = azurerm_role_definition.cudly_reservation_purchaser.role_definition_resource_id
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
variable "scope" {
2+
description = "Subscription resource ID used as the role definition scope and the base of assignable_scopes (e.g. data.azurerm_subscription.current.id)."
3+
type = string
4+
}
5+
6+
variable "name_suffix" {
7+
description = "Suffix appended to the role display name to keep customer and host role definitions distinct within the same Azure AD tenant (e.g. the subscription ID)."
8+
type = string
9+
}

0 commit comments

Comments
 (0)