Azure / Azure/azure-dev

[Feature] First-class idempotent "ensure" semantics for role assignments

Open
#9,253 0 comments 0 reactions 0 assignees View on GitHub
area/bicep enhancement
Dominant language
Go
Stars
569
Forks
364
Avg merge
2d 19h
Merged PRs (30d)
136

Description

### Summary

Role assignments are not idempotent to declare, and neither Bicep nor `azd` can express *"create this grant only if an equivalent one doesn't already exist"*. We need upsert-by-tuple **ensure** semantics.

### What happens

`azd provision` fails with `RoleAssignmentExists` when a matching **(principalId, roleDefinitionId, scope)** tuple already exists under a **different assignment name** (a GUID) — e.g. an out-of-band grant applied by another process/team in a locked resource group.

### Why a Bicep `if` condition does not solve it

A Bicep `if (...)` only gates whether a resource is declared; to gate it correctly you'd need to answer *"does a role assignment for this (principal, role, scope) already exist, under any name?"* — which ARM/Bicep can't:

1. **No query/enumeration primitive** — there's no `listRoleAssignments()` to test whether a matching tuple exists, so the `if` condition has no truthful input.
2. **`existing` requires the name, which you don't have** — `existing` by a computed deterministic GUID only matches assignments created with *that* name; the out-of-band grant has a different, arbitrary name, so it finds nothing.
3. **Deterministic naming (`guid(scope, principalId, roleId)`) only makes your *own* assignments idempotent** — it does nothing about a conflicting assignment created by someone else under a different name; ARM still returns `RoleAssignmentExists` on the tuple collision.
4. **No native upsert/ensure** — ARM/Bicep has no "ensure a grant for this tuple exists; treat an existing one under any name as success."

In short, an `if` condition needs a boolean derived from **live RBAC state**, and Bicep has no way to obtain it — the gate has no input.

### Current workaround

`hooks/lib/ensure-role-assignment.ts` — a thin wrapper over `az role assignment create` that **treats `RoleAssignmentExists` as success**. We removed the offending `roleAssignments` from Bicep entirely.

### Ask

First-class **idempotent "ensure"** semantics for role assignments — an upsert keyed on the (principal, role, scope) tuple that succeeds if the grant already exists under any name, expressible declaratively rather than via an imperative `az` hook.

_Note: this may be more of a Bicep/ARM platform gap than a pure `azd` gap; filing here for visibility and because `azd` is where the failure surfaces._

---
_Context: encountered while deploying a multi-component AI/chat service (App Service + Function App + Logic App + Foundry hosted agent) end-to-end with `azd` provision/deploy in [Azure/azure-sdk-tools `tools/sdk-ai-bots/deployment`](https://github.com/Azure/azure-sdk-tools/tree/main/tools/sdk-ai-bots/deployment). We currently work around this with a TypeScript hook._

Contributor guide

Open the contributing guide

Research direction

Start with hooks/lib/ensure-role-assignment.ts and the linked tools/sdk-ai-bots/deployment context to understand the current TypeScript workaround and how azd provision invokes it. Compare that workaround with the requested declarative role-assignment behavior; the issue is complete only when an agreed implementation target and acceptance criteria exist for ensure semantics.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, typescript
Domain
authorization, cloud
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.