Setting file permissions on a remote machine with PowerShell

Recently I needed to set some file permissions on a remote machine. Previously I’d done this relatively easily through a share as the user account I was using also had administrator rights on the other side and I was dealing with domain accounts. However, this did not work for a user that was local to the remote machine.

So, I creates a small PowerShell function to remotely set the user to a local (or any domain) account. (This also works for virtual accounts like IIS AppPool/ users)

function Add-RemoteAcl
(
    [string]$computerName,
    [string]$directory,
    [string]$user,
    [string]$permission
)
{
    $session = New-PSSession -ComputerName $computerName;
    Invoke-Command -Session $session -Args $directory, $user, $permission -ScriptBlock {
        param([string]$directory,[string]$user,[string]$permission)
        $acl = Get-Acl $directory;
        $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($user, $permission, "ContainerInherit, ObjectInherit", "None", "Allow");
        if ($accessRule -eq $null){
            Throw "Unable to create the Access Rule giving $permission permission to $user on $directory";
        }
        $acl.AddAccessRule($accessRule)
        Set-Acl -aclobject $acl $directory
    };
    Remove-PSSession $session;
}

To run the PowerShell remotely, first of all, I create a new PowerShell session on the remote machine with New-PSSession, then I run a script in that session with Invoke-Command, and finally I clean up with Remove-PSSession to end the remote session.

Bear in mind that you will need the appropriate permissions on the remote machine for whatever actions you want to take.

Invoke-Command

This is where all the work is done. You can pass a session to Invoke-Command, and you can also pass an ArgumentList to pass in to the command. This gives it some fantastic abilities.

Be aware that variables that exist outside the script block are not visible within the script block, you have to pass them as an ArgumentList (alias Args), and the script block has to pick them up. Hence the code above starts the script block with a params section in order to pick up the values passed as the Args.

Setting the file permissions

In order to add new rules to an ACL you have to Get-Acl to get the existing set of rules, create the new FileSystemAccessRule for the permission you want to grant, then AddAccessRule to the ACL you retrieved, and finally Set-Acl to persist the addition.

If you were just to create the new rule and set that, then all the existing rules would be replaced with the one rule that was just created.

Taking ownership of a file

I’m currently looking at using IIS Administration as a way to automate some deployment tasks. However, the way it got installed, it’s appsettings.json file could not be written to, even when running the text editor as Administrator.

It turns out, SYSTEM had full control of the file, and the installer configured it to only allow me to access the ReST API, yet I needed a deployment script running from the Continuous Delivery server to be able to access IIS Administration, so I needed to modify the settings file, somehow.

To take ownership – The quick guide

So, to take ownership of the appsettings.json file, what I needed was to run two commands at command prompt running as Administrator.

takeown /f "appsettings.json" /a

icacls "appsettings.json" /grant administrators:F /c /l

TAKEOWN

/f [filename] : Specifies the file or directory name, can contain wildcards.

/a : Optional, gives the ownership to the Administrators group, rather than the current user.

 

ICACLS

/grant [sid]:[permission] : Where sid is the name of the user or group, and [permission] is the permission set, in this case F for “full access”

/C : Indicates to continue on error

/L : Indicates the operation will run on the symbolic link itself, rather than the target of the link.