diff --git a/.github/workflows/deploy-azure.yml b/.github/workflows/deploy-azure.yml index f541f174f..387e4b7c3 100644 --- a/.github/workflows/deploy-azure.yml +++ b/.github/workflows/deploy-azure.yml @@ -237,9 +237,12 @@ jobs: # The provider fails the data read outright when the custom role is absent, # so a Terraform precondition can never run for this case. Issue #1794: the - # Azure deploy failed on every main run for three weeks because the raw - # provider error names neither the missing prerequisite nor the stack that - # creates it, and the explanation lived only in a source comment. + # Azure deploy failed on every main run since 2026-07-19 because the + # bootstrap stack's role definition carried an action that does not exist + # in Azure's operation catalog, so Azure rejected the whole role definition + # with InvalidActionOrNotAction and the role was never created. That action + # is removed in this PR. The message below still covers the general "role + # missing" case for any future recurrence. - name: Explain a missing bootstrap role if: failure() run: | @@ -276,11 +279,16 @@ jobs: pipeline holds roleAssignments/write only. If the bootstrap HAS been applied, check in this order: - 1. the deploy SP has Microsoft.Authorization/roleDefinitions/read. + 1. the bootstrap stack's own apply log for InvalidActionOrNotAction. + That means the role definition itself was rejected by Azure and + never created, not merely unassigned (issue #1794's root cause: + a nonexistent action in the role's actions list). Make sure the + bootstrap module is on a commit that includes that fix. + 2. the deploy SP has Microsoft.Authorization/roleDefinitions/read. Without it the lookup returns empty, which is indistinguishable from the role being absent. - 2. the role was not renamed or deleted out of band. - 3. the name suffix still matches. The runtime module looks up + 3. the role was not renamed or deleted out of band. + 4. the name suffix still matches. The runtime module looks up "CUDly Reservation Purchaser (custom) - "; the bootstrap builds the name from its own var.name_suffix. EOT diff --git a/arm/CUDly-CrossSubscription/template.json b/arm/CUDly-CrossSubscription/template.json index 8cdbdb03d..7a7a0c33e 100644 --- a/arm/CUDly-CrossSubscription/template.json +++ b/arm/CUDly-CrossSubscription/template.json @@ -39,7 +39,7 @@ "name": "[variables('customRoleName')]", "properties": { "roleName": "CUDly Reservation Purchaser (custom)", - "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.", + "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/write, calculatePrice/action and every Microsoft.BillingBenefits action.", "type": "CustomRole", "permissions": [ { @@ -49,7 +49,6 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/register/action", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", @@ -79,7 +78,7 @@ "roleDefinitionId": "[variables('customRoleDefinitionId')]", "principalId": "[parameters('servicePrincipalObjectId')]", "principalType": "ServicePrincipal", - "description": "CUDly — subscription-scope assignment of custom role; grants calculatePrice + purchase/action required by the two-step reservation purchase flow" + "description": "CUDly — subscription-scope assignment of custom role; grants calculatePrice/action and reservationOrders/write required by the two-step reservation purchase flow" } }, diff --git a/iac/federation/azure-target/terraform/main.tf b/iac/federation/azure-target/terraform/main.tf index 8d8c0b7e3..4dd428c4a 100644 --- a/iac/federation/azure-target/terraform/main.tf +++ b/iac/federation/azure-target/terraform/main.tf @@ -60,8 +60,9 @@ resource "azuread_application_federated_identity_credential" "cudly" { # Custom role: grants the exact Microsoft.Capacity and Microsoft.BillingBenefits actions # used by the calculatePrice -> purchase flow (introduced in PR #680). The built-in -# Reservation Purchaser role lacks reservationOrders/purchase/action, which causes 403 on -# production reservation purchases. +# Reservation Purchaser role lacks reservationOrders/write and calculatePrice/action, plus +# every Microsoft.BillingBenefits action, which is what caused 403s on production +# reservation purchases. # # The role definition is factored into a shared module so the customer-side (here) and # host-side (terraform/modules/compute/azure/container-apps) definitions stay in lockstep. diff --git a/scripts/testdata/role-parity/canonical-scope-variants-arm.json b/scripts/testdata/role-parity/canonical-scope-variants-arm.json index a79f213c2..4e08264ca 100644 --- a/scripts/testdata/role-parity/canonical-scope-variants-arm.json +++ b/scripts/testdata/role-parity/canonical-scope-variants-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/collision-assignablescopes-benign-first-arm.json b/scripts/testdata/role-parity/collision-assignablescopes-benign-first-arm.json index a4793ccc8..1e333e24d 100644 --- a/scripts/testdata/role-parity/collision-assignablescopes-benign-first-arm.json +++ b/scripts/testdata/role-parity/collision-assignablescopes-benign-first-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/collision-assignablescopes-evil-first-arm.json b/scripts/testdata/role-parity/collision-assignablescopes-evil-first-arm.json index 06c03d9ca..fa2d62bcf 100644 --- a/scripts/testdata/role-parity/collision-assignablescopes-evil-first-arm.json +++ b/scripts/testdata/role-parity/collision-assignablescopes-evil-first-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/collision-properties-evil-first-arm.json b/scripts/testdata/role-parity/collision-properties-evil-first-arm.json index ea1fac29b..42dad6db5 100644 --- a/scripts/testdata/role-parity/collision-properties-evil-first-arm.json +++ b/scripts/testdata/role-parity/collision-properties-evil-first-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/collision-roledefinitionid-evil-first-arm.json b/scripts/testdata/role-parity/collision-roledefinitionid-evil-first-arm.json index e617dba27..311da5abc 100644 --- a/scripts/testdata/role-parity/collision-roledefinitionid-evil-first-arm.json +++ b/scripts/testdata/role-parity/collision-roledefinitionid-evil-first-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/collision-root-resources-evil-first-arm.json b/scripts/testdata/role-parity/collision-root-resources-evil-first-arm.json index 28d5bdff4..7caab38a4 100644 --- a/scripts/testdata/role-parity/collision-root-resources-evil-first-arm.json +++ b/scripts/testdata/role-parity/collision-root-resources-evil-first-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", @@ -58,7 +58,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/collision-type-evil-first-arm.json b/scripts/testdata/role-parity/collision-type-evil-first-arm.json index 76b630cfb..476c138f3 100644 --- a/scripts/testdata/role-parity/collision-type-evil-first-arm.json +++ b/scripts/testdata/role-parity/collision-type-evil-first-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/dataactions-wildcard-arm.json b/scripts/testdata/role-parity/dataactions-wildcard-arm.json index 29af37e51..8e2bb2169 100644 --- a/scripts/testdata/role-parity/dataactions-wildcard-arm.json +++ b/scripts/testdata/role-parity/dataactions-wildcard-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/decorative-variables-arm.json b/scripts/testdata/role-parity/decorative-variables-arm.json index bfced043b..dcf94c453 100644 --- a/scripts/testdata/role-parity/decorative-variables-arm.json +++ b/scripts/testdata/role-parity/decorative-variables-arm.json @@ -33,7 +33,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/deploymentscript-scope-escape-arm.json b/scripts/testdata/role-parity/deploymentscript-scope-escape-arm.json index 6c474de5b..acef6a34b 100644 --- a/scripts/testdata/role-parity/deploymentscript-scope-escape-arm.json +++ b/scripts/testdata/role-parity/deploymentscript-scope-escape-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/deploymentstack-scope-escape-arm.json b/scripts/testdata/role-parity/deploymentstack-scope-escape-arm.json index 738a409a5..99761ca7e 100644 --- a/scripts/testdata/role-parity/deploymentstack-scope-escape-arm.json +++ b/scripts/testdata/role-parity/deploymentstack-scope-escape-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/foreign-subscription-literal-with-canonical-arm.json b/scripts/testdata/role-parity/foreign-subscription-literal-with-canonical-arm.json index 03730c27d..842c74f15 100644 --- a/scripts/testdata/role-parity/foreign-subscription-literal-with-canonical-arm.json +++ b/scripts/testdata/role-parity/foreign-subscription-literal-with-canonical-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/legacy-child-keyvault-roleassignment-owner-arm.json b/scripts/testdata/role-parity/legacy-child-keyvault-roleassignment-owner-arm.json index 5065983d9..f2b59630a 100644 --- a/scripts/testdata/role-parity/legacy-child-keyvault-roleassignment-owner-arm.json +++ b/scripts/testdata/role-parity/legacy-child-keyvault-roleassignment-owner-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/legacy-child-roleassignment-owner-arm.json b/scripts/testdata/role-parity/legacy-child-roleassignment-owner-arm.json index 2bce079e1..158c13a2a 100644 --- a/scripts/testdata/role-parity/legacy-child-roleassignment-owner-arm.json +++ b/scripts/testdata/role-parity/legacy-child-roleassignment-owner-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/lowercase-tenant-scope-arm.json b/scripts/testdata/role-parity/lowercase-tenant-scope-arm.json index 34884bdbd..7f4d2f5b0 100644 --- a/scripts/testdata/role-parity/lowercase-tenant-scope-arm.json +++ b/scripts/testdata/role-parity/lowercase-tenant-scope-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/lowercase-type-wildcard-actions-arm.json b/scripts/testdata/role-parity/lowercase-type-wildcard-actions-arm.json index dc24c3b00..7e66df61b 100644 --- a/scripts/testdata/role-parity/lowercase-type-wildcard-actions-arm.json +++ b/scripts/testdata/role-parity/lowercase-type-wildcard-actions-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/matching-arm.json b/scripts/testdata/role-parity/matching-arm.json index 516dc12cd..cfe1173c0 100644 --- a/scripts/testdata/role-parity/matching-arm.json +++ b/scripts/testdata/role-parity/matching-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/matching-tf.tf.fixture b/scripts/testdata/role-parity/matching-tf.tf.fixture index 2a51ab4d9..39d7f81a2 100644 --- a/scripts/testdata/role-parity/matching-tf.tf.fixture +++ b/scripts/testdata/role-parity/matching-tf.tf.fixture @@ -11,7 +11,7 @@ resource "azurerm_role_definition" "cudly_reservation_purchaser" { "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/mgmt-group-schema-arm.json b/scripts/testdata/role-parity/mgmt-group-schema-arm.json index c91eb6d8d..64365cc54 100644 --- a/scripts/testdata/role-parity/mgmt-group-schema-arm.json +++ b/scripts/testdata/role-parity/mgmt-group-schema-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/miscased-assignablescopes-arm.json b/scripts/testdata/role-parity/miscased-assignablescopes-arm.json index 0598ec9a1..4a4a440d1 100644 --- a/scripts/testdata/role-parity/miscased-assignablescopes-arm.json +++ b/scripts/testdata/role-parity/miscased-assignablescopes-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", @@ -46,7 +46,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/miscased-properties-arm.json b/scripts/testdata/role-parity/miscased-properties-arm.json index 22dd483c0..d8989aae1 100644 --- a/scripts/testdata/role-parity/miscased-properties-arm.json +++ b/scripts/testdata/role-parity/miscased-properties-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/miscased-roledefinitionid-arm.json b/scripts/testdata/role-parity/miscased-roledefinitionid-arm.json index 6796472df..d1701e7df 100644 --- a/scripts/testdata/role-parity/miscased-roledefinitionid-arm.json +++ b/scripts/testdata/role-parity/miscased-roledefinitionid-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/miscased-scope-arm.json b/scripts/testdata/role-parity/miscased-scope-arm.json index 819979f7f..af0a959f7 100644 --- a/scripts/testdata/role-parity/miscased-scope-arm.json +++ b/scripts/testdata/role-parity/miscased-scope-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/nested-deployment-arm.json b/scripts/testdata/role-parity/nested-deployment-arm.json index 4fd6f4ae5..112be4f3c 100644 --- a/scripts/testdata/role-parity/nested-deployment-arm.json +++ b/scripts/testdata/role-parity/nested-deployment-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/obfuscated-tenant-scope-arm.json b/scripts/testdata/role-parity/obfuscated-tenant-scope-arm.json index 96c952326..c0ca9d492 100644 --- a/scripts/testdata/role-parity/obfuscated-tenant-scope-arm.json +++ b/scripts/testdata/role-parity/obfuscated-tenant-scope-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/other-subscription-arm.json b/scripts/testdata/role-parity/other-subscription-arm.json index 96d5c9ece..0862d5615 100644 --- a/scripts/testdata/role-parity/other-subscription-arm.json +++ b/scripts/testdata/role-parity/other-subscription-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/pim-roleassignment-schedule-owner-arm.json b/scripts/testdata/role-parity/pim-roleassignment-schedule-owner-arm.json index c23c90b9a..245fb60ee 100644 --- a/scripts/testdata/role-parity/pim-roleassignment-schedule-owner-arm.json +++ b/scripts/testdata/role-parity/pim-roleassignment-schedule-owner-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/pim-roleeligibility-owner-arm.json b/scripts/testdata/role-parity/pim-roleeligibility-owner-arm.json index 0893d8ee2..42c25eb38 100644 --- a/scripts/testdata/role-parity/pim-roleeligibility-owner-arm.json +++ b/scripts/testdata/role-parity/pim-roleeligibility-owner-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/second-permissions-entry-arm.json b/scripts/testdata/role-parity/second-permissions-entry-arm.json index 9891bf4ba..41dbbb979 100644 --- a/scripts/testdata/role-parity/second-permissions-entry-arm.json +++ b/scripts/testdata/role-parity/second-permissions-entry-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/space-inside-literal-arm.json b/scripts/testdata/role-parity/space-inside-literal-arm.json index d56de7492..938da0a1d 100644 --- a/scripts/testdata/role-parity/space-inside-literal-arm.json +++ b/scripts/testdata/role-parity/space-inside-literal-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/tenant-scope-arm.json b/scripts/testdata/role-parity/tenant-scope-arm.json index aabccaf64..416664fc1 100644 --- a/scripts/testdata/role-parity/tenant-scope-arm.json +++ b/scripts/testdata/role-parity/tenant-scope-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/unallowed-roledefinitionid-arm.json b/scripts/testdata/role-parity/unallowed-roledefinitionid-arm.json index 9189f0ae7..4e2314553 100644 --- a/scripts/testdata/role-parity/unallowed-roledefinitionid-arm.json +++ b/scripts/testdata/role-parity/unallowed-roledefinitionid-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/uppercase-allowed-roledefinitionid-arm.json b/scripts/testdata/role-parity/uppercase-allowed-roledefinitionid-arm.json index 2fdcdfed0..072b4eba1 100644 --- a/scripts/testdata/role-parity/uppercase-allowed-roledefinitionid-arm.json +++ b/scripts/testdata/role-parity/uppercase-allowed-roledefinitionid-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/uppercase-canonical-scope-arm.json b/scripts/testdata/role-parity/uppercase-canonical-scope-arm.json index 7e0acee8f..b2d122506 100644 --- a/scripts/testdata/role-parity/uppercase-canonical-scope-arm.json +++ b/scripts/testdata/role-parity/uppercase-canonical-scope-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/scripts/testdata/role-parity/uppercase-guid-literal-arm.json b/scripts/testdata/role-parity/uppercase-guid-literal-arm.json index 0ae919a83..c5d3858d1 100644 --- a/scripts/testdata/role-parity/uppercase-guid-literal-arm.json +++ b/scripts/testdata/role-parity/uppercase-guid-literal-arm.json @@ -16,7 +16,7 @@ "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", + "Microsoft.BillingBenefits/register/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write", "Microsoft.BillingBenefits/savingsPlanOrders/read", diff --git a/terraform/modules/compute/azure/container-apps/main.tf b/terraform/modules/compute/azure/container-apps/main.tf index 433d49349..04cfcc69b 100644 --- a/terraform/modules/compute/azure/container-apps/main.tf +++ b/terraform/modules/compute/azure/container-apps/main.tf @@ -261,7 +261,8 @@ resource "azurerm_role_assignment" "subscription_reader" { # Custom reservation-purchaser role: same role as customer-side but scoped to # the host subscription. The built-in Reservation Purchaser lacks -# reservationOrders/purchase/action (same gap fixed customer-side by PR #744). +# reservationOrders/write and calculatePrice/action, plus every +# Microsoft.BillingBenefits action (same gap fixed customer-side by PR #744). # # BOOTSTRAP-vs-RUNTIME SPLIT: the role *definition* is created by the # human-applied bootstrap module diff --git a/terraform/modules/iam/azure/cudly-reservation-role/main.tf b/terraform/modules/iam/azure/cudly-reservation-role/main.tf index e9acc56f0..ef4407a14 100644 --- a/terraform/modules/iam/azure/cudly-reservation-role/main.tf +++ b/terraform/modules/iam/azure/cudly-reservation-role/main.tf @@ -15,7 +15,8 @@ terraform { # Custom role: grants the exact Microsoft.Capacity and Microsoft.BillingBenefits # actions used by the calculatePrice -> purchase flow. The built-in Reservation -# Purchaser role lacks reservationOrders/purchase/action, which causes 403 on +# Purchaser role lacks reservationOrders/write and calculatePrice/action, plus +# every Microsoft.BillingBenefits action, which is what caused 403s on # production reservation purchases. # # Used by both: @@ -35,7 +36,7 @@ locals { resource "azurerm_role_definition" "cudly_reservation_purchaser" { name = local.role_definition_name scope = var.scope - 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." + 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/write, calculatePrice/action and every Microsoft.BillingBenefits action." permissions { actions = [ @@ -44,7 +45,6 @@ resource "azurerm_role_definition" "cudly_reservation_purchaser" { "Microsoft.Capacity/catalogs/read", "Microsoft.Capacity/reservationOrders/read", "Microsoft.Capacity/reservationOrders/write", - "Microsoft.Capacity/reservationOrders/purchase/action", "Microsoft.Capacity/reservationOrders/reservations/read", "Microsoft.BillingBenefits/register/action", "Microsoft.BillingBenefits/savingsPlanOrderAliases/write",