Wednesday, September 26, 2012

How to remotely modify Windows ACL using Powershell

I have been spending a few hours working on a permission configuration issue on remote Windows systems (NT4, 2000 and 2003). The aim of my script was to modify the existing permission on a file on remote systems, as well as setting the ownership for this same file. Obviously I decided to use the cmdlet that Powershell kindly offers (gcm -noun acl), but there are only two:
CommandType     Name
-----------     ----
Cmdlet          Get-Acl
Cmdlet          Set-Acl
At first sight I was sure that I was going to have problems with setting file ownership on a remote system because none of these two commands reference it. And I was right. Powershell doesn't allow to change file ownership on a remote file system (as explained here). I even tried to perform this operation with cacls and icacls, but I had no better luck and lot of errors (such as "no SID for trustee" and "Error Bad trustee name/SID no lookup").

As I do most of the times when I want to run something remotely and it doesn't work with native tools/cmdlets, I pulled from my magic hat the old good 'psexec.exe' in conjunction with 'filelacl.exe' (whose project has been recently abandoned by Microsoft, as you can see here, so hurry-up and store a copy of this program in safe place!).

My final script is a combination of pure Powershell cmdlets and freshly-mixed psexec and fileacl statements:
$remotehost = "test_host"
$username1 = "my_username"
$paswd = "my password"
$path = "\\$remotehost\c$\folder\file.txt"
$acl = Get-Acl $path
#Going to add my username with Full Control
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule("$username1","FullControl","Allow")
$acl.AddAccessRule($rule)
#Going to assing readandexecute ot Everyone group
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule("everyone","readandexecute","Allow")
$acl.AddAccessRule($rule)
#Going to remove permissions for the local Administrator group
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule("administrators","FullControl","Allow")
$acl.RemoveAccessRule($rule)
Set-Acl $path $acl
#Going to set the remote owner for this file
psexec -c -u $username1 -p $paswd \\$remotehost fileacl.exe "$path" /O "$username1"
#Let's check everything's been properly set
Get-Acl $path | Format-List
I know it doesn't look good but that's the best I could do to solve my issue. It works this way:
  • first of all I define a few variables, as the remote host name on which to work
  • I retrieve the current ACL of the remote file
  • I add two access rule: one for my username and one for the Everyone group
  • I also add another access rule to remove the permission for the local admin group
  • I apply these access rule with Set-Acl
  • I start a psexec session on the remote host and fire fileacl to set the ownership 
  • I dump the applied permissions with Get-Acl to check everything went well
Possible values for rights that you can assign under System.Security.AccessControl.FileSystemAccessRule are listed below. Just choose the one that applies to your situation:
  • AppendData
  • ChangePermissions
  • CreateDirectories
  • CreateFiles
  • Delete
  • DeleteSubdirectoriesAndFiles
  • ExecuteFile
  • FullControl
  • ListDirectory
  • Modify
  • Read
  • ReadAndExecute
  • ReadAttributes
  • ReadData
  • ReadExtendedAttributes
  • ReadPermissions
  • Synchronize
  • TakeOwnership
  • Traverse
  • Write
  • WriteAttributes
  • WriteData
  • WriteExtendedAttributes
I hope this simple script will be useful for those who, like me, didn't find a way to write file ownership on remote Master File Tables... be it under Windows NT, Windows 2000 or Windows 2003. Maybe the Scripting Guy can intervene and post a better solution!

3 comments:

  1. $file=' \\System1\test\test.log'
    $acl=get-acl $file
    $owner=[System.Security.Principal.NTAccount]'MyDom\user01'
    $acl.SetOwner($owner)
    set-acl -Path $file -AclObject $acl

    ReplyDelete
  2. Thanks for the code. Anyway it doesn't work on my system, probably because the user I want to declare as owner of the remote file is a remote local user, not a domain user.

    When I run the code:

    $full_username = "$remote_hostname" + "\" + "$remote_username"
    $owner=[System.Security.Principal.NTAccount]$full_username
    $acl.SetOwner($owner)
    set-acl -Path $path -AclObject $acl

    I get the follwoing translation error:

    Exception calling "SetOwner" with "1" argument(s): "Some or all identity references could not be translated."

    I am pretty sure the problem is due to the Powershell trying to resolve the SID for $remote_username locally and not remotely. Am I wrong?

    Any other suggestion?

    ReplyDelete
  3. Thanks for this post, it helped.

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...