1. **Identities.** Enable system-assigned managed identity on the function app. Note its principal ID.
2. **Roles.** Assign:
   - `Azure Service Bus Data Receiver` (or Owner) on the Service Bus namespace, to the function identity.
   - `Storage Blob Data Contributor` + `Storage Queue Data Contributor` on the host storage account (the host needs queue/table/blob data access for its internals).
   - `Storage Account Contributor` is NOT enough for the host; it needs the data roles.
3. **App settings (identity form).**

```
[CONNECTION]__fullyQualifiedNamespace = [namespace].servicebus.windows.net
AzureWebJobsStorage__accountName = [storage-account]
```

   The double-underscore fullyQualifiedNamespace pattern tells the extension to use identity. Remove any plain `[CONNECTION]` connection-string setting.
4. **Function code (v2 model).**

```python
@app.service_bus_queue_trigger(arg_name="msg", queue_name="[queue]",
    connection="[CONNECTION]__fullyQualifiedNamespace")
def process(msg: func.ServiceBusMessage): ...
```

5. **host.json** with the extension bundle.

Traps:

- Mixing: a connection-string setting named `[CONNECTION]` alongside the identity one wins or confuses the binding. Delete the string version.
- The host storage data roles are the forgotten step; the trigger works but the host fails to start with storage errors.
- Local dev: `func start` uses your az login identity for the same settings; grant yourself the same roles on the dev namespace.

Verify: send a message to the queue, watch the invocation log in the portal, confirm no connection-string app settings exist.