ADLS Gen2 — how RBAC, ABAC and ACLs combine, ACL rules, and Blob API differences
How Azure Data Lake Storage authorizes a request on a hierarchical-namespace account — role assignments first, then ABAC conditions, then POSIX ACLs, so an ACL can never take away what a role grants — with the ACL rules that cause most 403s (Execute on every parent, default ACLs only apply to new children, service principal object ID, 32-entry limit, mask, sticky bit, $superuser from Shared Key), what anonymous access and Shared Key skip, and where Blob APIs and Data Lake APIs disagree.
markdownreferencev1RAAZ-9BZRstage: buildtrust: officialservice: storageinterface: anyADLS Gen2 403Data Lake ACLPOSIX ACLaccess ACLdefault ACLExecute permission traverseACL inheritance existing childrenset ACL recursivelyaz storage fs access set-recursiveservice principal object ID ACL32 ACL entriesACL masksticky bit 403$superuserStorage Blob Data Owner ACLRBAC ACL evaluation orderABAC role assignment conditionACL cannot restrict RBACdfs endpointDataLakeServiceClienthierarchical namespace known issues
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
blobanddfs; ACL management and directory operations needdfs.
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.