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.