I’ve always found Get-ACL and Set-ACL to be quite powerful. It really depends what you want to achieve. For example:
- if you want to adjust inheritance
- if some folders/files have or need explicit permissions only (not inherited)
- if you want to add/remove single user permissions or replace an entire set of permissions
I think it offers a little more granular control than icacls (I'm open to correction on that, it's been a while since I tried icacls). Here's a couple of useful snippets:
Note: I’d recommend logging any permission changes before AND after so that you can reverse any changes that might go awry along the way.
# Define a few bits
$dir2change = 'C:\kk\ps.org\test\'
$user1 = 'DOMAIN\user1'
$user2 = 'DOMAIN\user2'
$user3 = 'DOMAIN\user3'
# For later
$inherit = [system.security.accesscontrol.InheritanceFlags]"ContainerInherit,ObjectInherit"
$propagation = [system.security.accesscontrol.PropagationFlags]"None"
# Logs - use whatever naming convention works
$time = Get-Date -f "yyyy_MM_dd-HH-mm"
$acl_log1 = "C:\kk\ps.org\perm-before-$time.log"
New-Item $acl_log1 -type file -Force | Out-Null
$acl_log2 = "C:\kk\ps.org\perm-after-$time.log"
New-Item $acl_log2 -type file -Force | Out-Null
# Get ACL of target folder
$acl1 = $dir2change | Get-Acl
# Log before
$acl1 | FL | Out-File $acl_log1 -Append
# Define new rules
$rule1 = New-Object System.Security.AccessControl.FileSystemAccessRule("$user1","FullControl", $inherit, $Propagation ,,,"Allow")
$rule2 = New-Object System.Security.AccessControl.FileSystemAccessRule("$user2","Modify", $inherit, $Propagation ,,,"Allow")
$rule3 = New-Object System.Security.AccessControl.FileSystemAccessRule("$user3","Traverse,ExecuteFile,Read,ListDirectory,ReadAttributes,ReadExtendedAttributes,ReadPermissions", $inherit, $Propagation ,,,"Allow")
# Add new rules to ACL
try{
Write-Host "Adding new rules to ACL..." -f Cyan
$acl1 | foreach { $_.AddAccessRule($rule1) }
$acl1 | foreach { $_.AddAccessRule($rule2) }
$acl1 | foreach { $_.AddAccessRule($rule3) }
Write-Host "OK" -f Green
}
catch{ Write-Host "NOT OK" -f Red }
# Apply the updated ACL
try{
Write-Host "Setting ACL on '$dir2change'...`n" -f Cyan
Set-Acl $dir2change $acl1 -Verbose
Write-Host "OK" -f Green
}
catch { Write-Host "NOT OK" -f Red }
# Log after
$acl2 = $dir2change | Get-Acl
$acl2 | FL | Out-File $acl_log2 -Append
This is what the ACL logs look like after that:
Before:
Path : Microsoft.PowerShell.Core\FileSystem::C:\kk\ps.org\test\
Owner : DOMAIN\kieran
Group : DOMAIN\Domain Users
Access : NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
DOMAIN\kieran Allow FullControl
CREATOR OWNER Allow 268435456
After:
Path : Microsoft.PowerShell.Core\FileSystem::C:\kk\ps.org\test\
Owner : DOMAIN\kieran
Group : DOMAIN\Domain Users
Access : DOMAIN\user1 Allow FullControl
DOMAIN\user2 Allow Modify, Synchronize
DOMAIN\user3 Allow ReadAndExecute, Synchronize
NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
DOMAIN\kieran Allow FullControl
CREATOR OWNER Allow 268435456
Similarly, you can remove the same permissions we added above by replacing the rule set above with:
$acl1 | foreach { $_.RemoveAccessRuleAll($rule1)}
$acl1 | foreach { $_.RemoveAccessRuleAll($rule2)}
$acl1 | foreach { $_.RemoveAccessRuleAll($rule3)}