Set-Acl is the PowerShell cmdlet that writes a security descriptor back to a file or folder. On its own it does very little; the work happens on the ACL object you get from Get-Acl, where you add, remove, or replace access rules before handing the result to Set-Acl. Once you know that pattern, every permission change in this cheat sheet is a variation on the same three lines.
This page is the companion to our Get-Acl cheat sheet for viewing folder permissions. Everything here changes NTFS permissions on disk, so read the safety section first if you are working on a production file server.
- Before you change anything
- The Get, modify, Set pattern
- Anatomy of a FileSystemAccessRule
- Grant a user or group access
- Add a Deny entry
- Remove an account from a folder
- Replace an account's rights
- Disable or re-enable inheritance
- Change the folder owner
- Apply a change across a folder tree
- Copy permissions from another folder
- Common Set-Acl errors
- icacls equivalents
- Verify the change and keep an audit trail
Before You Change Anything
Permission changes take effect immediately, there is no undo, and a mistake on a parent folder inherits down to everything beneath it. Three habits keep Set-Acl safe:
- Capture the current state first. Save the SDDL string or an icacls backup so you can restore it (see verification).
- Test on one folder. Run the change against a scratch folder, inspect
$acl.Access, and only then point it at the real path. - Run elevated. Set-Acl writes the owner along with the access rules, which needs an administrator session even when the owner is not changing.
Never drop inherited entries without copying them first. Calling
SetAccessRuleProtection($true, $false) on a folder removes every inherited
permission, including the Administrators and SYSTEM entries that let you fix it afterwards.
The Get, Modify, Set Pattern
Every change follows the same shape. Read the descriptor, change the in-memory object, write it back:
$path = "C:\Data\Finance"
# 1. read the current security descriptor
$acl = Get-Acl -Path $path
# 2. build a rule and add it to the descriptor (nothing has changed on disk yet)
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\Finance-RW", "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
$acl.AddAccessRule($rule)
# 3. write the descriptor back
Set-Acl -Path $path -AclObject $acl
Between steps two and three you can print $acl.Access to confirm the rule
looks right. If it does not, discard the variable and start again; the folder is untouched.
Anatomy of a FileSystemAccessRule
The five constructor arguments map directly onto the fields you see in the Windows Security dialog:
| Argument | Meaning | Common values |
|---|---|---|
| Identity | The user or group, as DOMAIN\Name, a built-in name, or an NTAccount object | CORP\Finance-RW, BUILTIN\Users, NT AUTHORITY\Authenticated Users |
| Rights | A FileSystemRights value; combine with commas | FullControl, Modify, ReadAndExecute, Read, Write |
| Inheritance flags | What the entry passes to children | ContainerInherit, ObjectInherit (subfolders and files), ContainerInherit (subfolders only), None (this folder only) |
| Propagation flags | How far inheritance reaches | None (all descendants), InheritOnly (children but not this folder), NoPropagateInherit (one level only) |
| Type | Allow or Deny | Allow, Deny |
The Security dialog's "Applies to" dropdown is just a combination of the inheritance and propagation flags:
| Applies to | Inheritance flags | Propagation flags |
|---|---|---|
| This folder, subfolders and files | ContainerInherit, ObjectInherit | None |
| This folder only | None | None |
| This folder and subfolders | ContainerInherit | None |
| This folder and files | ObjectInherit | None |
| Subfolders and files only | ContainerInherit, ObjectInherit | InheritOnly |
| Files only | ObjectInherit | InheritOnly |
Grant a User or Group Access
The most common change: give a group Modify rights that flow to everything beneath the folder.
$path = "C:\Data\Finance"
$acl = Get-Acl -Path $path
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\Finance-RW", "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
$acl.AddAccessRule($rule)
Set-Acl -Path $path -AclObject $acl
Read-only access uses ReadAndExecute; a folder-only entry that should not
inherit uses "None" for the inheritance flags:
# read-only, inherited by subfolders and files
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\Finance-RO", "ReadAndExecute", "ContainerInherit, ObjectInherit", "None", "Allow")
# full control on this folder only
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\FS-Admins", "FullControl", "None", "None", "Allow")
Grant to groups, not people. An entry for an individual account becomes an orphaned SID the day that account is deleted. Put the user in a group and grant the group, as described in our NTFS permissions best practices.
Add a Deny Entry
A Deny rule uses the same constructor with "Deny" as the type. Deny entries
are evaluated before Allow entries, so they win even when the same account is allowed
through another group:
$acl = Get-Acl -Path "C:\Data\Finance\Archive"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\Contractors", "Write, Delete", "ContainerInherit, ObjectInherit", "None", "Deny")
$acl.AddAccessRule($rule)
Set-Acl -Path "C:\Data\Finance\Archive" -AclObject $acl
Use Deny sparingly. Most access problems that end in a Deny entry are better solved by removing the account from the group that allows it. Deny entries are the leading cause of "access denied" tickets that nobody can explain six months later.
Remove an Account from a Folder
PurgeAccessRules removes every explicit entry for an identity in one call.
It takes an NTAccount object rather than a string:
$path = "C:\Data\Finance"
$acl = Get-Acl -Path $path
$who = [System.Security.AccessControl.NTAccount]"CORP\jsmith"
$acl.PurgeAccessRules($who)
Set-Acl -Path $path -AclObject $acl
To remove one specific rule rather than everything for the account, find it in the
Access list and pass it to RemoveAccessRule:
$acl = Get-Acl -Path $path
$rule = $acl.Access | Where-Object {
$_.IdentityReference.Value -eq "CORP\jsmith" -and
$_.AccessControlType -eq "Deny" -and
-not $_.IsInherited
}
$rule | ForEach-Object { [void]$acl.RemoveAccessRule($_) }
Set-Acl -Path $path -AclObject $acl
Inherited entries cannot be removed from the child folder. Either remove them from the parent where they are defined, or disable inheritance on the child and then remove the copied entry.
Replace an Account's Rights
SetAccessRule replaces all existing explicit entries for the identity with
the rule you supply, which is cleaner than removing and re-adding:
# downgrade a group from Modify to read-only in one step
$acl = Get-Acl -Path "C:\Data\Finance"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\Finance-RW", "ReadAndExecute", "ContainerInherit, ObjectInherit", "None", "Allow")
$acl.SetAccessRule($rule)
Set-Acl -Path "C:\Data\Finance" -AclObject $acl
Disable or Re-enable Inheritance
SetAccessRuleProtection controls whether a folder inherits from its parent.
The second argument decides what happens to the entries that were being inherited:
$true converts them to explicit entries on this folder, $false
discards them.
# disable inheritance and keep the inherited entries as explicit copies (safe)
$acl = Get-Acl -Path "C:\Data\Payroll"
$acl.SetAccessRuleProtection($true, $true)
Set-Acl -Path "C:\Data\Payroll" -AclObject $acl
# re-enable inheritance (the second argument is ignored when enabling)
$acl = Get-Acl -Path "C:\Data\Payroll"
$acl.SetAccessRuleProtection($false, $false)
Set-Acl -Path "C:\Data\Payroll" -AclObject $acl
Re-enabling inheritance does not remove the explicit copies that were created when it was disabled, so a folder can end up with the same entry twice. Purge the duplicates afterwards, or use the reset approach under applying changes across a tree. Our broken inheritance guide covers how to find every folder where inheritance has been disabled and decide which breaks are legitimate.
Change the Folder Owner
The owner is set on the descriptor with SetOwner. Setting a group such as
Administrators as owner is the usual fix for folders left behind by departed users:
$path = "C:\Data\Finance\OldProjects"
$acl = Get-Acl -Path $path
$owner = [System.Security.AccessControl.NTAccount]"BUILTIN\Administrators"
$acl.SetOwner($owner)
Set-Acl -Path $path -AclObject $acl
Set-Acl can only assign ownership to yourself or to a group you belong to, and it needs
the restore privilege that comes with an elevated session. If you cannot even read the
folder, take ownership first with takeown and then fix the permissions:
takeown /F "C:\Data\Finance\OldProjects" /A /R /D Y
icacls "C:\Data\Finance\OldProjects" /grant "BUILTIN\Administrators:(OI)(CI)F" /T
Apply a Change Across a Folder Tree
Because NTFS inheritance already propagates entries to children, you rarely need to loop.
Add the rule with ContainerInherit, ObjectInherit at the top of the tree and
every subfolder that inherits will pick it up. Looping is only necessary when some
subfolders have inheritance disabled and you want the entry there too:
$root = "C:\Data\Finance"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\FS-Admins", "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
# apply at the root, then only to subfolders that do not inherit
$targets = @(Get-Item -LiteralPath $root) +
(Get-ChildItem -Path $root -Directory -Recurse -ErrorAction SilentlyContinue |
Where-Object { (Get-Acl -LiteralPath $_.FullName).AreAccessRulesProtected })
foreach ($folder in $targets) {
$acl = Get-Acl -LiteralPath $folder.FullName
$acl.AddAccessRule($rule)
Set-Acl -LiteralPath $folder.FullName -AclObject $acl
}
To reset a tree so that every subfolder simply inherits from the root again, icacls is faster and safer than a PowerShell loop:
# re-enable inheritance everywhere below the root and remove explicit entries
icacls "C:\Data\Finance" /reset /T /C
Preview the blast radius first. Before resetting or looping over a tree, get a list of every folder with explicit entries or disabled inheritance so you know what the change will touch. The Get-Acl cheat sheet has the one-liner, and Permissions Reporter's filter presets do it across an entire server.
Copy Permissions from Another Folder
Piping Get-Acl into Set-Acl copies one folder's descriptor to another, which is a quick way to stamp a template ACL onto new project folders:
Get-Acl -Path "C:\Templates\ProjectFolder" | Set-Acl -Path "C:\Data\Projects\NewClient"
This copies the owner as well, which fails unless you are allowed to assign that owner. To copy only the access rules, transfer them through SDDL and strip the owner and group sections:
$source = Get-Acl -Path "C:\Templates\ProjectFolder"
$target = Get-Acl -Path "C:\Data\Projects\NewClient"
# keep only the DACL portion of the source descriptor
$dacl = $source.Sddl.Substring($source.Sddl.IndexOf("D:"))
$target.SetSecurityDescriptorSddlForm($dacl)
Set-Acl -Path "C:\Data\Projects\NewClient" -AclObject $target
Common Set-Acl Errors
| Error | Cause | Fix |
|---|---|---|
The security identifier is not allowed to be the owner of this object |
The descriptor names an owner you cannot assign (typically the previous owner), and Set-Acl tries to write it back | Run elevated, or build a new DirectorySecurity object that contains only your access rules |
Attempted to perform an unauthorized operation |
The session lacks Write DAC on the folder, or is not elevated | Run PowerShell as administrator; take ownership if the ACL locks out Administrators |
Some or all identity references could not be translated |
The account name is misspelled, the domain is unreachable, or the account was deleted | Check the name with [System.Security.Principal.NTAccount]"CORP\name" and translate it to a SID before building the rule |
Cannot find path ... because it does not exist |
Wildcard characters in the path, or a path longer than 260 characters | Use -LiteralPath; prefix long paths with \\?\ |
| Change appears to succeed but children are unaffected | The rule was created with None inheritance flags, or the children have inheritance disabled |
Rebuild the rule with ContainerInherit, ObjectInherit; find protected subfolders and fix them individually |
To sidestep the owner problem entirely, create an empty descriptor, add only the rules you want to change, and let Set-Acl merge it:
$acl = New-Object System.Security.AccessControl.DirectorySecurity
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"CORP\Finance-RW", "Modify", "ContainerInherit, ObjectInherit", "None", "Allow")
$acl.AddAccessRule($rule)
$acl.SetAccessRuleProtection($false, $true) # keep inheriting from the parent
Set-Acl -Path "C:\Data\Finance" -AclObject $acl
Careful: a fresh DirectorySecurity object has an empty DACL.
Set-Acl writes the whole DACL, so without SetAccessRuleProtection($false, $true)
the folder would end up with your one rule and nothing else.
icacls Equivalents
icacls performs the same changes with one line each and no object model to learn. It is
the better choice inside batch files, and its /save and /restore
pair makes an easy backup.
| Task | icacls |
|---|---|
| Grant Modify to a group, inherited | icacls "C:\Data" /grant "CORP\Finance-RW:(OI)(CI)M" |
| Replace an account's rights | icacls "C:\Data" /grant:r "CORP\Finance-RW:(OI)(CI)RX" |
| Add a Deny entry | icacls "C:\Data" /deny "CORP\Contractors:(OI)(CI)W" |
| Remove an account | icacls "C:\Data" /remove "CORP\jsmith" |
| Disable inheritance, keep copies | icacls "C:\Data" /inheritance:d |
| Disable inheritance, drop entries | icacls "C:\Data" /inheritance:r |
| Re-enable inheritance | icacls "C:\Data" /inheritance:e |
| Change owner | icacls "C:\Data" /setowner "BUILTIN\Administrators" /T |
| Reset a tree to inherited defaults | icacls "C:\Data" /reset /T /C |
| Back up and restore ACLs | icacls "C:\Data" /save acls.txt /T then icacls "C:\" /restore acls.txt |
Rights are abbreviated as F full control, M modify,
RX read and execute, R read, and W write.
(OI) and (CI) are object and container inheritance, matching the
ObjectInherit and ContainerInherit flags above. Add /T
to apply the change to every existing subfolder and file rather than relying on inheritance.
Verify the Change and Keep an Audit Trail
Always confirm the result. For one folder, re-run Get-Acl or icacls:
(Get-Acl -Path "C:\Data\Finance").Access |
Format-Table IdentityReference, FileSystemRights, AccessControlType, IsInherited -AutoSize
For rollback, capture the descriptor before the change and restore it if needed:
# before
$saved = (Get-Acl -Path "C:\Data\Finance").Sddl
# rollback
$acl = Get-Acl -Path "C:\Data\Finance"
$acl.SetSecurityDescriptorSddlForm($saved)
Set-Acl -Path "C:\Data\Finance" -AclObject $acl
On a file server, the change you made is rarely the only one that matters. Inheritance carries it to thousands of subfolders, some of which had their own explicit entries or disabled inheritance, and the net effect on any given user depends on nested group membership that Get-Acl cannot see. That is where a before-and-after report earns its keep:
- Run a Permissions Reporter scan of the server before the change and export it to XML.
- Make the change with Set-Acl or icacls.
- Scan again and use report comparison to see exactly which folders and principals gained or lost access, with group membership expanded.
- Keep both exports as the audit record, or schedule the scan so every future change is captured automatically.
Permissions Reporter does not modify permissions itself; it shows you what to change and proves what changed. The Basic edition is free.
Related Resources
- View Folder Permissions with PowerShell (Get-Acl Cheat Sheet) - The companion page for reading and exporting ACLs
- NTFS Permissions Overview - How allow, deny, and inheritance work
- NTFS Permissions Best Practices - Designing ACLs that stay manageable
- Broken Inheritance - Finding folders that stopped inheriting
- Everyone Full Control - Finding and fixing over-exposed folders
- Orphaned SIDs - Cleaning up permissions for deleted accounts
- NTFS Permissions Troubleshooting FAQ - Common problems and fixes