The happy path that fails at the last step:

```python
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

credential = DefaultAzureCredential()
client = BlobServiceClient(
    account_url="https://YOUR-ACCOUNT.blob.core.windows.net",
    credential=credential,
)
container = client.get_container_client("[container]")
container.upload_blob(name="[blob-name]", data=open("file.bin", "rb"))
```

What agents get wrong:

1. **The RBAC trap.** Being Owner or Contributor on the subscription does NOT grant blob data access. Data-plane operations need one of the data roles: Storage Blob Data Contributor (read/write), Storage Blob Data Reader, Storage Blob Data Owner. Assign it at the storage account scope:
   `az role assignment create --assignee [object-id] --role "Storage Blob Data Contributor" --scope /subscriptions/[sub]/resourceGroups/[rg]/providers/Microsoft.Storage/storageAccounts/[account]`
   Then wait. Role assignments can take several minutes to propagate; an immediate 403 right after assigning the role is usually propagation, not a wrong role.

2. **Connection string vs identity.** `BlobServiceClient.from_connection_string()` works but puts a storage key in your config. Prefer identity (DefaultAzureCredential). If the key leaks or is rotated, the connection string breaks everywhere; the identity keeps working.

3. **Shared Key disabled.** Some orgs disable Shared Key auth on the account (`allowSharedKeyAccess=false`). Then connection strings fail by policy and only identity works. Check the account setting before assuming your string is bad.

4. **Container must exist.** `upload_blob` to a missing container is a 404, not auto-created. Use `create_container` first or `container_client.exists()`.

5. **Sync vs async.** `azure-storage-blob` has an `aio` namespace. Do not mix: if you import from `azure.storage.blob.aio`, you need `await` everywhere and an async credential. Pick one.

Verify: list blobs after upload, then download and compare bytes. If upload works but download 403s, you have a read-role gap, which is exactly the Owner-without-data-role trap.