cheat sheet
icacls
Display and modify NTFS access control lists on files and directories — grant, deny, or remove permissions for users and groups, manage inheritance, and save or restore full ACL sets.
icacls — ACL Editor
What it is
icacls (Integrity Control Access Control Lists) is the built-in Windows command for reading and modifying NTFS discretionary access control lists (DACLs) and system ACLs (SACLs). It replaces the older cacls and xcacls commands and supports inheritance flags, object-level integrity levels, saving/restoring ACL sets across machines, and granular permission masks. Use it in deployment scripts to lock down folder trees, grant specific users access, or audit permissions across a directory hierarchy. The PowerShell equivalent is Get-Acl / Set-Acl.
Availability
icacls ships as C:\Windows\System32\icacls.exe on Windows Vista and later. Modifying ACLs on system-protected paths requires Administrator privileges.
icacls /?
Output:
ICACLS name /save aclfile [/T] [/C] [/L] [/Q]
ICACLS directory [/substitute SidOld SidNew [...]] /restore aclfile [/C] [/L] [/Q]
ICACLS name /setowner user [/T] [/C] [/L] [/Q]
ICACLS name /findsid Sid [/T] [/C] [/L] [/Q]
ICACLS name /verify [/T] [/C] [/L] [/Q]
ICACLS name /reset [/T] [/C] [/L] [/Q]
ICACLS name [/grant[:r] Sid:perm [...]] [/deny Sid:perm [...]] [/remove[:g|:d]] Sid [...]] [/T] [/C] [/L] [/Q] [/setintegritylevel Level...]
Syntax
icacls <file/dir> [/grant[:r] user:perm] [/deny user:perm] [/remove user] [/T] [/C] [/Q]
icacls <dir> /save <file> [/T]
icacls <dir> /restore <file>
icacls <file/dir> /reset [/T]
icacls <file/dir> /setowner <user> [/T]
Output: (ACL display or operation result)
Essential options
| Switch | Meaning |
|---|---|
| (just path) | Display the ACL of the file or directory |
/grant user:perm | Add a permission grant (additive) |
/grant:r user:perm | Replace existing grants with the new set |
/deny user:perm | Add an explicit deny entry |
/remove user | Remove all ACEs (grants and denies) for a user |
/remove:g user | Remove only grant ACEs |
/remove:d user | Remove only deny ACEs |
/T | Apply recursively to all files and subdirectories |
/C | Continue on error instead of stopping |
/Q | Suppress success messages |
/reset [/T] | Reset to inherited permissions only (remove explicit ACEs) |
/setowner user [/T] | Change the owner |
/save file [/T] | Save ACLs to a file for later /restore |
/restore file | Restore ACLs from a saved file |
/inheritance:e|d|r | e=enable, d=disable, r=remove inherited ACEs (Microsoft Learn now documents the long form /inheritancelevel: with identical semantics; both spellings are accepted by the current binary) |
Permission masks
Permissions are specified as a string of letters or a hex mask. The most common simple rights:
| Mask | Meaning |
|---|---|
F | Full control |
M | Modify (read + write + delete, no ACL change) |
RX | Read & execute |
R | Read-only |
W | Write |
D | Delete |
(OI) | Object inherit — applies to files inside this directory |
(CI) | Container inherit — applies to subdirectories |
(IO) | Inherit only — does not apply to this object itself |
(NP) | No propagation — do not propagate to nested objects |
Combine inheritance flags with rights: (OI)(CI)F means full control, inherited by all child files and folders.
Viewing ACLs
Running icacls with just a path prints the current ACL. Each line shows the principal, inheritance flags, and the permission mask.
icacls C:\Data\Reports
Output:
C:\Data\Reports MYHOST\alicedev:(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)
BUILTIN\Users:(OI)(CI)(RX)
Successfully processed 1 files; Failed processing 0 files
Granting permissions
/grant user:perm adds a permission ACE. Without :r the grant is additive — existing ACEs remain. With :r it replaces all existing grant ACEs for that user.
icacls C:\Data\Reports /grant alicedev:(OI)(CI)M
Output:
processed file: C:\Data\Reports
Successfully processed 1 files; Failed processing 0 files
rem Replace (not add to) alicedev's grants
icacls C:\Data\Reports /grant:r alicedev:(OI)(CI)RX
Output:
processed file: C:\Data\Reports
Successfully processed 1 files; Failed processing 0 files
Denying access
An explicit Deny ACE overrides any Allow ACE for the same right, even if the user is also in a group that has Allow. Use deny sparingly — it can create confusing effective-permission combinations.
icacls C:\Sensitive /deny "BUILTIN\Users":RX
Output:
processed file: C:\Sensitive
Successfully processed 1 files; Failed processing 0 files
Applying permissions recursively
/T applies the ACL change to all files and subdirectories under the path. Combine with /C to continue past files where access is denied (useful for system-protected paths).
icacls C:\AppData\Logs /grant alicedev:(OI)(CI)M /T /C /Q
Output:
Successfully processed 42 files; Failed processing 0 files
Removing permissions
/remove user strips all explicit ACEs (grants and denies) for the specified user from the object.
icacls C:\Data\Reports /remove alicedev
Output:
processed file: C:\Data\Reports
Successfully processed 1 files; Failed processing 0 files
Resetting to inherited permissions
/reset removes all explicit ACEs from the object, leaving only inherited permissions. Use /T to propagate the reset down a directory tree — useful after a bulk permission change that went wrong.
icacls C:\Data\Reports /reset /T /C /Q
Output:
Successfully processed 42 files; Failed processing 0 files
Saving and restoring ACLs
/save writes a binary ACL snapshot to a file; /restore reapplies it. Use this pair to back up ACLs before a migration and restore them afterward.
icacls C:\Data /save C:\Backup\data_acls.bin /T
Output:
Successfully processed 42 files; Failed processing 0 files
rem Restore ACLs to the same tree
icacls C:\Data /restore C:\Backup\data_acls.bin
Output:
Successfully processed 42 files; Failed processing 0 files
Common pitfalls
- User principal names are case-sensitive on some systems — prefer
DOMAIN\usernameorBUILTIN\Administratorsto avoid silent no-ops. /grantwithout:ris additive — running it twice adds a second ACE; use/grant:rwhen you want a clean replace.- Deny takes precedence over Allow — a deny ACE in a parent's ACL propagated by inheritance can block access for a user even though you granted them access directly; check effective permissions in Explorer → Security → Advanced.
/T /Cis needed for directories with protected sub-paths — without/C, the first access-denied path terminates the command, leaving the rest unprocessed./savestores SIDs, not account names — if the file is moved to a different domain,/restoremay not resolve old SIDs; use/substitute OldSid NewSidduring restore to remap them.
Real-world recipes
Lock a folder to a specific user only
icacls C:\Private\alicedev /inheritance:d
icacls C:\Private\alicedev /remove "BUILTIN\Users"
icacls C:\Private\alicedev /remove "NT AUTHORITY\Authenticated Users"
icacls C:\Private\alicedev /grant alicedev:(OI)(CI)F
Output:
processed file: C:\Private\alicedev
Successfully processed 1 files; Failed processing 0 files
Grant read access to a web app account on a deployment folder
icacls C:\inetpub\wwwroot\myapp /grant "IIS APPPOOL\DefaultAppPool":(OI)(CI)RX /T /C /Q
Output:
Successfully processed 156 files; Failed processing 0 files
Audit who has explicit permissions on a directory tree
icacls C:\Sensitive /T /C 2>NUL | findstr /V "Successfully\|Failed"
Output:
C:\Sensitive MYHOST\alicedev:(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)
C:\Sensitive\config.json MYHOST\alicedev:(F)
...
Full permission specifier reference
The icacls permission language is richer than the simple F/M/RX/R/W/D letters shown above. Each ACE can combine a simple right (F, M, etc.), one or more specific rights (each a single letter or short code), and inheritance flags (in parentheses). The simple rights are aliases for combinations of specific rights — useful for common cases but limited.
Simple rights
| Code | Name | Includes |
|---|---|---|
F | Full control | All specific rights including WO, WD, WA |
M | Modify | RX + W + D (cannot change ACL or take ownership) |
RX | Read & execute | R + X |
R | Read | RA + REA + RC |
W | Write | WA + WEA + AD + WD (but not delete) |
D | Delete | Single-right delete |
Specific (granular) rights
Specific rights map 1:1 to the underlying ACCESS_MASK bits and let you express ACLs that simple rights can't. Combine them with ; — for example (RD,X,RA,REA,RC,S,RC):
| Code | Right | Bit name |
|---|---|---|
RD | Read data / List directory | FILE_READ_DATA |
WD | Write data / Add file | FILE_WRITE_DATA |
AD | Append data / Add subdirectory | FILE_APPEND_DATA |
REA | Read extended attributes | FILE_READ_EA |
WEA | Write extended attributes | FILE_WRITE_EA |
X | Execute / Traverse | FILE_EXECUTE / FILE_TRAVERSE |
DC | Delete child | FILE_DELETE_CHILD |
RA | Read attributes | FILE_READ_ATTRIBUTES |
WA | Write attributes | FILE_WRITE_ATTRIBUTES |
DE | Delete | DELETE |
RC | Read control (DACL) | READ_CONTROL |
WDAC | Write DAC | WRITE_DAC |
WO | Write owner | WRITE_OWNER |
S | Synchronize | SYNCHRONIZE |
GA | Generic All | GENERIC_ALL |
GR | Generic Read | GENERIC_READ |
GE | Generic Execute | GENERIC_EXECUTE |
GW | Generic Write | GENERIC_WRITE |
Example — grant a service account "append-only writes for logging" without read or delete:
icacls C:\Logs /grant "MYHOST\svclog":"(OI)(CI)(AD,WD,RA,REA,RC,S)"
Output:
processed file: C:\Logs
Successfully processed 1 files; Failed processing 0 files
Inheritance flags
Inheritance flags control whether and how an ACE propagates from a container (directory) to its children (files and subdirs). They appear in parentheses before the rights letters.
| Flag | Meaning |
|---|---|
(OI) | Object Inherit — applies to files in this folder |
(CI) | Container Inherit — applies to subdirectories |
(IO) | Inherit Only — does NOT apply to this object itself, only to children |
(NP) | No Propagate — children inherit, grandchildren do not |
(I) | Inherited — ACE inherited from the parent container; set automatically on child objects |
N | No access — appears in the simple-rights list alongside F/M/RX/R/W/D (rarely useful by itself since the absence of an ACE already denies access) |
Common combinations:
| Combination | Result |
|---|---|
(OI)(CI) | Applies to this folder, all files, and all subfolders recursively |
(OI) alone | Applies to this folder and all files inside (but not subfolders) |
(CI) alone | Applies to this folder and subfolders (but not files inside) |
(OI)(CI)(IO) | Applies only to children (not to the folder itself) |
(OI)(CI)(NP) | Applies to direct children only, not grandchildren |
Numeric/hex permission masks
For exotic or programmatic uses, icacls also accepts raw hex masks. (0x1F01FF) is equivalent to F (FILE_ALL_ACCESS = 0x001F01FF). The Get-Acl PowerShell cmdlet uses these constants by name.
icacls C:\Data /grant alicedev:"(0x1F01FF)"
Output:
processed file: C:\Data
Successfully processed 1 files; Failed processing 0 files
Inheritance management — /inheritance
/inheritance controls whether a child object continues to inherit ACEs from its parent. By default, every new file inherits its parent's ACEs (marked (I) in icacls output). Disabling inheritance is the first step in locking down a folder to a specific set of principals.
Disable inheritance and convert inherited ACEs
icacls C:\Private /inheritance:d
Output:
processed file: C:\Private
Successfully processed 1 files; Failed processing 0 files
/inheritance:d disables inheritance but preserves the previously inherited ACEs as explicit entries. This is usually what you want — locking down a folder without immediately stripping all permissions. Inspect the result:
icacls C:\Private
Output:
C:\Private NT AUTHORITY\SYSTEM:(OI)(CI)(F)
BUILTIN\Administrators:(OI)(CI)(F)
BUILTIN\Users:(OI)(CI)(RX)
MYHOST\alicedev:(OI)(CI)(F)
Note the absence of (I) flags — every entry is now explicit.
Disable and remove inherited ACEs
icacls C:\Private /inheritance:r
Output:
processed file: C:\Private
Successfully processed 1 files; Failed processing 0 files
/inheritance:r removes all inherited ACEs without converting them. The folder is left with only its explicit (non-inherited) ACEs. Useful when you want a clean slate before granting just one principal.
Re-enable inheritance
icacls C:\Private /inheritance:e
Output:
processed file: C:\Private
Successfully processed 1 files; Failed processing 0 files
/inheritance:e re-enables inheritance. Inherited ACEs from the parent flow back in; existing explicit ACEs remain.
Integrity levels and mandatory access control
Windows Vista introduced Mandatory Integrity Control (MIC) — a layer above the DACL that classifies objects with an integrity level (Low, Medium, High, System). Objects can be marked "No Write Up", meaning processes at a lower integrity level cannot modify them even with a permissive DACL. Browsers (Chrome, Edge sandbox tabs) and PDF readers use Low integrity to contain exploits.
rem View an object's integrity level
icacls C:\Sandbox\untrusted.exe
Output:
C:\Sandbox\untrusted.exe BUILTIN\Users:(I)(RX)
Mandatory Label\Low Mandatory Level:(NW)
rem Set Low integrity level
icacls C:\Sandbox\untrusted.exe /setintegritylevel L
Output:
processed file: C:\Sandbox\untrusted.exe
Successfully processed 1 files; Failed processing 0 files
| Level | Code | Typical use |
|---|---|---|
| Low | L | Browser sandbox, attachment downloads |
| Medium | M | Standard user processes (default) |
| High | H | Elevated/admin processes |
| System | S | Kernel and core OS services |
The integrity level label appears as Mandatory Label\<Level> Mandatory Level:(NW) in icacls output. The (NW) policy ("No Write Up") is the default; (NR) is "No Read Up" and (NX) is "No Execute Up".
PowerShell equivalents — Get-Acl and Set-Acl
PowerShell offers a fully structured ACL API via Get-Acl and Set-Acl. The objects returned are System.Security.AccessControl.FileSecurity (or DirectorySecurity) — the same .NET types used by application code. Use them when you need to script complex ACL transformations, copy ACLs between objects, or programmatically test effective permissions.
Inspect an ACL
Get-Acl C:\Data\Reports | Format-List
Output:
Path : Microsoft.PowerShell.Core\FileSystem::C:\Data\Reports
Owner : MYHOST\alicedev
Group : MYHOST\None
Access : {MYHOST\alicedev Allow FullControl,
NT AUTHORITY\SYSTEM Allow FullControl,
BUILTIN\Administrators Allow FullControl,
BUILTIN\Users Allow ReadAndExecute, Synchronize}
Audit : {}
Sddl : O:S-1-5-21-...G:S-1-5-21-...D:AI(A;OICI;FA;;;S-1-5-21-...)(A;OICIID;FA;;;SY)...
Add an Access Rule
$path = 'C:\Data\Reports'
$acl = Get-Acl $path
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
'MYHOST\alicedev',
'Modify',
'ContainerInherit,ObjectInherit',
'None',
'Allow')
$acl.AddAccessRule($rule)
Set-Acl -Path $path -AclObject $acl
Output: (silent on success)
The FileSystemRights enum values mirror icacls simple rights: FullControl, Modify, ReadAndExecute, Read, Write, Delete, plus granular ones like ReadData, AppendData, ChangePermissions. InheritanceFlags is None | ContainerInherit | ObjectInherit; PropagationFlags is None | NoPropagateInherit | InheritOnly.
Remove an Access Rule
$path = 'C:\Data\Reports'
$acl = Get-Acl $path
$acl.Access | Where-Object IdentityReference -eq 'MYHOST\bobdev' |
ForEach-Object { $acl.RemoveAccessRule($_) | Out-Null }
Set-Acl -Path $path -AclObject $acl
Output: (silent on success)
Copy an ACL from one path to another
$src = Get-Acl C:\Templates\StandardFolder
Set-Acl -Path C:\Data\NewProject -AclObject $src
Output: (silent on success)
This copies owner + DACL + SACL atomically. Equivalent to icacls C:\Templates\StandardFolder /save acl.bin followed by icacls C:\Data\NewProject /restore acl.bin, but in one round trip without a temp file.
SDDL — Security Descriptor Definition Language
Every Windows security descriptor has a textual representation in SDDL, the Security Descriptor Definition Language. icacls accepts SDDL strings via /setsddl (not commonly used) and Get-Acl exposes them via the .Sddl property. SDDL is dense but unambiguous — Microsoft documentation and many security audit tools use it as canonical form.
(Get-Acl C:\Data\Reports).Sddl
Output:
O:S-1-5-21-1004336348-1177238915-682003330-1001G:S-1-5-21-1004336348-1177238915-682003330-513D:AI(A;OICI;FA;;;S-1-5-21-1004336348-1177238915-682003330-1001)(A;OICIID;FA;;;SY)(A;OICIID;FA;;;BA)(A;OICIID;0x1200a9;;;BU)
SDDL structure: O:<ownerSID>G:<groupSID>D:<flags>(<ACE>)(<ACE>)... where each ACE is (<type>;<flags>;<rights>;<objguid>;<inheritobjguid>;<sid>).
O:owner SIDG:primary group SID (legacy POSIX compatibility)D:AIdiscretionary ACL with auto-inherit flag(A;OICI;FA;;;sid)Allow ACE, Object+Container Inherit, File All access, for the given SID
To apply an SDDL string to a registry key or arbitrary object, use [System.Security.AccessControl.RawSecurityDescriptor]::new($sddl).
Saving and restoring ACLs across machines — /substitute
/save writes a binary file with each object's SDDL keyed by relative path. The SIDs are preserved verbatim — so restoring on a different machine works only for built-in SIDs (S-1-5-32-*) and well-known domain SIDs. Local user SIDs differ between machines.
When migrating between domains or to a freshly imaged box, /restore accepts /substitute oldSid newSid to remap.
rem Save ACLs on the source
icacls C:\Data /save C:\Backup\data_acls.bin /T
Output:
Successfully processed 312 files; Failed processing 0 files
rem Look up old and new SIDs (e.g. with PsGetSid or PowerShell)
powershell -c "(New-Object System.Security.Principal.NTAccount('OLDDOMAIN','alicedev')).Translate([System.Security.Principal.SecurityIdentifier]).Value"
Output:
S-1-5-21-1234567890-987654321-111111111-1001
rem Restore on the target, mapping old SID to new
icacls C:\Data /substitute S-1-5-21-1234567890-987654321-111111111-1001 S-1-5-21-9876543210-123456789-222222222-1001 /restore C:\Backup\data_acls.bin
Output:
Successfully processed 312 files; Failed processing 0 files
Multiple /substitute pairs can chain: /substitute oldA newA /substitute oldB newB ....
Deny vs allow precedence
Windows evaluates ACEs in canonical order: explicit deny → explicit allow → inherited deny → inherited allow. As soon as a matching deny is found, access is refused; an allow only succeeds if no preceding deny matches. This is why deny ACEs are dangerous — an inherited deny on a high-level folder can block access to a child even if you explicitly granted access there.
rem Pitfall: this CAN block alicedev despite the second grant
icacls C:\Sensitive /deny "alicedev":(F) /T
icacls C:\Sensitive\subfolder /grant "alicedev":(F)
Output:
processed file: C:\Sensitive
Successfully processed 1 files; Failed processing 0 files
processed file: C:\Sensitive\subfolder
Successfully processed 1 files; Failed processing 0 files
The deny on C:\Sensitive inherits down to C:\Sensitive\subfolder and runs before the grant. To audit effective permissions, use:
# Effective access for a user on a path
$acl = Get-Acl C:\Sensitive\subfolder
$acl.GetAccessRules($true, $true, [System.Security.Principal.NTAccount]) |
Where-Object IdentityReference -like '*alicedev*' |
Format-Table IdentityReference, FileSystemRights, AccessControlType, IsInherited
Output:
IdentityReference FileSystemRights AccessControlType IsInherited
----------------- ---------------- ----------------- -----------
MYHOST\alicedev FullControl Deny True
MYHOST\alicedev FullControl Allow False
The deny is inherited and evaluated first → access denied. Best practice: never use deny in routine ACLs; remove a user from the allowlist instead.
Comparing with chmod/setfacl on Linux
Windows ACLs and Linux POSIX permissions both solve the same problem but with very different models. The Linux side has a much simpler base (owner/group/other with rwx) and grafts ACLs on as an optional extension; Windows is ACL-native from the start. See permissions — chmod, chown, umask, ACLs for the full Linux story.
| Concept | Linux | Windows |
|---|---|---|
| View permissions | ls -l | icacls path |
| Three classes | owner/group/other | None — every principal is an ACE |
| Read | r (4) | R or RD,REA,RA,RC,S |
| Write | w (2) | W or WD,AD,WEA,WA |
| Execute | x (1) | X |
| Modify perms | needs ownership | needs WRITE_DAC (owner has it implicitly) |
| Inheritance | setfacl -d (default ACLs) | (OI)(CI) flags |
| Recursion | -R (or /T in icacls) | /T |
| Deny entries | not in POSIX (ACLs only via mask) | First-class (with precedence over allow) |
| Special bits | setuid, setgid, sticky | No equivalent (capabilities instead) |
| Sandbox enforcement | namespaces, AppArmor, SELinux | Mandatory Integrity Control (MIC) |
| ACL backup | getfacl -R / setfacl --restore | icacls /save and /restore |
Common mappings between chmod modes and icacls ACEs:
# Linux: 755 — owner full, others read+execute
chmod 755 file
Output: (none — exits 0 on success)
rem Windows equivalent (approximate; assumes owner=alicedev, group=Users)
icacls file /inheritance:r /grant alicedev:F /grant "BUILTIN\Users":RX
Output:
processed file: file
Successfully processed 1 files; Failed processing 0 files
Audit logging — Event 4670 and 4663
ACL changes generate Event 4670 ("Permissions on an object were changed") in the Security log when SACL auditing is enabled on the path. Event 4663 records the underlying object-access attempt. Both are essential for forensic investigations and compliance audits.
rem Enable file system audit policy globally
auditpol /set /subcategory:"File System" /success:enable /failure:enable
Output:
The command was successfully executed.
Adding a SACL entry to a path tells Windows which operations to audit on that object:
icacls C:\Sensitive /grant *S-1-1-0:(OI)(CI)(F) /T
icacls C:\Sensitive /setintegritylevel H
icacls C:\Sensitive /T /C /Q /audit Everyone:(OI)(CI)(F)
Output: (none — exits 0 on success)
Note: /audit is not a documented icacls switch on all builds — use PowerShell's $acl.AddAuditRule(...) for portable script-based SACL management:
$path = 'C:\Sensitive'
$acl = Get-Acl $path -Audit
$rule = New-Object System.Security.AccessControl.FileSystemAuditRule(
'Everyone','Modify,Delete,ChangePermissions,TakeOwnership',
'ContainerInherit,ObjectInherit','None','Success,Failure')
$acl.AddAuditRule($rule)
Set-Acl -Path $path -AclObject $acl
Output: (silent on success)
# Query recent permission-change events
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4670; StartTime=(Get-Date).AddHours(-24)} |
Select-Object TimeCreated,
@{Name='Object';Expression={$_.Properties[6].Value}},
@{Name='Subject';Expression={$_.Properties[1].Value}},
@{Name='OriginalSD';Expression={$_.Properties[7].Value}},
@{Name='NewSD';Expression={$_.Properties[8].Value}}
Output:
TimeCreated Object Subject OriginalSD NewSD
----------- ------ ------- ---------- -----
5/25/2026 9:30 AM C:\Data\Reports alicedev D:AI(A;OICI...) D:AI(A;OICI...)(A;OICI...)
Verifying ACLs — /verify
/verify checks every object under a path against its inherited/canonical ACL and reports any anomalies. Useful after a security descriptor migration to catch corruption.
icacls C:\Data /verify /T /C
Output:
Successfully processed 312 files; Failed processing 0 files
If Failed processing is non-zero, the failed paths are listed with the specific anomaly (orphaned SID, out-of-canonical-order ACE, etc.). Pair with /reset /T to normalize.
Finding all objects owned by or granted to a specific SID — /findsid
/findsid locates ACEs referring to a given SID anywhere in a tree — useful for auditing what a departed user can still access before disabling their account.
icacls C:\ /findsid "MYHOST\alicedev" /T /C 2>NUL
Output:
C:\Data\Reports MYHOST\alicedev:(OI)(CI)(F)
C:\Users\alicedev MYHOST\alicedev:(F)
...
Successfully processed 25431 files; Failed processing 0 files
After the user is offboarded, remove their explicit ACEs in bulk:
icacls C:\ /remove "MYHOST\alicedev" /T /C 2>NUL
Output:
Successfully processed 25431 files; Failed processing 0 files
Common pitfalls (extended)
In addition to the basics above, watch out for these in real-world scripts:
- Quoting matters for complex permission strings —
(OI)(CI)Fworks without quotes, but(OI)(CI)(F)requires quoting in cmd because parentheses are batch metacharacters: use"(OI)(CI)(F)"or escape. PowerShell needs different quoting. /Ton huge trees is single-threaded — bulk security-descriptor work on millions of files can take hours. For very large trees, userobocopy /COPY:S /SECFIX /MT:32to apply security descriptors with multi-threading.- Empty
/grantsyntax silently no-ops —icacls file /grant alicedev(no permission) accepts the command and prints success without making any change. Always specify:perm. /setownerrequiresSeRestorePrivilege, notSeTakeOwnership— you can take ownership of files you don't own, but to assign ownership to a different user you needSeRestorePrivilege, which Administrators have by default. From a standard user shell,/setownerfails with "A required privilege is not held by the client".Authenticated Users≠Everyone—Everyoneincludes anonymous and guest sessions;Authenticated Usersdoes not. The two are NOT interchangeable; modern security baselines preferAuthenticated Users.CREATOR OWNERis a placeholder SID — when set as inheritable, new files inherit an ACE granting their creator (not the original CREATOR OWNER user) full control. Useful for shared directories where each user should own their own files.- DFS replication can desync ACLs — replicated folders may have ACLs that drift between replicas. Set the source ACL with
/save//restorerather than incrementally. - SIDs in ACLs survive user deletion — when a user is deleted, their SID remains in any ACL granting them rights.
icaclsdisplays the bare SID (e.g.*S-1-5-21-...) instead of a name. Clean up with/findsid+/remove. - Compressed and encrypted files have separate semantics — EFS-encrypted files can be listed with
icacls /grantbut require key recovery to read; compressed files have normal ACLs but/save//restoreignores the compression flag. - NULL DACL means "everyone has full access" — distinct from "no DACL" (which means "nobody").
icaclscannot create a NULL DACL directly; useSet-Aclwith$acl.SetAccessRuleProtection($true, $false)followed by clearing all rules. - Numeric SIDs need the
*prefix — Microsoft Learn explicitly notes that when supplying a SID in numeric form rather than a friendly name, you must prefix it with*(for example*S-1-1-0). Without the asterisk,icaclssearches for a principal literally namedS-1-1-0and fails with "No mapping between account names and security IDs was done". icaclsenforces canonical ACE order — operations rearrange ACEs into the canonical sequence: explicit denies → explicit grants → inherited denies → inherited grants. Tools that depend on a different ordering (third-party ACL editors that pre-date the canonical-ordering rule) may interact poorly with paths edited byicacls./denyremoves a matching explicit grant — Microsoft Learn documents that adding an explicit deny for a permission silently removes any explicit grant of the same permission for the same SID. The net effect is "switch this ACE from allow to deny" rather than "add a deny on top".
Real-world recipes (extended)
Reset a profile after re-imaging
When a user's profile gets corrupted ACLs (often after restoring from backup), reset to inherited defaults and re-grant.
icacls C:\Users\alicedev /reset /T /C /Q
icacls C:\Users\alicedev /setowner alicedev /T /C /Q
icacls C:\Users\alicedev /grant alicedev:"(OI)(CI)F" /T /C /Q
Output:
Successfully processed 2451 files; Failed processing 0 files
Successfully processed 2451 files; Failed processing 0 files
Successfully processed 2451 files; Failed processing 0 files
Lock down an app's data directory to itself only
Common deployment pattern: only the service account that runs the app, plus SYSTEM and Administrators, can touch the data folder.
icacls C:\AppData\myapp /inheritance:r
icacls C:\AppData\myapp /grant:r SYSTEM:"(OI)(CI)F"
icacls C:\AppData\myapp /grant:r Administrators:"(OI)(CI)F"
icacls C:\AppData\myapp /grant:r "IIS APPPOOL\MyAppPool":"(OI)(CI)M"
Output:
processed file: C:\AppData\myapp
Successfully processed 1 files; Failed processing 0 files
...
Audit a fileserver tree for orphaned SIDs
After domain migration or bulk account deletion, find ACEs referring to SIDs that no longer resolve to a name.
Get-ChildItem -LiteralPath \\fileserver01\Share -Recurse -Directory -ErrorAction SilentlyContinue |
ForEach-Object {
$acl = Get-Acl $_.FullName -ErrorAction SilentlyContinue
if ($acl) {
$acl.Access | Where-Object { $_.IdentityReference -match '^S-1-' } |
ForEach-Object {
[pscustomobject]@{
Path = $_.FullName
Sid = $_.IdentityReference.Value
Rights = $_.FileSystemRights
}
}
}
} | Export-Csv C:\Audit\orphaned_sids.csv -NoTypeInformation
Output:
(writes CSV with one row per orphaned ACE; review and clean with icacls /remove)
Migrate ACLs between two machines
rem On source:
icacls D:\Share /save D:\Backup\share_acls.bin /T
rem Copy share_acls.bin + the data tree to the target machine
rem On target (after restoring data):
icacls D:\Share /restore D:\Backup\share_acls.bin
Output:
Successfully processed 312 files; Failed processing 0 files
Successfully processed 312 files; Failed processing 0 files
For cross-domain migration with different SIDs, add /substitute pairs as described above.
Detect anyone with FullControl other than admins
Get-ChildItem -LiteralPath C:\Data -Recurse -Force |
ForEach-Object {
$path = $_.FullName
(Get-Acl $path).Access | Where-Object {
$_.FileSystemRights -match 'FullControl' -and
$_.IdentityReference -notmatch 'Administrators|SYSTEM|TrustedInstaller'
} | ForEach-Object {
[pscustomobject]@{ Path=$path; Identity=$_.IdentityReference; Inherited=$_.IsInherited }
}
} | Format-Table -AutoSize
Output:
Path Identity Inherited
---- -------- ---------
C:\Data\Sensitive\budget.xlsx CORP\bobdev False
C:\Data\Sensitive\salaries.csv CORP\bobdev False
Apply a template ACL to every new project folder
# Create the template
icacls C:\Templates\ProjectFolder /inheritance:r
icacls C:\Templates\ProjectFolder /grant:r "CORP\DevTeam":"(OI)(CI)M"
icacls C:\Templates\ProjectFolder /grant:r "BUILTIN\Administrators":"(OI)(CI)F"
# Copy ACL to each new project
foreach ($proj in 'ProjectA','ProjectB','ProjectC') {
New-Item -ItemType Directory -Path "C:\Projects\$proj" -Force | Out-Null
Get-Acl C:\Templates\ProjectFolder | Set-Acl -Path "C:\Projects\$proj"
}
Output: (silent — verify with icacls C:\Projects\ProjectA)
Find files where Everyone has write access (security audit)
Get-ChildItem -LiteralPath C: -Recurse -File -ErrorAction SilentlyContinue |
ForEach-Object {
$acl = Get-Acl $_.FullName -ErrorAction SilentlyContinue
if ($acl -and ($acl.Access | Where-Object {
$_.IdentityReference -eq 'Everyone' -and
$_.FileSystemRights -match 'Write|Modify|FullControl' -and
$_.AccessControlType -eq 'Allow'
})) {
$_.FullName
}
} | Out-File C:\Audit\world_writable.txt
Output:
(writes one path per line — review for legitimacy)
See also
- takeown — Take File Ownership — change ownership before changing ACLs
- runas — Run as Different User — elevate to run icacls on protected paths
- net user — Local User Account Manager — manage the accounts you grant access to
- net localgroup — Local Group Manager — group-based ACL management
- gpresult & gpupdate — inspect GPOs that set default ACLs
- permissions — chmod, chown, umask, ACLs — POSIX permissions for direct comparison
Sources
- icacls — Microsoft Learn