Reset file/folder permissions & Set new permissions - powershell

Trying to use powershell to reset permissions on files/folders copied over from a Linux machine.
Structure looks similar to this
E:\Parent Folder - No inheritance - Group based permissions
|
|
Folder01 - No inheritance
|
|
Subfolders and file - Group based permissions, inheritance
Folder02 - No inheritance
|
|
Subfolders and file - Group based permissions, inheritance
Folder03 - No inheritance
|
|
Subfolders and file - Group based permissions, inheritance
Folder04 - No inheritance
|
|
User Folder - User based permissions, No Inheritance
|
|
Inheritance
The script currently first runs the takeown command, followed by the icacls.
I then loop through the first level of folders to disable the inheritance.
Then the permissions are applied to the folders.
Here is a sample of what I have. It just repeats itself for each specific permission I want to set.
Get-ChildItem -Path "$solidPath" -ErrorAction SilentlyContinue | ForEach-Object {
$Item = $_.FullName
If ( "$Item" -eq "E:\ParentFolder\Folder04" -or "$Item" -eq "E:\ParentFolder\Folder03" )
{
Write-Host "Do Not Touch" -ForegroundColor DarkRed
}
Else
{
$colRightsAdmin = [System.Security.AccessControl.FileSystemRights]"FullControl"
$InheritanceFlagAdmin = [System.Security.AccessControl.InheritanceFlags]::ContainerInHerit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInHerit
$PropagationFlagAdmin = [System.Security.AccessControl.PropagationFlags]::None
$objTypeAdmin = [System.Security.AccessControl.AccessControlType]::Allow
$objUserAdmin = New-Object System.Security.Principal.NTAccount("BUILTIN\Administrators")
$objACEAdmin = New-Object System.Security.AccessControl.FileSystemAccessRule($objUserAdmin, $colRightsAdmin, $objTypeAdmin)
$objACLAdmin = Get-ACL "$Item"
$objACLAdmin.AddAccessRule($objACEAdmin)
Set-ACL "$Item" $objACLAdmin
I am using takeown + icacls to try and reset all the permissions. This initially worked well because the inheritance flags get reset as well.
I then use the method described in the link to set the appropriate permissions and inheritance on my folders and let the inheritance do the rest - Powershell & .net
The script worked on my test directory structure. But after coping the folders and files over I am getting a permission denied message when the icacls command runs (the takeown command runs without issue). The rest of the script then fails.
I know that I can use the RemoveAccessRule($objACE) to remove permissions from objects, but I haven't been able to figure out how to do it for all the user permissions that are defined.
I would like to start the folders and files off with a clean slate, and then apply permissions. All the examples I have found only show how to remove permissions for a specific user.
Where am I going wrong with the initial permission cleanup?

I ended up going with a bit of messy solution.
After I had a synced copy of the data from the Linux server I used robocopy to "move" all the data from one folder to another. This completely reset all the permissions on all the files.
After having done that I learned a little bit more about setting permissions and realized that I had been making a mistake all along.
I was aimlessly using this object to remove permissions
$acl.RemoveAccessRule($ace) | Out-Null
This was actually removing all privilege on the file before I had a chance to set any other permissions. So I was removing my ability to adjust permissions.
Once I realized this I used a combination of the properties IdentityReference and IsInherited to selectively target which permissions I was going to remove.
The script is big and ugly, but seems to work.

Related

Grant permissions to a folder

How (which command) can I use to grant permissions (full_control) to a user (service_account) on a folder using PowerShell?
Thank you!
I was trying to use icacls in my PowerShell but it's not working.
There are several ways to do this. If you don't want to install a module as James suggests above then:
# Get current access permissions from folder and store in object
$Access = Get-Acl -Path $FolderPath
# Create new object with required new permissions
$NewRule = New-Object System.Security.AccessControl.FileSystemAccessRule("MyDomain\MyUserOrGroup","FullControl","ContainerInherit,ObjectInherit","None","Allow")
# Add new rule to our copy of the current rules
$Access.AddAccessRule($NewRule)
# Apply our new rule object to destination folder
Set-Acl -Path $FolderPath -AclObject $Access -ErrorAction Stop
As James mentions though, using ACLs in Powershell without a module, whilst powerful, is also a pain. I only do it when I'm sharing scripts (so that there isn't a dependency on the module).
I would recommend using the NTFSSecurity Powershell module for setting the permissions as it's much easier to use (and understand) than acls!
To add permissions for a user is just one command.
I've shown two examples for both network paths/domain account and local folder/local user. They can be mixed in any way you can set via the GUI.
Add-NTFSAccess -Path "\\sdk\data\SHAREDIR\$NAME" -Account "Domain\K_NV_TEST_R" -AccessRights FullControl
Add-NTFSAccess -Path "C:\folder\subfolder" -Account "LocalComputerName\LocalUserName" -AccessRights FullControl

Remove NTFS permissions of a user in all subdirectories

I am writing a script which would delete a specific user if the account is older than 7 days.
But when the user is deleted the NTFS permissions on my file server remain.
How can I delete all the permission for a specific user with PowerShell?
You should never grant permissions to individual users (with the exception of home directories and user profiles). As you can see for yourself it's a mess to clean up. Always create groups representing the particular functions/roles that require access, and grant permissions to those groups.
You can clean up the permissions via icacls:
icacls C:\root\folder /remove DOMAIN\user /t /c
Note, however, that you MUST do this before deleting the account, because for some reason icacls can't clean up SIDs of deleted accounts.
If you have already deleted the account you can try to fix permissions with Get-Acl and Set-Acl:
Get-ChildItem C:\root\folder -Recurse -Force | ForEach-Object {
$acl = Get-Acl -LiteralPath $_.FullName
$ace = $acl.Access | Where-Object { $_.IdentityReference -like 'S-1-5-*' }
$acl.RemoveAccessRule($ace) | Out-Null
Set-Acl -LiteralPath $_.FullName -AclObject $acl
}
Note that you may need to adjust the condition for selecting the ACE to remove from the file or folder's ACL.
Note also, that the above will fail for files/folders where the owner isn't either the user running the code or one of his groups. In a situation like that you can use tools like subinacl or SetACL as a last resort, as described in the answers to this question on ServerFault.

Powershell: NTFS permissions and Parent Folders -pathtoolong issues

I apologize in advance of the long post. I have spent a significant amount of time trying to find an answer or piece together a solution to this problem.
It's a simple request: Add a user/group with 2 sets of permissions to your entire DFS environment applying one set to folders and sub-folders and the other to files only.
Seems easy enough, however in the environment I'm trying to manage we have 1000's of folder paths greater than 260 characters deep and any use of dir -recurse or get-childitem will result in hitting the error "pathtoolong". Every example solution for this problem has used a variation of the above or relies on "get-childitem". This fails for most real world situations as I believe many of us IT admins are faced with long paths due to the nature of DFS use.
The current attempt:
Currently I'm using a custom module "NTFSSecurity" which is fantastic to apply NTFS permissions. It works great!
It can be found here: https://ntfssecurity.codeplex.com/
A tutorial from here: https://blogs.technet.microsoft.com/fieldcoding/2014/12/05/ntfssecurity-tutorial-1-getting-adding-and-removing-permissions/
The problem found in the above tutorial and every other example I've been able to find, it references commands such as:
dir -Recurse | Get-NTFSAccess -Account $account
This will fail in the real world of super long file paths.
The "PathTooLong" error workaround:
My workaround current consists of using Robocopy to export the file paths to a text file. I found this as a recommendation from someone dealing with a similar problem. Robocopy will not error on "pathtoolong" issues and is perfect for this exercise. I then try and run commands against the text file containing all of the paths I need to modify.
The command for the Robocopy is this:
robocopy '<insert source path here>' NULL /NJH /E /COPYALL /XF *.* | Out-File -FilePath '<path to fileout.txt>'
This will create a text file while copying only folder structure and permissions. Excellent!
You will then have to clean up the text file from additional characters which I use:
$filepath = '<path>\DFS_Folder_Structure.txt'
$structure = Get-Content $filepath
$structure -replace ' New Dir 0 '| Out-File -FilePath \\<path_you_want_file>\DFS_Folder_Structure2.txt
I also reversed the contents of the text file so it shows the furthest child object (folder) and work down. I thought this might be easier for identifying a parent folder or some other recursive logic which I haven't been able to figure out.
To reverse text from bottom to top use this command here:
$x = Get-Content -Path 'C:\temp_dfs\DFS_Folder_Structure2.txt'; Set-Content -Path 'C:\temp_dfs\Reversed_data.txt' -Value ($x[($x.Length-1)..0])
This script currently only applies to paths with Inheritance off or for childobjects with Inheritance off. This is taken from the NTFSSecurity module command Get-NTFSInheritance which will return results for AccessInheritance and AuditInheritance.Access is if the folder is inheriting from a parent above. Audit is if the folder is passing it down to child objects.
There are 4 possibilities:
AccessInheritance True AuditInheritance True
AccessInheritance True AuditInheritance False
AccessInheritance False AuditInheritance True
AccessInheritance False AuditInheritance False
(*Special note: I have seen all 4 show up in the DFS structure I'm dealing with.)
Script to Set Permissions based on file path contained in text file:
#Get File content to evaluate
$path = Get-Content 'C:\temp_dfs\Reversed_data.txt'
$ADaccount = '<insert fully qualified domain\user or group etc.>'
Foreach ($line in $path)
{
#Get-NTFSAccess -Path $line -Account $ADaccount | Remove-NTFSAccess
#This command will find the access of an account and then remove it.
#It has been omitted but included in case needed later.
$result = Get-NTFSInheritance -Path $line
If ($result.AccessInheritanceEnabled -Match "False" -and $result.AuditInheritanceEnabled -match "False")
{
Add-NTFSAccess -Path $line -Account $ADaccount -AccessRights Traverse,ExecuteFile,ListDirectory,ReadData,ReadAttributes,ReadExtendedAttributes,ReadPermissions -AppliesTo ThisFolderAndSubfolders
Add-NTFSAccess -Path $line -Account $ADaccount -AccessRights ReadAttributes,ReadExtendedAttributes,ReadPermissions -AppliesTo FilesOnly
}
If ($result.AccessInheritanceEnabled -Match "False" -and $result.AuditInheritanceEnabled -Match "True")
{
Add-NTFSAccess -Path $line -Account $ADaccount -AccessRights Traverse,ExecuteFile,ListDirectory,ReadData,ReadAttributes,ReadExtendedAttributes,ReadPermissions -AppliesTo ThisFolderAndSubfolders
Add-NTFSAccess -Path $line -Account $ADaccount -AccessRights ReadAttributes,ReadExtendedAttributes,ReadPermissions -AppliesTo FilesOnly
}
If ($result.AccessInheritanceEnabled -Match "True" -and $result.AuditInheritanceEnabled -Match "False")
{
continue
}
If ($result.AccessInheritanceEnabled -Match "True" -and $result.AuditInheritanceEnabled -Match "True")
{
continue
}
}
This script will apply permissions for the specified User/Group account and set permissions for Folder and Sub-folders and then add another set of permissions to Files only.
Now this current fix works great except it only touches folders with Inheritance turned off. This means you'd need to run this script and then set permissions on the "main parent folder". This is completely do-able and may be the best method to avoid double entries of permissions and is the current state of my solution.
If you add criteria to the bottom sections where AccessInheritanceEnable = True and Audit = True you will get double entries because you're applying permissions to both the parent --> which pushes its permissions to the child-objects and also explicitly on the child-objects themselves. This is due to the text file contains both parent and child and I haven't figure out a way to address that. This isn't "horrible" but my OCD doesn't like to have double permissions added if it can be avoided.
The real question:
What I'd really like to do is somehow identify parent folders, compare them to parents further up the tree and see if it was inheriting permissions and only apply the permission set to the highest parent in a specific chain. My mind wants to explode thinking about how you would compare parents and find the "highest parent".
Again the problem being anytime you want to -recurse the folder structure it will fail due to "pathtoolong" issues so it needs to be contained to logic applied to the text file paths. I've seen a bit mentioned about split-path but I don't really understand how that's applied or how you could compare a path to another path until you identified a parent path.
Thank-you for taking the time to read this long post and question. If you're still awake now, I'm open to any suggestions. lol.
the NTFSSecurity module is indeed fantastic.
I used it to make a script that can export ntfs security of a UNC path and it's subfolders to a readable excel file.
It can be found on:
https://github.com/tgoetheyn/Export-NtfsSecurity
I use is frequently and didn't had any problem with long filenames.
Hope you like it.
PS: If you add ntfssecurity, don't forget to include the "Synchronize" permission. If not included, strange things can happen.

Removing just one inherit permission using PowerShell

I'm trying to write a script that can remove access rights for just one (e.g. Everyone) on folders that have inherited permissions in place.
The other inherit permissions should stay intact. I can remove the inherit permissions and then remove access for that group, but inheritance is then broken. I don't want to enable inheritance after this action because of subfolders having no inheritance being broken.
How do I just remove this group without messing with the rest of the permissions?
You cannot (by design) remove an inherited permission, "without messing with the rest of the permissions".
What you can do is
Disallow inheritance, but preserve already inherited rules
Remove/modify the EVERYONE ACE after removing inheritance
Like this:
$FilePath = "C:\parentFolder\childItem.ext"
$FileACL = Get-Acl $FilePath
# Remove inheritance but preserve existing entries
$FileACL.SetAccessRuleProtection($true,$true)
Set-Acl $FilePath -AclObject $FileACL
# Retrieve new explicit set of permissions
$FileACL = Get-Acl $FilePath
# Retrieve "everyone" rule
$EveryoneRule = $FileACL.GetAccessRules($true,$true,[System.Security.Principal.NTAccount]) | Where-Object {$_.IdentityReference -eq [System.Security.Principal.NTAccount]"EVERYONE"}
# Remove it - or modify it and use SetAccessRule() instead
$FileACL.RemoveAccessRule($EveryoneRule)
# Set ACL on file again
Set-Acl $FilePath -AclObject $FileACL
To remove groups or users ACE without disabling inheritance, use CACLS folder /E /R group/user. I know that CACLS is deprecated but I have not found any equivalent when using iCacls or SETACL.
Actually you don't have to delete inheritance.
It is possible to just remove this one little mistake. Had the same error and it was successful on a Windows 2016 Fileserver.
I modified the Script from Mathias R. Jessen a bit. If you want to do this just to one Folder, replace "$folders = Get-Childitem" with "$filepath = Get-Item" and only use the commands inside the foreach loop.
Open Powershell as Admin
$folders = Get-ChildItem "C:\Path\To\Folder" | where {$_.psiscontainer -eq $true}
foreach ($FilePath in $folders)
{
$FileACL = Get-Acl $FilePath.FullName
$EveryoneRule = $FileACL.GetAccessRules($true,$true,[System.Security.Principal.NTAccount]) | Where-Object {$_.AccessControlType -eq "Deny"}
$FileACL.RemoveAccessRule($EveryoneRule)
Set-Acl $FilePath.FullName -AclObject $FileACL
}

On which system is get-acl resolved?

I've inherited a Powershell script that a remote customer uses to recursively search for directories and exports (to csv) multiple ACL values including Path, Owner, FileSystemRights, IdentifyReference, and AccessControlType. The script works great, but I am curious as to how the flow actually takes place. Below is partial script to show code relevant to my question below.
//Partial script begin:
get-childitem $rootdir -recurse | where-object {$_.psIscontainer -eq $true} | foreach-object {
$a = ($_.Fullname)
$b = (get-acl $_.Fullname).Owner
$c = (get-acl $_.Fullname).Access
foreach ($c1 in $c) {
$d = $c1.FileSystemRights
$e = $c1.AccessControlType
//Partial script end.
To my question: If running this script on a remote system, using admin privileges and variable $rootdir = \\someshare, on which system does the get-acl get resolved...on the system hosting the folder structure, or the remote system running the PS script and mapped to the share folder?
Thanks.
// My original question may have been a bit nebulous, so hopefully I can clarify a bit. By using get-acl on a remote system and mapped to a server share folder, will invoking get-acl cause any resource hit on the server during the ACL resolution process...disk I/O, memory, CPU. I am not a programmer, so please bear with me as I try to formulate my question properly.
Assuming that you have all authentication correctly setup (you would run into a double-hop auth problem if i understand your plan correctly) the call to Get-Acl would be executed on the system the script is run on.
From the technet article on the Get-ACL cmdlet
The Get-Acl cmdlet enables you to retrieve the security descriptor
(access control list) for a file, a folder, or even a registry key
It retrieves NTFS persmission for any folder specified, including remote folders.
In your case, it would run from the machine the script is running from, and authenticate to the remote machine using the credentials supplied to retrieve the ACL