Repository navigation
ci(azure): Deploy to Azure Container Apps has failed on every main run for 3 weeks — no Azure change since 2026-07-19 is deployed #1794
Description
Activity
- addedtriagedItem has been triagedItem has been triagedpriority/p0Drop everything; same-day fixDrop everything; same-day fixseverity/criticalMajor harm when it happensMajor harm when it happensurgency/nowDrop other thingsDrop other thingsimpact/all-usersAffects every userAffects every usereffort/mDaysDaystype/bugDefectDefect
on Aug 9, 2026 Corrected root cause: the bootstrap role is unappliable, not unapplied
This issue currently says the custom role was never created because the bootstrap step was missed. That is wrong, and the real cause has a wider blast radius. Established by execution against the live subscription, not by reading.
The actual cause
Microsoft.Capacity/reservationOrders/purchase/actiondoes not exist in Azure's operation catalog.Applying
terraform/environments/azure/ci-cd-permissionsfails with:Error: unexpected status 400 (400 Bad Request) with error: InvalidActionOrNotAction: 'Microsoft.Capacity/reservationOrders/purchase/action' does not match any of the actions supported by the providers. with module.cudly_reservation_role.azurerm_role_definition.cudly_reservation_purchaser, on ../../../modules/iam/azure/cudly-reservation-role/main.tf line 35Azure rejects the whole role definition over that one entry, so the role is never created, so the deploy stack's name-based
data.azurerm_role_definitionlookup finds nothing. The bootstrap stack was not skipped: it cannot succeed as written, and re-running it would never have helped.I validated all 12 actions in the module against the live catalog (
az provider operation show, covering both namespace-leveloperations[]andresourceTypes[].operations[]). Exactly one is invalid; the other 11 are fine.Hypothesis tested and ruled out
Microsoft.CapacityandMicrosoft.BillingBenefitswere bothNotRegisteredin subscription24d185cc. That looked like the answer, since Azure validates role actions against registered providers. I registered both and re-applied: identical error. Registration was not the cause.The two registrations are additive, reversible, and arguably correct to keep, but they were an incidental change rather than the fix, and I am flagging them rather than leaving them undisclosed.
The module's stated premise is also wrong
The module justifies its own existence with:
The built-in Reservation Purchaser role lacks
reservationOrders/purchase/action, which causes 403 on production reservation purchases.The built-in role's actual actions do not include
Microsoft.Capacity/reservationOrders/write,Microsoft.Capacity/calculatePrice/action, or anyMicrosoft.BillingBenefitsaction. So the custom role's purpose is legitimate, but the action added to serve that purpose is fictional. Purchasing a reservation isPUT /providers/Microsoft.Capacity/reservationOrders/{id}, which isreservationOrders/writeand was already in the list.Blast radius is wider than this issue
arm/CUDly-CrossSubscription/template.json:51carries the same invalid action. That template is customer-facing: customers deploy it to grant CUDly cross-subscription access. It should fail identically for every customer who has run it. That deserves its own triage.iac/federation/azure-target/terraformis the other customer-side consumer.Why no test caught this
scripts/testdata/role-parity/matching-arm.jsonandmatching-tf.tf.fixtureboth contain the invalid action. The ARM-vs-TF parity checker therefore passed while both sides were invalid. The fixtures prove the two representations agree; nothing proved either is a thing Azure accepts. Parity is not validity.Verification
Removing the single invalid action and applying against the live subscription created the role:
Apply complete! Resources: 1 added, 0 changed, 0 destroyed. reservation_role_definition_id = "/subscriptions/24d185cc-.../roleDefinitions/ab9419be-..."Confirmed in both directions: fails with the action present, succeeds with it removed.
Answering the open question about deployed state
The container app is running
cudly:ddeeb98-20260811004918, andddeeb98is the currentmainHEAD. The deploy job builds and ships the image, then fails later duringterraform applywhen the role lookup is resolved (plan defers it: the log showsdata.azurerm_role_definition.cudly_reservation_purchaser will be read during apply).So application code from the merged Azure money-path work is live. What has not been applying is the Terraform-managed infrastructure ordered after the failure point. That is a meaningfully better position than the worst case, but the two should not be conflated.
A rerun of the failed deploy is in flight now that the role exists; I will post the result.
Note on the CI diagnostic from #1795
The
Explain a missing bootstrap rolestep added in #1795 tells operators the bootstrap stack was never applied. Given the above, that message points at the wrong cause and is being corrected in the fix PR.
The Azure deploy has not succeeded on
mainin the last 100 workflow runs, going back to at least 2026-07-19. Found while checking whether #1223 was stale.It fails at Terraform Apply, not at build or test:
Blocked resources:
azurerm_role_assignment.cost_management_readerandazurerm_role_assignment.subscription_reader.Why this went unnoticed for three weeks
CI - Build & Testis green onmainand has been across every recent merge (a37e14790,d6e60f637,726389b48). Every PR gate we look at is green. The deploy workflow is a separate run that nothing surfaces, so "CI is green" has been true and irrelevant at the same time.This is the same shape as the multi-module gap in #1751: a check that looked comprehensive was scoped to something narrower than anyone assumed. There,
./...covered one module of six. Here, "CI green" covers build and test but not deploy.What it means for what we have been merging
If this workflow is what ships the running Azure service, then no Azure change merged since 2026-07-19 is live. That window includes several
providers/azuremoney-path fixes merged in the last day:execute:ri-exchangecarved out ofadmin:*Verify that inference before acting on it. I have established the workflow fails; I have not established what it deploys, whether another path ships the Azure service, or whether the failing role assignments are the last step or an early one that aborts before anything useful happens. A partial apply that fails at the end has different consequences from one that fails before deploying the app image.
The immediate cause
A custom role definition named
CUDly Reservation Purchaser (custom) - <subscription>does not exist in the target subscription, so Terraform cannot resolve it to assign it.Establish which of these it is before fixing:
Option 4 is worth ruling out explicitly:
Microsoft.Authorization/roleDefinitions/readis a separate grant from the ability to create assignments, and a caller without it sees an empty list rather than a permission error.Verification for whatever lands
mainreaching success, not merely getting past this error to a different one.main, since three weeks of failed applies may have left state that a single success does not fully reconcile.Related
CI - Build & Testis green onmain.