Setting Folder ACL

Hi All,

I’m trying to set ACL permissions on a folder. The goal is to set a ACE on the folder with a user in AD to have modify rights. Cant seem to ge thtis done. Below you find a snipping from the script.

setup directories
New-Item -Path \ec-data\ecdata\documents -Name $initials -ItemType directory
set acl
$homepath=“\ec-data\ecdata\documents$initials”
$acl=get-acl $homepath
$permission=“ecp$username”,“modify”,“allow”
$accessrule=new-object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessrule)
$acl | set-acl $homepath
new-item -path \ec-redir\pst$ -name $username -ItemType directory

I think this is the third ACL post today ;). The usual recommendation is, “Get-ACL and Set-ACL are a pain in the butt. Consider using icacls.exe instead.”

I know that seems like “cheating,” but it isn’t. There’s a good reason PowerShell runs external command-line utilities - sometimes, they’re the best tool for the job.

If you are for some reason dead-set on using Set-ACL, you’ll need to do more than post your script, though. What’s it do? Any errors? Does absolutely nothing happen? Does Get-ACL return something different from the GUI?

But… I’d use the command-line utility.

My experience shows you have to use the right set of parameters for FileSystemAccessRule. Another thing to be aware of is if you are creating the account in the same script you may have to delay it, or get the GUID of the account at creation (PassThru) and apply with that.

The particular constructor I’ve had to follow is this one :
FileSystemAccessRule Constructor (IdentityReference,FileSystemRights,InheritanceFlags,PropagationFlags,AccessControlType)

Details : https://msdn.microsoft.com/en-us/library/ms147785(v=vs.110).aspx

Here are some snippets of the script I created at work that is used to make new shared folders.

$Internal_ACL = Get-Acl -Path $Internal

#region Disable Inheritance, remove previous ACLs
$Internal_ACL.SetAccessRuleProtection($true, $false) | Out-Null
$Internal_ACL.Access | ForEach-Object { $Internal_ACL.RemoveAccessRule($_) | Out-Null }
#endregion Disable Inheritance, remove previous ACLs

#region Create ACLs
# Domain Admins
$Rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule("Domain Admins", "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
$Internal_ACL.AddAccessRule($Rule) | Out-Null
# Group rights
$Rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule($Department.Group, "DeleteSubdirectoriesAndFiles, Modify", "ContainerInherit, ObjectInherit", "InheritOnly", "Allow")
$Internal_ACL.AddAccessRule($Rule) | Out-Null
$Rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule($Department.Group, "DeleteSubdirectoriesAndFiles, Write, ReadAndExecute", "None", "None", "Allow")
$Internal_ACL.AddAccessRule($Rule) | Out-Null

# Internal Group rights
$Rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule($Department.GroupInR, "ReadAndExecute", "ContainerInherit, ObjectInherit", "None", "Allow")
$Internal_ACL.AddAccessRule($Rule) | Out-Null
#endregion Create ACLs

#region Set ACLs
Set-Acl -Path $Internal -AclObject $Internal_ACL -ErrorVariable ACLError -ErrorAction 'SilentlyContinue' | Out-Null
if ($ACLError)
{
	Write-Output -InputObject "An error was caught attempting to apply security rights, `n`tyou may have to take ownership of the `'$ParentPath\$Internal`' folder"
	Write-Verbose -Message "$ACLError"
	$ACLError = $false
} # if ACLError
#endregion Set ACLs

I don’t already know icacls.exe. Am I better off trying to learn how to massage ACLs with PS, or taking the detour in my PS exploration and learning icacls.exe instead? Which will be faster? Will learning icacls give me a better understanding of what is being done in this command?

$accessrule=new-object System.Security.AccessControl.FileSystemAccessRule $permission

Thanks!

I’d learn Icacls. But no, it won’t help with that command. That command is basically low level .NET programming. Icacls doesn’t use that. And yes, it’s faster. It’s probably easier. And it isn’t a detour; you can run it in PowerShell. The Set-Acl stuff would also, by that definition, be a detour, because it’s going to be low-level .NET programming, not cmdlets.

That script line just creates the rule, and matters a lot on what is in that $permission variable.

The link my previous post is probably your best information for what those settings are and how to configure them for what you need.

As for details about that particular line, it’s creating a variable called $accessrule, which is a System.Security.AccessControl.FileSystemAccessRule .NET object type, the properties you feed it are what define the created object. You typically collect these in a different object with the .AddAccessRule method of an ACL Object.

Learning either is useful, I’ve been trying to stick with powershell for as much as I can but I recently just hit a task where I had to break out icalcs for something powershell just could not properly push through, and gave almost no error about. So learn both, and then learn to start with the powershell part and just have icacls as a backup in the same script, that would be a great lesson.

If you have more specific questions about that object type or the properties it sets, I can attempt to answer them to the best of my knowledge.

Gentlemen, thanks for the quick responses!

I’ll take a look at icacls. Y’all scared me off with talk of ‘low-level .NET objects’. I understood the creation of the $permission variable in the earlier statement. I understood the ‘$accessrule=’ was creating another variable and was using the $permission variable, but the whole ‘new-object System.Security.AccessControl.FileSystemAccessRule’ threw me for a loop.

Don, FYI, ‘PS in a Month of Lunches’ arrived today. The preface and ‘1.2 Is this book for you?’ are very encouraging. You and Jeff are correct in the preface; the other books I’ve looked at assumed previous scripting experience.

Okay, let me know if this icacls question is considered outside the scope of this forum and I’ll take it elsewhere. On the other hand, since y’all did bring it up…

I’m not getting the results I expect from either of these commands. As I understand, either of these should remove ‘Domain Users’ from the ACL of the specified folder.

PS C:\> icacls \\domain.com\users\last6fm /remove 'domain.com\Domain Users'
processed file: \\domain.com\users\last6fm
Successfully processed 1 files; Failed processing 0 files

and

PS C:\> icacls \\domain.com\users\last6fm /remove:g 'domain.com\Domain Users'
processed file: \\domain.com\users\last6fm
Successfully processed 1 files; Failed processing 0 files

When I look in the GUI, Domain Users is still in the ACL with ‘Read & Execute’, ‘List Folder Contents’, and Read, the permissions it had before I ran either command. The output seems to indicate my commands are valid, so apparently the syntax isn’t correct for what I want to accomplish.

Thanks for any suggestions, including that I take this elsewhere.

Are those permissions inheriting from somewhere else? Icacls only modifies the direct ACL; it doesn’t (and can’t) remove any permissions inheriting from above, unless you use it to disable inheritance.

Yes, they are inherited!

Setting the ACL is a step in the series of actions I take when I create a new user account. I’m used to doing it all from the GUI, and disabling the inheritance has become another automatic ‘point and click’ action to me. I don’t consciously think about it any more (or most of the other steps).

I’ll dig up handling inheritance at the command line. Thanks.

I licked the inheritance problem (icacls parameter /inheritance:d, if anyone is interested in the future).

Also for anyone else’s future benefit, I ran into an issue using a variable with icacls. The first command works successfully, but it’s just to test my syntax. The second replaces the coded username with a variable holding the same username string. I tried enclosing the entire domain.com$Name in single quotes, double quotes, percent signs, double and single, etc. Enclosing only the variable but NOT the domain turned out to be the solution.

PS C:\Windows\system32> icacls \\domain.com\users\$Name /grant domain.com\last6fm:m 
processed file: \\domain.com\users\last6fm
Successfully processed 1 files; Failed processing 0 files

PS C:\Windows\system32> icacls \\domain.com\users\$Name /grant "domain.com\$Name":m 
icacls : Invalid parameter "/grant"
At line:1 char:1
+ icacls \\domain.com\users\$Name /grant "domain.com\$Name":m
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Invalid parameter "/grant":String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

PS C:\Windows\system32> icacls \\domain.com\users\$Name /grant domain.com\"$Name":m 
processed file: \\mu3srvfsbbg1.mu3.global.sys\users\last6fm
Successfully processed 1 files; Failed processing 0 files

I’ve got to figure out more effective search terms to use when Googling. I’m getting too many results that relate to other scripting tools.

Well, I thought it did what I want but it doesn’t. It handles the variable okay, but I’m still not getting the permissions I want.

I thought :M would apply the same permissions as checking Modify in the GUI, but it doesn’t. The account is added to the ACL but the permissions are applied as ‘Special’.

I also can’t get the command to accept the (OI) and (CI) inheritance parameters for subordinate files and folders.

PS C:\Windows\system32> icacls \\domain.com\users\$Name /t /grant last6fm:(OI)(CI)M
OI : The term 'OI' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the 
name, or if a path was included, verify that the path is correct and try again.
At line:1 char:69
+ icacls \\mu3srvfsbbg1.mu3.global.sys\users\$Name /t /grant last6fm:(OI)(CI)M
+                                                                     ~~
    + CategoryInfo          : ObjectNotFound: (OI:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

EDIT - the above command does work correctly from a command prompt. It looks like my inability to use the inheritance parameter is why the rights are showing up as Special.

The error indicates that PowerShell is trying to parse the command, rather than sending it as-is to Cmd.exe. That’s not unusual. See http://edgylogic.com/blog/powershell-and-external-commands-done-right/ for background (especially the v3 section), but you probably want to run…

&"icacls.exe" --% ...everything...else...

The --% will tell PowerShell to knock it off with the remainder of the command, and just pass it along as-is.

Yeah, that works as long as everything is hard-coded. I want to replace the specified username ‘last6fm’ with a variable $Name. As I suspected and my tests showed, the variable value doesn’t getting passed along to icacls.

This is what frustrates me more than anything else. I suspect I have the logic down, it’s the syntax that drives me nuts, especially trying to make external, non-cmdlet pieces fit together.

Check out that article. You’ll have to start getting fancy with double quotes, and seeing if you can get the right combination to get it parsed and passed.

I started with the article, but I can’t find any use of both --% and a variable.

Also, if you need to reference PowerShell variables, you can't use this trick either. Read on.

but darned if I’ve found it yet. I’ll keep digging. Thanks.

Could you give an example of what you want the file rights to be, along with it’s inheritance and propagation rights?

OI is probably causing “special” permissions because it’s for sub folders and files only, instead of this folder and decedents.
CI is asking to inherit any propagated rights from above, so this could be tweaking what the final right set it as well.

I typically end up giving the folder itself some kind of special rights, to stop users from being able to move or modify it directly, while still having control of all the items within it properly.

Sure. I want to give the user the same rights he would get if I used the GUI and clicked the Allow:Modify box. That results in the following boxes getting checked: Modify, Read & Execute, List folder contents, Read, and Write.

This command does exactly what I want, once Don pointed out the & and --% requirements.

PS I:\> &icacls --% e:\users\last6fm /grant domain.com\last6fm:(oi)(ci)m

But I want to replace last6fm with a variable $Name. The exact rights aren’t the issue; I don’t have any problems from a command prompt. It’s getting PS to hand off all the parameters to icacls. I’d find something to copy and paste and just move on, but it looks like there’s something important I need to learn from this about using resources external to PS. There’s got to be some logic here somewhere, but most of what I’ve found makes it look like trial and error.

Thanks.

ok, what about this.

&icacls e:\users\$($Name) /grant domain.com\$($Name):"(oi)(ci)m"

That works. Can you tell me how? What’s with

$($Name)

and
:“(oi)(ci)m”
?