---
cuppy_hub: azure
cuppy_path: reference/storage/adls-gen2.md
cuppy_section: docs
cuppy_doc: RAAZ-9BZR
cuppy_doc_uuid: 8d3952f5-142e-4c20-b51d-1ee92adb865f
cuppy_permalink: https://cuppy.io/azure/d/RAAZ-9BZR
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/azure/docs/reference/storage/adls-gen2
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 4933b03f0af9bf56a3e2178a62d2068a21808222e689d01e38758c2f24b1156e
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# ADLS Gen2 — RBAC, ABAC and ACLs together

**Order of evaluation for an Entra ID caller:** (1) Azure role assignments; if one grants the action, (2) its ABAC conditions are checked; if they match, access is granted. (3) Only if nothing granted it are the **ACLs** evaluated. So **an ACL can't restrict access that a role already grants.** Give broad roles sparingly (Storage Blob Data Reader at the account means everyone with it reads everything) and use ACLs for fine-grained grants.

**Shared Key, account SAS and service SAS skip all of this:** no identity, so RBAC, ABAC and ACLs have no effect, and items they create are owned by `$superuser`. **Anonymous read** on a container also ignores its ACLs (writes still honour them). Both are reasons to turn them off (`reference/storage/access-models.md`).

## ACL rules that cause 403s

| Rule | Consequence |
|---|---|
| **Execute (`X`) on every parent directory**, from the container root down, is needed to reach an item when access comes from ACLs only | Granting `r--` on a file alone still returns 403 |
| Listing a directory needs **Read + Execute** on it; creating children needs **Write + Execute** | — |
| **Default ACLs** are templates for **new** children only; changing a parent's default ACL doesn't change existing children | Use recursive ACL updates (`az storage fs access set-recursive` / `update-recursive`); not available in the portal |
| Permissions aren't inherited after the fact: an item's ACL is stored on the item | Moving or creating items before setting defaults leaves them without the grant |
| For a **service principal**, use the **service principal's object ID**, not the app registration's | `az ad sp show --id <appId> --query id -o tsv` |
| ACLs apply only to principals in the **same tenant** | Principals from another tenant get nothing from an ACL |
| **32 entries** per access ACL and per default ACL, effectively 28 usable | Grant to **groups**, not individuals |
| The **mask** caps named users, named groups and the owning group | An entry with `rwx` under mask `r-x` can't write |
| **Sticky bit** on a directory: only the child's owner, the directory's owner or the superuser can delete or rename children | 403 on delete or rename for everyone else |
| Changing permissions: the item's **owning user** (its creator) or a **super-user**; changing the owning user: a super-user only. Storage Blob Data **Owner** is the role Microsoft lists for setting ownership and managing ACLs | Setting ACLs as a Contributor on items someone else created fails |

## Blob APIs on a hierarchical-namespace account

- Blob APIs, Data Lake APIs and NFS 3.0 can read the same data, but **can't write the same file**: a file written with the Data Lake API can't take Put Block / Put Block List (409 `BlobOperationNotSupported`); overwrite with Put Blob instead.
- List Blobs without a delimiter returns directories too; the only supported delimiter is `/`.
- Blob Delete removes a directory only when it's empty; use the Data Lake API with `recursive: true`.
- Not supported: page blobs (Put Page, Get Page Ranges), Incremental Copy Blob, Put Page from URL, Append Blob Seal, unmanaged VM disks.
- **Private endpoints:** create both `blob` and `dfs`; ACL management and directory operations need `dfs`.

## Client choice (TypeScript)

Use `@azure/storage-file-datalake` (`DataLakeServiceClient`, endpoint `https://<acct>.dfs.core.windows.net`) for directories, renames and ACLs: `fileSystemClient.getDirectoryClient(path).setAccessControlRecursive(acl)`. Use `@azure/storage-blob` for plain object reads and writes. Both take the same `TokenCredential`. Setup: `guides/storage/access-blobs-from-typescript.md`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 107 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/azure/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
