By the end of this build sheet your pilot share has production-shaped identity: named Entra groups at the RBAC gate, a working NTFS ACL tree that a cloud-only user actually hits, group SIDs flowing in tickets, the Conditional Access exclusion in place and documented, and a validation pass proving a standard user sees exactly what the ACLs say. No domain controller, no hybrid sync, no storage key in daily use.
Prerequisites: the [AF 1.1] share (or any share with Entra Kerberos enabled and admin consent granted); Entra roles to create groups and edit Conditional Access; Owner or User Access Administrator on the storage account for role assignments; a Windows 11 24H2+ Entra-joined test device and two test users, one intended for read-write and one for read-only. This sheet assumes cloud-only identities; every step also works for hybrid identities under Entra Kerberos. Step 3 additionally requires that the storage account sits in a region on the published list for cloud-only per-user and per-group share-level RBAC, which the account built in the starter sheet does. iphlpsvc and WinHttpAutoProxySvc must be running on the test device, as in the starter sheet.
Step 1: Create the access groups
Model permissions in groups from the first minute, even in a pilot, because retrofitting groups under per-user ACLs is the single most tedious cleanup this platform offers. My naming convention for file access groups carries the share and the level: AZF-<share>-RW and AZF-<share>-RO, plus one AZF-<share>-Admins per share for the people allowed to edit ACLs.
Connect-MgGraph -Scopes "Group.ReadWrite.All"
foreach ($g in "AZF-Pilot-RW","AZF-Pilot-RO","AZF-Pilot-Admins") {
New-MgGroup -DisplayName $g -MailEnabled:$false -MailNickname $g `
-SecurityEnabled:$true
}
Add your read-write test user to AZF-Pilot-RW, the read-only user to AZF-Pilot-RO, and your own admin account to AZF-Pilot-Admins. Expected result: three security groups, memberships confirmed in the Entra admin center.
Step 2: Tag the storage app for cloud group SIDs
This step is mandatory for cloud-only identities, not an optimization. Without the tag, tickets issued for cloud-only users do not carry cloud group SIDs, and Microsoft is explicit that authentication for those identities fails as a result. Treat any outcome milder than that, group ACLs evaluating as though the user belonged to nothing, as the lucky version. The fix is a tag on the storage account’s auto-created application manifest. In the Entra admin center, open app registrations, find [Storage Account] stvciofiles01.file.core.windows.net, open its manifest, and add kdc_enable_cloud_group_sids to the tags array. Via Graph:
$app = @(Get-MgApplication -Filter "displayName eq '[Storage Account] stvciofiles01.file.core.windows.net'")
if ($app.Count -ne 1) { throw "Expected exactly one application, found $($app.Count)" }
Update-MgApplication -ApplicationId $app[0].Id `
-Tags ($app[0].Tags + "kdc_enable_cloud_group_sids")
The count check is not defensive padding. A tenant with several storage accounts using Entra Kerberos holds several near-identically named applications, and a filter that returns two of them will happily write the tag onto the wrong one, leaving you debugging the account you did not touch. Failure mode: on a cloud-only tenant, skipping this breaks authentication outright; in the milder variants people report, share-level access passes and every group-based deny or grant in the ACL tree silently misbehaves. If group permissions “don’t apply,” check this tag before touching a single ACL. Also note the ceiling: a ticket carries at most 1,010 group SIDs; users in sprawling nested-group estates can fail ticket issuance entirely (error 140011), and the fix is group hygiene, not a setting.
Step 3: Replace the default share permission with RBAC on groups
The starter build used a tenant-wide default share permission. Production uses named groups against the three SMB data roles, and the default goes back to None.
| Setting | Value | Why |
|---|---|---|
| Role for AZF-Pilot-RW | Storage File Data SMB Share Contributor | Read, write, delete through the gate; ACLs still refine beneath it. |
| Role for AZF-Pilot-RO | Storage File Data SMB Share Reader | Read-only through the gate regardless of any generous ACL beneath. |
| Role for AZF-Pilot-Admins | Storage File Data SMB Share Elevated Contributor | Permits applying permissions over SMB. With a Modify ACL that reaches the holder’s own files and folders; with Full Control it reaches anyone’s. |
| Role for AZF-Pilot-Admins, second assignment | Storage File Data SMB Admin | Grants share-level access plus takeOwnership, so an administrator can take ownership of a file whose ACL locks them out and then repair it, without ever mounting with the storage account key. |
| Default share-level permission | None | The gate should name its groups; “all authenticated users” was pilot scaffolding. |
$scope = "/subscriptions/<sub-id>/resourceGroups/rg-files-pilot/providers/Microsoft.Storage/storageAccounts/stvciofiles01/fileServices/default/fileshares/pilot"
New-AzRoleAssignment -ObjectId (Get-MgGroup -Filter "displayName eq 'AZF-Pilot-RW'").Id `
-RoleDefinitionName "Storage File Data SMB Share Contributor" -Scope $scope
New-AzRoleAssignment -ObjectId (Get-MgGroup -Filter "displayName eq 'AZF-Pilot-RO'").Id `
-RoleDefinitionName "Storage File Data SMB Share Reader" -Scope $scope
New-AzRoleAssignment -ObjectId (Get-MgGroup -Filter "displayName eq 'AZF-Pilot-Admins'").Id `
-RoleDefinitionName "Storage File Data SMB Share Elevated Contributor" -Scope $scope
New-AzRoleAssignment -ObjectId (Get-MgGroup -Filter "displayName eq 'AZF-Pilot-Admins'").Id `
-RoleDefinitionName "Storage File Data SMB Admin" -Scope $scope
Set-AzStorageAccount -ResourceGroupName "rg-files-pilot" `
-StorageAccountName "stvciofiles01" -DefaultSharePermission None
The regional constraint that decided the starter build’s region and media tier applies here, and this is the step it applies to. Assigning roles to specific cloud-only users and groups is limited to a published subset of Azure public cloud regions, roughly half of which are qualified SSD and premium only, and read that qualifier carefully because a region can be on the list and still refuse this on HDD. Check the current list against your own region and media tier rather than trusting any article, including this one. Where the combination is not supported, the pattern is the default share-level permission as the gate with ACLs doing all refinement, and the rest of this sheet still applies from Step 4 onward. Two things in Microsoft’s own documentation will trip you if you go and read it around this step, and both are documentation faults rather than product faults. The billing page calls the provisioned v2 model generally available in every Azure public cloud region and its own availability section says most of them; neither sentence is the list that governs this step, which lives on the Entra Kerberos enablement page. And the portal walk-through for assigning share-level permissions still carries a requirement that the identity be hybrid, left from before cloud-only support shipped, which the same documentation set contradicts elsewhere in its own tool matrix. A reader who meets that sentence first concludes this whole approach is unsupported. It is supported, it went generally available in May 2026, and the sentence is stale. Expected result either way: within 30 minutes, the RW user mounts and writes, the RO user mounts and cannot write.
Step 4: The Conditional Access exclusion, done deliberately
If any Conditional Access policy requiring MFA applies to all cloud apps, mounts will fail with system error 1327 until the storage account’s application is excluded. Do the exclusion as governance, not as a quiet fix: in each MFA policy’s target apps, exclude [Storage Account] stvciofiles01.file.core.windows.net, and record in the policy’s description why the exclusion exists (SMB Kerberos cannot satisfy MFA; enforcement for file access lives on the network path). If your CA estate follows a framework with a standing exclusion group pattern, add the storage app there instead, one place, documented. The compensating control is the subject of the next two articles; an exclusion without a compensating control is a finding, and you should treat it as one.
Step 5: Build the ACL tree
Now the real permission model. Worked example, the one I use because it is the shape of almost every departmental share: the share holds \Common (everyone reads and writes), \Finance (RW group writes, RO group has nothing), and \Readonly-Published (everyone reads, only admins write).
With cloud-only identities you do not use File Explorer’s security tab or icacls; they cannot resolve cloud principals. The portal path is the share’s file browser: select a directory, choose Manage access, and add allow entries per group. For anything beyond a handful of entries, use the RestSetAcls PowerShell module, which speaks REST and needs no line of sight to anything:
Install-Module RestSetAcls -Scope CurrentUser
# The OAuth context is only usable with -EnableFileBackupRequestIntent. Without
# it the object constructs cleanly and every later file operation through it
# fails, which is a confusing way to lose an afternoon.
$ctx = New-AzStorageContext -StorageAccountName "stvciofiles01" `
-UseConnectedAccount -EnableFileBackupRequestIntent
# Inheritance is requested, not implied. ObjectInherit reaches files,
# ContainerInherit reaches subdirectories. Omit them and the entry
# applies to the named directory alone and nothing created inside it.
$inherit = "ObjectInherit","ContainerInherit"
# \Common : RW group modify, RO group read
Add-AzFileAce -Context $ctx -FileShareName pilot -FilePath "/Common" -Type Allow `
-Principal "AZF-Pilot-RW" -AccessRights Modify,Synchronize -InheritanceFlags $inherit
Add-AzFileAce -Context $ctx -FileShareName pilot -FilePath "/Common" -Type Allow `
-Principal "AZF-Pilot-RO" -AccessRights Read,Synchronize -InheritanceFlags $inherit
# \Finance : RW only
Add-AzFileAce -Context $ctx -FileShareName pilot -FilePath "/Finance" -Type Allow `
-Principal "AZF-Pilot-RW" -AccessRights Modify,Synchronize -InheritanceFlags $inherit
# \Readonly-Published : everyone read, admins write
Add-AzFileAce -Context $ctx -FileShareName pilot -FilePath "/Readonly-Published" -Type Allow `
-Principal "AZF-Pilot-RO" -AccessRights Read,Synchronize -InheritanceFlags $inherit
Add-AzFileAce -Context $ctx -FileShareName pilot -FilePath "/Readonly-Published" -Type Allow `
-Principal "AZF-Pilot-RW" -AccessRights Read,Synchronize -InheritanceFlags $inherit
Add-AzFileAce -Context $ctx -FileShareName pilot -FilePath "/Readonly-Published" -Type Allow `
-Principal "AZF-Pilot-Admins" -AccessRights Modify,Synchronize -InheritanceFlags $inherit
One thing to know before you read the result back. A share root arrives with an ACL of its own, and the entries on it inherit downward. Microsoft documents the root as carrying BUILTIN\Administrators, BUILTIN\Users, NT AUTHORITY\SYSTEM, CREATOR OWNER, and the one that matters here, NT AUTHORITY\Authenticated Users with Modify applied to this folder, subfolders and files. Add-AzFileAce adds an access control entry; it does not remove what is already there. So every directory you create under the root starts with that inherited entry in place, and any identity that clears the RBAC gate carries it in. Read the effective permissions on each directory in the portal after you build the tree rather than assuming the six entries above are the whole picture, and if the inherited entry is wider than you want, narrow it deliberately rather than leaving it to be discovered later.
That instruction has a limit, and the shape of your folder tree depends on knowing it. Microsoft documents how to add an entry and how to edit one, through the portal’s Manage access pane and through Add-AzFileAce. It documents no method for removing the root’s Authenticated Users entry on the cloud-only path, and the two tools that would strip an inherited ACE on a file server, icacls and File Explorer’s security tab, are both marked unsupported for cloud-only identities in the same tool matrix that sends you to the portal and RestSetAcls. Editing the entry down from Modify to Read does not make a directory private either, because Read is what a Share Reader assignment resolves to anyway, so the read-only user still sees \Finance. So a departmental share here does not behave like a share on a file server. Every directory under the root starts readable by anyone who clears the RBAC gate, and you design the tree around that rather than finding it out when somebody tests it. Step 6 validates the share in the state this sheet builds, with the inherited entry sitting where it arrived.
Two rules carried over from every file server you have ever run, both still true here: set the root and top-level ACLs before data lands in bulk, because propagation across a large tree is slow and disruptive; and never ACL to individual users, because the group is the unit your future self can audit. One rule that is new: an ACL entry for a principal that Entra Kerberos cannot resolve (an old on-prem SID, an unsynced account) is accepted and stored but never enforced, silently. If you migrated data in with old ACLs, assume dead ACEs exist and rebuild deliberately; the migration articles handle this at scale.
Step 6: Validate like an auditor
The end-to-end test, run as each test user on the Entra-joined device, no cached sessions (sign out and in between users, or use separate devices):
net use Z: \\stvciofiles01.file.core.windows.net\pilot
klist get cifs/stvciofiles01.file.core.windows.net
# RW user, expected: all three succeed, the third one included. The explicit
# Read entry on Readonly-Published caps nothing: the root's inherited
# Authenticated Users Modify is cumulative with it, and Share Contributor
# resolves that combination to read, write and delete.
echo test > Z:\Common\rw-test.txt
echo test > Z:\Finance\rw-test.txt
echo test > Z:\Readonly-Published\rw-test.txt
# RO user, expected: read succeeds, the write to Common FAILS (access denied),
# and dir Z:\Finance SUCCEEDS. Finance is readable. The root's inherited
# Authenticated Users entry carries Modify into every directory beneath it,
# and Share Reader caps that at Read. Step 5 explains why it stays there.
type Z:\Common\rw-test.txt
echo test > Z:\Common\ro-test.txt
dir Z:\Finance
Every expected failure is as much a pass condition as every expected success. When something does not match, run Debug-AzStorageAccountAuth from the AzFilesHybrid module before working anything by hand; it performs a set of basic checks against the storage account’s Entra configuration for the signed-in user, and it covers Entra Kerberos and AD DS but not Entra Domain Services. If the RO user can write to Common, the likely culprits in order: the manifest tag (Step 2), RBAC still propagating, a leftover default share permission granting Contributor above your ACLs, or the inherited Authenticated Users entry described in Step 5 still sitting on the directory. Access-based enumeration does not exist on direct mounts, so wherever an ACL genuinely does deny a user, the name still shows in a listing and only the open fails. Managing that expectation belongs to you, not the platform.
Close the loop on the key: with identity working, nobody mounts with the storage account key anymore. Rotate both keys now (storage account, Security + networking, Access keys, rotate each) so any copy that escaped during testing dies, and record the Admins group, not the key, as the operational path for permission changes. The key stays in the break-glass drawer with the same ceremony as a domain recovery credential. From here, the networking articles decide what carries this traffic in production.
Azure Files
‹ Previous: [AF 2] Identity: Who Your File Server Trusts Now
Next: [AF 3] Port 445 and the Internet: The Honest Risk Model ›




