Managing permissions on files and folders is essential to maintaining data security and access control in a Windows environment. With PowerShell, administrators have powerful tools to manage these permissions efficiently.
Two fundamental cmdlets for working with permissions are Get-ACL and Set-ACL. In this blog post, weโll explore using these cmdlets to manage NTFS permissions using PowerShell effectively.
Note. The Get-ACL and Set-ACL cmdlets can work with several PowerShell providers, including FileSystem, Registry, and Certificate. This article focuses only on FileSystem examples.
Understanding Access Control List (ACL) and Access Control Entry (ACE)
Access Control List (ACL)
An ACL, or Access Control List, is a data structure that defines the permissions and security settings associated with a particular object, such as a file, folder, or network resource. It contains a list of Access Control Entries (ACEs), each specifying the access permissions for a specific user, group, or security principal.
An ACL is a collection of rules that dictate who can perform what actions on the object. These actions include reading, writing, modifying, deleting, or executing the object. ACLs enforce security by ensuring that only authorized users or groups can access and manipulate resources.
Important. Set-Acl writes the entire ACL object back to the target file/folder. If you modify an ACL object incorrectly, existing permissions may be overwritten. You should always review the ACL before applying changes.
Access Control Entry (ACE)
An ACE, or Access Control Entry, is a component of an ACL that defines the permissions granted or denied to a specific security principal (user, group, or other entity). An ACE contains the following elements:
- Security Principal: The user or group for whom the permissions are being defined. It can also represent specific types of users, such as anonymous users or built-in system accounts.
- Access Type: This specifies whether the ACE allows or denies the specified permissions. Access types can include โAllowโ or โDeny.โ
- Access Mask (Rights): The access mask is a set of flags that represent specific permissions or rights. It defines what actions the security principal is allowed or denied on the resource. Examples of access mask flags include read, write, execute, delete, modify, and more.
- Inheritance Flags: Inherited ACEs allow permissions to be propagated from a parent object to its child objects. Inheritance flags indicate whether the ACE should be inherited and how it should be inherited.
- Propagation Flags: These flags control the inheritance of ACEs and how child objects inherit them. They define whether ACEs should be inherited only by immediate children, all descendants, or no descendants.
When a user or group attempts to access a resource, the operating system checks the ACL associated with that resource to determine whether the requested action is allowed or denied based on the ACEs within the ACL.
When Windows evaluates an ACL, it does not simply read permissions as a flat list. Instead, access is determined by a strict evaluation model. Note that access decisions are based on the userโs access token, which contains SIDs from direct and nested group memberships. Keep in mind that ACLs are evaluated against this token, not against the user identity directly.
- Explicit Deny and Allow rules are evaluated according to the ACL evaluation order. An explicit Deny is evaluated before an explicit Allow, and both explicit ACEs are evaluated before inherited ACEs. However, an explicit Allow can take precedence over an inherited Deny. The simplified canonical order is: Explicit Deny > Explicit Allow > Inherited Deny > Inherited Allow.
- Explicit rules override inherited rules. Explicit ACEs are evaluated before inherited ACEs.
This means local object permissions are stronger than parent directory permissions. - Evaluation is order-sensitive inside ACL. Windows evaluates ACEs in order until access is granted/access is explicitly denied.
- Group membership expands evaluation scope. A user can receive multiple Allow rules from different groups, a single Deny rule that overrides all of them.
- Effective permissions are computed from the access token, not from ACL entries directly.
Windows expands group membership (including nested groups) before evaluating ACLs.
When modifying ACLs, you should always consider existing Deny rules. Keep in mind that adding Allow permissions does not override an explicit Deny entry for the same user/group.
Understanding Get-ACL and Set-ACL
PowerShell version differences in ACL handling
Get-Acl and Set-Acl work in both Windows PowerShell 5.1 and PowerShell 7. For NTFS permissions on Windows, behavior is generally identical when executed locally. Differences may appear when ACL objects are serialized through remoting/automation pipelines.
Get-ACL
The Get-ACL cmdlet retrieves the ACL information for a specified file or folder object. The object can be at a local or network location. The ACL contains a list of ACEs that define the permissions and security principals associated with the object. To use Get-ACL, the syntax is as follows.
Get-ACL -Path C:\Path\To\FileOrFolder
For example, letโs get the ACL of a file and folder on the local machine:
# Folder ACL
$dir_acl = Get-Acl -Path C:\demo\dir1
$dir_acl | Get-Member -MemberType CodeProperty, Property
# File ACL
$file_acl = Get-Acl C:\demo\dir1\dummy_file.txt
$file_acl | Get-Member -MemberType CodeProperty, Property
The folder ACL and file ACL returns these two object classes, respectively.
[System.Security.AccessControl.DirectorySecurity]

(link to learn more)
[System.Security.AccessControl.FileSecurity]

(link to learn more)
Youโll notice that both ACL objects have identical properties, indicating that they are only different in the item type (directory vs. file.)
Once you have retrieved the objectโs ACL, you can view the properties by piping the object to the Format-List cmdlet.
$dir_acl | Format-list $file_acl | Format-List
For comparison, the ACL object shown in PowerShell and File Explorer is below.

The brief description of the output is as follows.
- Path = Path to the resource (Provider::Resource Path)
- Owner = Owner of the resource
- Group = Group of the owner
- Access = Access control entries
Note. The -536805376 access right is only shown when inheritance is enabled on a directory object.
Set-ACL
The Set-ACL cmdlet allows you to modify permissions on files and folders. It allows adding, modifying, or removing ACEs from an objectโs ACL. The basic syntax is:
Set-ACL -Path C:\Path\To\FileOrFolder -AclObject $aclObject
The $aclObject can be copied from another file or folder ACL or created from scratch. The succeeding sections will show more examples of setting ACLs on files and folders.
Managing Permissions with Get-ACL and Set-ACL
Modifying permissions on an object is a common task in file management. Combining Get-ACL and Set-ACL makes a perfect pair when modifying existing permissions.
Suppose you want to replace the owner of a directory and grant โReadโ and โWriteโ permissions to a group.
- Directory object path: C:\demo\dir1
- New owner: THEITBROS\alpha
- Additional group: THEITBROS\CA IT Ops
Note that the behavior of ACL cmdlets is consistent across PowerShell 5.1 and 7 when executed locally on Windows, but you can see differences in remoting and automation contexts.
PowerShell ACL vs ICACLS (Enterprise Consideration)
While PowerShell provides full access to NTFS ACL management through Get-Acl and Set-Acl, in enterprise environments icacls.exe is still widely used for easier scripting in legacy systems.
When you should use PowerShell:
- in automation scripts
- during object-level ACL manipulation
- integration with AD/identity logic
When to use icacls:
- during large-scale recursive permission changes
- production file servers
- in migration cases
Here is the command:
icacls C:\demo\dir1 /grant "DOMAIN\User:(R,W)"
Note that in modern Windows environments, both tools are fully supported. Your choice should depend more on operational consistency than capability differences.
Changing the Owner
Before modifying permissions, retrieving the current ACL using Get-ACL is a good practice.
$acl = Get-ACL -Path C:\demo\dir1
Important. Keep in mind that changing the owner of a file/folder is a privileged operation. Depending on the current owner, the requested new owner, and the account’s privileges, the operation may require an elevated PowerShell session and the SeTakeOwnershipPrivilege privilege. Without the required permissions, SetOwner() can fail with an UnauthorizedAccessException.
You can check the current owner before making any ownership changes with the command:
(Get-Acl -Path C:\demo\dir1).Owner
The current ACL is now stored in the $acl variable. Letโs manipulate this variable to set the new owner and add the access control entry for the group.
To set the owner, call the SetOwner() method.
# Specify the new owner $newOwner = 'THEITBROS\alpha' # Set the new owner $newOwnerId = ([System.Security.Principal.NTAccount]::new($newOwner)).Translate([System.Security.Principal.SecurityIdentifier]) $acl.SetOwner($newOwnerId)
The owner has now been changed in the in-memory ACL object. The change is applied to the file or folder only when you call Set-Acl.

Hint. You can also change a file or folder ownership using the takeown.exe command.
Adding Access Control Entry
To add a Read and Write access to the group, we must create the object [System.Security.AccessControl.FileSystemAccessRule] (learn more here).
# Specify the new group to add
$addtlGroupIdentity = 'THEITBROS\CA IT Ops'
# Define the access rights
$addtlGroupAccessRights = 'Read,Write'
# Define the access type
$addtlGroupAccessType = 'Allow'
# Inherit the ACE to child folders and files
$inheritanceFlags = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor `
[System.Security.AccessControl.InheritanceFlags]::ObjectInherit
$propagationFlags = [System.Security.AccessControl.PropagationFlags]::None
Because the example applies permissions to a directory, the inheritance flags ensure that the ACE is also inherited by child folders and files.
$newACE = [System.Security.AccessControl.FileSystemAccessRule]::new(
$addtlGroupIdentity,
$addtlGroupAccessRights,
$inheritanceFlags,
$propagationFlags,
$addtlGroupAccessType
)

Add the new ACE to the ACL object by calling the AddAccessRule() method.
# Set the new ACE $acl.AddAccessRule($newACE) # Display the updated ACE $acl.Access | Format-Table
Note that adding an Allow rule does not guarantee access if a conflicting Deny rule exists in the ACL/inherited from parent objects.

Finally, letโs modify the ACL of the directory with Set-ACL.
# Update the directory ACL Set-Acl -Path C:\demo\dir1 -AclObject $acl # Get the new directory ACL Get-Acl -Path C:\demo\dir1 | Format-List
Removing Access Control Entry
Suppose the user or group is no longer eligible to access a folder, and you must remove its ACE from that directoryโs ACL. Hereโs how to do it.
Get the current ACL and list the ACEs.
$acl = Get-ACL -Path C:\demo\dir1 $acl.Access | Format-Table
Note the 0-index-based position of the ACE you want to remove. In this example, the ACE is at index 0.

Note. While you can identify an ACE by its position in the Access list (e.g., index 0), we strongly recommend against index-based removal. Keep in mind that array order isn’t guaranteed to remain stable, and identity-based matching (shown below) is safer.
To remove that access control entry, the command to run is:
# Get ACL
$acl = Get-Acl "C:\demo\dir1"
# Define the rule to remove
$ruleToRemove = $acl.Access | Where-Object {
$_.IdentityReference -eq "THEITBROS\CA IT Ops" -and
$_.AccessControlType -eq "Allow"
}
if ($ruleToRemove) {
# Remove the matching rule
$acl.RemoveAccessRule($ruleToRemove)
# Apply changes
Set-Acl -Path "C:\demo\dir1" -AclObject $acl
}
else {
Write-Warning "No matching ACE was found. No changes were made."
}
RemoveAccessRule removes matching permissions from the ACL. Note that in case multiple ACEs exist for the same identity, you may need to use RemoveAccessRuleSpecific() to remove a single rule.
In enterprise environments, ACL modifications should always be identity-based, not index-based. You should always match on IdentityReference and Rights, not array position.
Keep in mind that FileSystemRights is a bitmask. You need to avoid strict equality comparisons in production scripts. You should prefer .HasFlag()/check for containment.
As you can see, the ACE for โTHEITBROS\CA IT Opsโ has been removed.

Keep in mind that removing an ACE can change effective permissions in unexpected ways (especially when multiple Allow and Deny rules exist for the same identity/group membership chain).
In production environments, icacls.exe is often preferred for deterministic permission changes and auditability.
icacls C:\demo\dir1 /remove "THEITBROS\CA IT Ops"
Turning-Off Inheritance Without Breaking Existing Access Rights
Turning off inheritance without breaking existing access rights involves removing inheritance from an objectโs ACL while preserving the existing access rights. This can be useful when you want to establish custom permissions for an object without affecting the permissions inherited from its parent. Hereโs how you can achieve this using PowerShell.
First, we retrieve the current ACL of the target object.
# Define the path to the file or folder $objectPath = "C:\demo\dir1"
# Retrieve the current ACL
$acl = Get-ACL -Path $objectPath
# Show the current inheritance status
$acl.AreAccessRulesProtected
In this example, we can confirm that the inheritance is turned on.

To remove the inheritance, weโll call the SetAccessRuleProtection(bool isProtected, bool preserveInheritance) method. This method requires two arguments:
- isProtected (Boolean) โ Sets the inheritance on or off.
- preserveInheritance (Boolean) โ Indicates whether existing ACEs will be retained or removed.
# Disable inheritance while preserving existing ACEs $acl.SetAccessRuleProtection($true, $true) # Disable inheritance, keep existing ACEs
Note that when you disable inheritance, preserving inherited ACEs can lead to permission duplication and unexpected effective access expansion.
Finally, apply the modified ACL to the object.
# Apply the modified ACL to the object Set-ACL -Path $objectPath -AclObject $acl

NTFS vs Share Permissions Interaction
NTFS permissions alone do not define effective access in network cases. When accessing files over SMB shares, both Share permissions and NTFS permissions are evaluated.
- Effective access rule:
Most restrictive of Share permissions and NTFS permissions.
Here is an example:
Share: Full Control
NTFS: Read only
Result: Read only
Share: Read
NTFS: Full Control
Result: Read only
Note that share permissions are evaluated only at SMB layer. Local access (console/RDP) ignores share ACLs entirely.
Important note. You should note that Set-Acl modifies only NTFS permissions. It does not affect Share permissions.
Auditing NTFS Access and Permission Changes
In order to track who accessed/modified permissions, NTFS supports auditing through System Access Control List (SACL). Note that auditing requires two layers:
- First enable Audit Policy. To do this, use Group Policy. Go to Computer Configuration โ Windows Settings โ
Security Settings โ Advanced Audit Policy Configuration โ Object Access โ Audit File System.
Here you need to enable:
– Success
– Failure - Now you need to configure SACL on object. You can do this with PowerShell:
$acl = Get-Acl -Path C:\demo\dir1 -Audit
$auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
"Everyone",
"Modify",
"Success, Failure"
)
$acl.AddAuditRule($auditRule)
Set-Acl C:\demo\dir1 $acl
Keep in mind that excessive SACL usage on high-traffic file servers can significantly impact performance due to audit log generation overhead.
Note that without auditing:
- you cannot track who changed permissions
- troubleshooting security incidents becomes difficult
- compliance requirements may not be met (ISO, SOC2)
Keep in mind that both Audit Policy and SACL entries must be configured for auditing to work.
Caution and Best Practices
When working with permissions, exercise caution to prevent unintentional security vulnerabilities. Here are some best practices:
- Test in a Safe Environment: Before making changes in a production environment, test your PowerShell scripts in a controlled and safe environment to ensure the desired results.
- Document Changes: Maintain clear documentation of the changes you make to permissions, including who made the changes and the reasons behind them.
- Backup: Before modifying permissions, create backups of the current ACL. Exporting the objectโs ACL to an XML file is the most convenient way.
Get-Acl -Path C:\Path\To\FileOrFolder | Export-CliXml -Path C:\Path\To\ACLBackup.xml - Creating an ACL backup allows you to restore the original permissions if something goes wrong. The following command restores the ACL from the backup file and applies it to the target file/folder (you need to ensure the backup was created from the same resource/compatible object before restoring it):
$acl = Import-CliXml -Path C:\Path\To\ACLBackup.xml
Set-Acl -Path C:\Path\To\FileOrFolder -AclObject $acl - Least Privilege: Follow the principle of least privilege, granting only the necessary permissions to users and groups to minimize security risks.
Best Practice: Effective Permissions Model
- Note that you should always evaluate effective access, not raw ACL. You should use:
Get-Acl | Format-List
or even better use Advanced Security Settings (GUI) and Effective Access tab. - You should never assume that ACL is an actual access. Because real access depends on group nesting, Deny precedence, inheritance and token evaluation.
- Note that Microsoft recommends using Allow-based models and minimizing Deny usage. However, sometimes you may require Deny for explicit exclusion cases (e.g., breaking inheritance chains, restricting sensitive folders in delegated environments).
Conclusion
PowerShell provides robust cmdlets like Get-ACL and Set-ACL that effectively empower administrators to manage NTFS permissions on files and folders. By understanding these cmdlets and following best practices, you can maintain a secure and organized environment while ensuring the right users have the appropriate access to your data.
ACLs and ACEs provide the foundation for controlling access to resources in a secure and controlled manner. They allow administrators to define fine-grained permissions for users and groups, ensuring that sensitive data remains protected and authorized users can perform their intended actions.
Remember always to exercise caution when working with permissions.
What does the Get-Acl cmdlet do in PowerShell?
The Get-Acl cmdlet retrieves the security descriptor of a file or folder, including the owner, group, access rules (ACEs), and auditing settings.
What does the Set-Acl cmdlet do?
The Set-Acl cmdlet applies a modified ACL to a file or folder, allowing you to change ownership, add or remove permissions, configure inheritance, or update auditing settings.
Are Get-Acl and Set-Acl supported in both Windows PowerShell and PowerShell 7?
Yes. Both cmdlets are available in Windows PowerShell 5.1 and PowerShell 7+, although PowerShell 7 may behave differently in remoting and serialization scenarios.
How do I change the owner of a file or folder with PowerShell?
Retrieve the ACL with Get-Acl, modify the owner using the SetOwner() method, and then apply the changes with Set-Acl.


