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.

cmd
icacls /?

Output:

ini
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

cmd
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

SwitchMeaning
(just path)Display the ACL of the file or directory
/grant user:permAdd a permission grant (additive)
/grant:r user:permReplace existing grants with the new set
/deny user:permAdd an explicit deny entry
/remove userRemove all ACEs (grants and denies) for a user
/remove:g userRemove only grant ACEs
/remove:d userRemove only deny ACEs
/TApply recursively to all files and subdirectories
/CContinue on error instead of stopping
/QSuppress 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 fileRestore ACLs from a saved file
/inheritance:e|d|re=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:

MaskMeaning
FFull control
MModify (read + write + delete, no ACL change)
RXRead & execute
RRead-only
WWrite
DDelete
(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.

cmd
icacls C:\Data\Reports

Output:

r
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.

cmd
icacls C:\Data\Reports /grant alicedev:(OI)(CI)M

Output:

csharp
processed file: C:\Data\Reports
Successfully processed 1 files; Failed processing 0 files
cmd
rem Replace (not add to) alicedev's grants
icacls C:\Data\Reports /grant:r alicedev:(OI)(CI)RX

Output:

csharp
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.

cmd
icacls C:\Sensitive /deny "BUILTIN\Users":RX

Output:

csharp
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).

cmd
icacls C:\AppData\Logs /grant alicedev:(OI)(CI)M /T /C /Q

Output:

ini
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.

cmd
icacls C:\Data\Reports /remove alicedev

Output:

csharp
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.

cmd
icacls C:\Data\Reports /reset /T /C /Q

Output:

ini
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.

cmd
icacls C:\Data /save C:\Backup\data_acls.bin /T

Output:

ini
Successfully processed 42 files; Failed processing 0 files
cmd
rem Restore ACLs to the same tree
icacls C:\Data /restore C:\Backup\data_acls.bin

Output:

ini
Successfully processed 42 files; Failed processing 0 files

Common pitfalls

  1. User principal names are case-sensitive on some systems — prefer DOMAIN\username or BUILTIN\Administrators to avoid silent no-ops.
  2. /grant without :r is additive — running it twice adds a second ACE; use /grant:r when you want a clean replace.
  3. 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.
  4. /T /C is needed for directories with protected sub-paths — without /C, the first access-denied path terminates the command, leaving the rest unprocessed.
  5. /save stores SIDs, not account names — if the file is moved to a different domain, /restore may not resolve old SIDs; use /substitute OldSid NewSid during restore to remap them.

Real-world recipes

Lock a folder to a specific user only

cmd
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:

csharp
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

cmd
icacls C:\inetpub\wwwroot\myapp /grant "IIS APPPOOL\DefaultAppPool":(OI)(CI)RX /T /C /Q

Output:

ini
Successfully processed 156 files; Failed processing 0 files

Audit who has explicit permissions on a directory tree

cmd
icacls C:\Sensitive /T /C 2>NUL | findstr /V "Successfully\|Failed"

Output:

r
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

CodeNameIncludes
FFull controlAll specific rights including WO, WD, WA
MModifyRX + W + D (cannot change ACL or take ownership)
RXRead & executeR + X
RReadRA + REA + RC
WWriteWA + WEA + AD + WD (but not delete)
DDeleteSingle-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):

CodeRightBit name
RDRead data / List directoryFILE_READ_DATA
WDWrite data / Add fileFILE_WRITE_DATA
ADAppend data / Add subdirectoryFILE_APPEND_DATA
REARead extended attributesFILE_READ_EA
WEAWrite extended attributesFILE_WRITE_EA
XExecute / TraverseFILE_EXECUTE / FILE_TRAVERSE
DCDelete childFILE_DELETE_CHILD
RARead attributesFILE_READ_ATTRIBUTES
WAWrite attributesFILE_WRITE_ATTRIBUTES
DEDeleteDELETE
RCRead control (DACL)READ_CONTROL
WDACWrite DACWRITE_DAC
WOWrite ownerWRITE_OWNER
SSynchronizeSYNCHRONIZE
GAGeneric AllGENERIC_ALL
GRGeneric ReadGENERIC_READ
GEGeneric ExecuteGENERIC_EXECUTE
GWGeneric WriteGENERIC_WRITE

Example — grant a service account "append-only writes for logging" without read or delete:

cmd
icacls C:\Logs /grant "MYHOST\svclog":"(OI)(CI)(AD,WD,RA,REA,RC,S)"

Output:

csharp
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.

FlagMeaning
(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
NNo 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:

CombinationResult
(OI)(CI)Applies to this folder, all files, and all subfolders recursively
(OI) aloneApplies to this folder and all files inside (but not subfolders)
(CI) aloneApplies 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.

cmd
icacls C:\Data /grant alicedev:"(0x1F01FF)"

Output:

csharp
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

cmd
icacls C:\Private /inheritance:d

Output:

csharp
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:

cmd
icacls C:\Private

Output:

r
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

cmd
icacls C:\Private /inheritance:r

Output:

csharp
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

cmd
icacls C:\Private /inheritance:e

Output:

csharp
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.

cmd
rem View an object's integrity level
icacls C:\Sandbox\untrusted.exe

Output:

makefile
C:\Sandbox\untrusted.exe BUILTIN\Users:(I)(RX)
                         Mandatory Label\Low Mandatory Level:(NW)
cmd
rem Set Low integrity level
icacls C:\Sandbox\untrusted.exe /setintegritylevel L

Output:

csharp
processed file: C:\Sandbox\untrusted.exe
Successfully processed 1 files; Failed processing 0 files
LevelCodeTypical use
LowLBrowser sandbox, attachment downloads
MediumMStandard user processes (default)
HighHElevated/admin processes
SystemSKernel 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

powershell
Get-Acl C:\Data\Reports | Format-List

Output:

css
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

powershell
$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

powershell
$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

powershell
$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.

powershell
(Get-Acl C:\Data\Reports).Sddl

Output:

css
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 SID
  • G: primary group SID (legacy POSIX compatibility)
  • D:AI discretionary 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.

cmd
rem Save ACLs on the source
icacls C:\Data /save C:\Backup\data_acls.bin /T

Output:

ini
Successfully processed 312 files; Failed processing 0 files
cmd
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:

code
S-1-5-21-1234567890-987654321-111111111-1001
cmd
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:

ini
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.

cmd
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:

csharp
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:

powershell
# 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:

sql
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.

ConceptLinuxWindows
View permissionsls -licacls path
Three classesowner/group/otherNone — every principal is an ACE
Readr (4)R or RD,REA,RA,RC,S
Writew (2)W or WD,AD,WEA,WA
Executex (1)X
Modify permsneeds ownershipneeds WRITE_DAC (owner has it implicitly)
Inheritancesetfacl -d (default ACLs)(OI)(CI) flags
Recursion-R (or /T in icacls)/T
Deny entriesnot in POSIX (ACLs only via mask)First-class (with precedence over allow)
Special bitssetuid, setgid, stickyNo equivalent (capabilities instead)
Sandbox enforcementnamespaces, AppArmor, SELinuxMandatory Integrity Control (MIC)
ACL backupgetfacl -R / setfacl --restoreicacls /save and /restore

Common mappings between chmod modes and icacls ACEs:

bash
# Linux: 755 — owner full, others read+execute
chmod 755 file

Output: (none — exits 0 on success)

cmd
rem Windows equivalent (approximate; assumes owner=alicedev, group=Users)
icacls file /inheritance:r /grant alicedev:F /grant "BUILTIN\Users":RX

Output:

csharp
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.

cmd
rem Enable file system audit policy globally
auditpol /set /subcategory:"File System" /success:enable /failure:enable

Output:

bash
The command was successfully executed.

Adding a SACL entry to a path tells Windows which operations to audit on that object:

cmd
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:

powershell
$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)

powershell
# 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:

css
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.

cmd
icacls C:\Data /verify /T /C

Output:

ini
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.

cmd
icacls C:\ /findsid "MYHOST\alicedev" /T /C 2>NUL

Output:

makefile
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:

cmd
icacls C:\ /remove "MYHOST\alicedev" /T /C 2>NUL

Output:

ini
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:

  1. Quoting matters for complex permission strings(OI)(CI)F works 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.
  2. /T on huge trees is single-threaded — bulk security-descriptor work on millions of files can take hours. For very large trees, use robocopy /COPY:S /SECFIX /MT:32 to apply security descriptors with multi-threading.
  3. Empty /grant syntax silently no-opsicacls file /grant alicedev (no permission) accepts the command and prints success without making any change. Always specify :perm.
  4. /setowner requires SeRestorePrivilege, not SeTakeOwnership — you can take ownership of files you don't own, but to assign ownership to a different user you need SeRestorePrivilege, which Administrators have by default. From a standard user shell, /setowner fails with "A required privilege is not held by the client".
  5. Authenticated UsersEveryoneEveryone includes anonymous and guest sessions; Authenticated Users does not. The two are NOT interchangeable; modern security baselines prefer Authenticated Users.
  6. CREATOR OWNER is 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.
  7. DFS replication can desync ACLs — replicated folders may have ACLs that drift between replicas. Set the source ACL with /save//restore rather than incrementally.
  8. SIDs in ACLs survive user deletion — when a user is deleted, their SID remains in any ACL granting them rights. icacls displays the bare SID (e.g. *S-1-5-21-...) instead of a name. Clean up with /findsid + /remove.
  9. Compressed and encrypted files have separate semantics — EFS-encrypted files can be listed with icacls /grant but require key recovery to read; compressed files have normal ACLs but /save//restore ignores the compression flag.
  10. NULL DACL means "everyone has full access" — distinct from "no DACL" (which means "nobody"). icacls cannot create a NULL DACL directly; use Set-Acl with $acl.SetAccessRuleProtection($true, $false) followed by clearing all rules.
  11. 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, icacls searches for a principal literally named S-1-1-0 and fails with "No mapping between account names and security IDs was done".
  12. icacls enforces 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 by icacls.
  13. /deny removes 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.

cmd
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:

ini
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.

cmd
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:

python-repl
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.

powershell
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:

sql
(writes CSV with one row per orphaned ACE; review and clean with icacls /remove)

Migrate ACLs between two machines

cmd
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:

ini
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

powershell
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:

sql
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

powershell
# 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)

powershell
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:

css
(writes one path per line — review for legitimacy)

See also

Sources

  • icacls — Microsoft Learn