Your ads will be inserted here by

Easy AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

PowerShell LogoThis is every file server admin’s nightmare: hundreds of shares, thousands of folders, hundreds of thousands of files – and custom or not inherited rights on many of them. Terabytes of data that need auditing – e.g. to find customer data, or credit card information. How do you go about accessing all the data in all the trees? What about backups failing because someone removed the System account? Of course you can seize control of the folder by taking ownership and pushing down from a top level – but how do you preserve the existing Access Control Lists?

Microsoft gives us 2 tools for doing this, Takeown.exe and ICACLs.exe - but there is a catch. To use takeown.exe to to take ownership of a tree you can use the /R recurse option, but you then have to specify a default answer – yes or no. The question that is asked is: “you do not have permission to take ownership, do you want to?” This will strip out existing permissions!!! If you answer yes, you delete the permissions you wish to preserve. If you answer no you do not take ownership of the folder. Without the recurse option you can take ownership of an individual file or folder, but of course this needs to be run as many times as there are folders to be sure.

Engaging with Microsoft product support for this gave us a solution of sorts – namely to run a looped batch file until all the folders were owned, and then running ICACLS.exe. There would be no way of knowing whether this had completed the tree unless you knew already the number of files and folders within the tree – and if you could get that info you would already have permissions! Not a great solution.

Fortunately, using PowerShell and Takeown.exe, we can work around this limitation, by exploiting Get-ChildItem in recursive mode, and changing the default error action to SilentlyContinue. Get-ChildItem will throw an error if it tries to access a folder you do not have permissions to, and you can catch this error and pass the folder to Takeown.exe to seize ownership. Trapping this error does not work because it’s a “stop” error – but changing the error action means that the $error variable is populated with the exception details. This is the basis of the first function in my script.

Function: Test-Folder

function Test-Folder($FolderToTest){
$error.Clear()
$ErrorArray = @()
Get-ChildItem $FolderToTest -Recurse -ErrorAction SilentlyContinue | Select FullName
if ($error) {
$ErrorArray = $error + $ErrorArray
foreach ($err in $ErrorArray) {
if($err.FullyQualifiedErrorId -eq "DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand") {
    Write-Host Unable to access $err.TargetObject -Fore Red
    Write-Host Attempting to take ownership of $err.TargetObject -Fore Yellow
    Take-Ownership($err.TargetObject)
    Test-Folder($err.TargetObject)
   }
  }
 }
}

* This section has been updated as per David’s comments and solution below – thanks to David for his work!

It’s fairly simple code, running through it we clear any existing errors using $error.Clear(), recurse through the folder structure using Get-ChildItem with the errors suppressed. The select statement is for logging output – the full path of the file or folder that we DO have access to. We then check if $error is set – if it is then we had errors accessing a file or folder, so we loop through them and check if the error Fully Qualified ID matches the access denied error message. If it does we write something for the logging and call the Take-Ownership function, followed by looping back with a call to itself to re-test the folder we are working on (and anything below it).

Function: Take-Ownership

Your ads will be inserted here by

Easy AdSense.

Please go to the plugin admin page to
Paste your ad code OR
Suppress this ad slot.

  The Take-Ownership function simply calls Takeown.exe against the folder it is passed, then adds entries to the ACL for that folder.

function Take-Ownership {
 param(
  [String]$Folder
 )
 takeown.exe /A /F $Folder
 $CurrentACL = Get-Acl $Folder
 write-host ...Adding NT Authority\SYSTEM to $Folder -Fore Yellow
 $SystemACLPermission = "NT AUTHORITY\SYSTEM","FullControl","ContainerInherit,ObjectInherit","None","Allow"
 $SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $SystemACLPermission
 $CurrentACL.AddAccessRule($SystemAccessRule)
 write-host ...Adding Infrastructure Services to $Folder -Fore Yellow
 $AdminACLPermission = "DEFINIT\AdminGroup","FullControl","ContainerInherit,ObjectInherit","None","Allow"
 $SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $AdminACLPermission
 $CurrentACL.AddAccessRule($SystemAccessRule)
 Set-Acl -Path $Folder -AclObject $CurrentACL
}

Running through, it takes the folder name as a parameter, runs Takeown.exe against it using the /A option to add the administrators group. We then get the current ACL from the folder, build a new ACL permission as a string and use that to create a FileSystemAccessRule object. That object is then added to the ACL we copied from the folder. In this example I am adding two accounts, NT AUTHORITY\SYSTEM and a domian group DEFINIT\AdminGroup. The Set-ACL command applies the ACL list with the two new entries to the folder.

The final script

Finally, we can build the script together taking a parameter for the target folder, and a log file. The Functions are called against the target folder – first taking ownership of the root, and then testing the contents. I start and stop a transcript around them to capture output, which can be crucial for proving that you have access!

Start-Transcript $Log
Take-OwnerShip ($RootPath)
Test-Folder($RootPath)
Stop-Transcript

 Download the whole script as a zip file here: set-ownerrecursively

 

Creative Commons License
PowerShell: Recursively taking ownership of files and folders and adding permissions without removing existing permissions by DefinIT, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

Tagged on:                                 

19 thoughts on “PowerShell: Recursively taking ownership of files and folders and adding permissions without removing existing permissions

  • 23/05/2012 at 11:24 pm
    Permalink

    Hello, I’ve been trying to use the code you have listed and running into an issue…

    It seems to occur when the recursion is “returning” from the deepest function back up one… if I run it again, it gets further, (speficially the deepest level )…

    If I keep running it over and over again, it eventually works itself through the entire folder structure.

    I don’t know where the $error array is being modified that could cause this issue…

    Example:

    .\set-ownerrecursively-orig.ps1 I:\Homeshare\patrick C:\Log.txt

    I:\Homeshare\patrick\Folder1\
    I:\Homeshare\patrick\Folder2\
    I:\Homeshare\patrick\Folder3\
    I:\Homeshare\patrick\somefile.xls
    I:\Homeshare\patrick\somefile.doc
    I:\Homeshare\patrick\Folder3\2009\
    I:\Homeshare\patrick\Folder3\2010\
    I:\Homeshare\patrick\Folder3\3folder-somefile.xls
    I:\Homeshare\patrick\Folder3\3folder-somefile.doc
    I:\Homeshare\patrick\Folder3\2010\2010-file1.xls
    I:\Homeshare\patrick\Folder3\2010\2010-file2.xls

    An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..
    At F:\Server Installs\set-ownerrecursively-orig.ps1:32 char:10
    + foreach <<<< ($err in $error) {
    + CategoryInfo : InvalidOperation: (System.Collecti…numeratorSimple:ArrayListEnumeratorSimple) [], RuntimeException
    + FullyQualifiedErrorId : BadEnumeration

    When I run it again on the same folder, it gets a bit futher…

    .\set-ownerrecursively-orig.ps1 I:\Homeshare\patrick C:\Log.txt

    I:\Homeshare\patrick\Folder1\
    I:\Homeshare\patrick\Folder2\
    I:\Homeshare\patrick\Folder3\
    I:\Homeshare\patrick\somefile.xls
    I:\Homeshare\patrick\somefile.doc
    I:\Homeshare\patrick\Folder3\2009\
    I:\Homeshare\patrick\Folder3\2010\
    I:\Homeshare\patrick\Folder3\3folder-somefile.xls
    I:\Homeshare\patrick\Folder3\3folder-somefile.doc
    I:\Homeshare\patrick\Folder3\2010\2010-file1.xls
    I:\Homeshare\patrick\Folder3\2010\2010-file2.xls
    I:\Homeshare\patrick\Folder3\2009\2010-file1.xls
    I:\Homeshare\patrick\Folder3\2009\2010-file2.xls

    An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..
    At F:\Server Installs\set-ownerrecursively-orig.ps1:32 char:10
    + foreach <<<< ($err in $error) {
    + CategoryInfo : InvalidOperation: (System.Collecti…numeratorSimple:ArrayListEnumeratorSimple) [], RuntimeException
    + FullyQualifiedErrorId : BadEnumeration

    Reply
  • 24/05/2012 at 6:22 pm
    Permalink

    I’ve managed to figure out the issue. The $error is a global array, if it’s cleared the foreach loop looses the ability to enumerate the next item. Copying it to a new array and adding the two arrays together fixed the issue:

    function Test-Folder($FolderToTest){
    # Clear lobal array of errors
    $error.Clear()

    # Create an empty array
    $ErrorArray = @()
    # Attempt to traverse all folders, showing full file names and continue on hard errors
    Get-ChildItem $FolderToTest -Recurse -ErrorAction SilentlyContinue | Select FullName
    if ($error) {

    # If an error has occurred, copy the error array to another array to preserve the current list of access denieds
    $ErrorArray = $error + $ErrorArray

    # For each folder which we don’t have access to perform the steps
    foreach ($err in $ErrorArray) {

    - rest of it is the same

    Reply
    • 28/05/2012 at 9:20 am
      Permalink

      Thanks for the update David, I will amend the script above to include those changes.

      Sam

      Reply
  • 20/08/2012 at 3:43 pm
    Permalink

    Thanks for this, saved me hours of work.

    Only addition I found of use was that i added -force to the get-childitem.

    Get-ChildItem $FolderToTest -Recurse -force -ErrorAction SilentlyContinue | Select FullName

    This ensured it took ownership of “My Pictures”, My Music” and recycler on first run (old network home areas being removed).

    Hope this helps (could be a complete red herring ??)

    Reply
  • 22/11/2012 at 5:41 pm
    Permalink

    What if the user has recursively removed the SYSTEM account from his “my documents” permissions? It is not possible to gain access, even if I run the script with psexec as system. What can I do? :s

    Reply
    • 23/11/2012 at 9:29 am
      Permalink

      Depends on the politics of the situation, I’d talk to the user and re-add SYSTEM under their credentials – that’s by far the simplest way and maybe they’ll learn something – after that I’d remove the right to modify permissions!

      So long as you have permissions to the folder beneath it you can manually seize ownership of the folder and everything underneath – even with SYSTEM removed.

      Reply
  • 12/12/2012 at 12:22 am
    Permalink

    On my system (Server 2012) I cannot get the code running without errors:

    After taking successfully ownership of the root, I get an “attempted to perform an unauthorized operation” from set-acl.
    -> permission denied
    Then the code cycles endless on the root folder…

    What am I doing wrong here? The ownership taking is successful. In GUI I can add an account to acl, but even when I select “only to this folder”, I get an error from the childs trying to take over the acl and can’t (permission denied logically because I do not own them). After canceling all windows I have the new acl on the folder.

    Any ideas?

    Reply
  • 28/01/2013 at 3:17 pm
    Permalink

    I have the same problem like Ulli in Windows Server 2008 SP2. My user is member of the local Group of Administrators. But when I try to open the folder in explorer I get an Error like: “You have no permission to access this folder…”
    When I then start the script, I get the same error like Ulli “attempted to perform an unauthorized operation” and the script cycles in an endless loop.

    F1

    Reply
    • 29/01/2013 at 9:23 am
      Permalink

      I haven’t looked at the script in Server 2012, but I have used it extensively on 2008 and have not had the issue – my guess would be something to do with UAC? If you are running UAC enabled then perhaps try it disabled for a brief period to verify that it’s not that? If you can post an example of a folder structure and permissions that are causing the error I can see if I can replicate it.

      Sam

      Reply
  • 27/02/2013 at 2:40 pm
    Permalink

    I Slightly modified your script…
    Fact is that the scope I ran onto had an enormous bunch of folders & Subfolders with dito files.
    Your script could not take the whole scope…
    For that i recreated a loop…

    Function main
    {
    param ([Parameter(Mandatory=$true, Position=0)][String]$RootPath,
    [Parameter(Mandatory=$true, Position=0)][string]$Log)

    function Take-Ownership {
    param(
    [String]$Folder
    )
    takeown.exe /A /F $Folder /R /D n
    $CurrentACL = Get-Acl $Folder
    write-host …Adding Enterprise Storage Management to $Folder -Fore Yellow
    $SystemACLPermission = “DefinIT\GroupToAdmin”,”FullControl”,”ContainerInherit,ObjectInherit”,”None”,”Allow”
    $SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $SystemACLPermission
    $CurrentACL.AddAccessRule($SystemAccessRule)
    write-host …Adding Domain Admins to $Folder -Fore Yellow
    $AdminACLPermission = “DefinIT\Domain Admins”,”FullControl”,”ContainerInherit,ObjectInherit”,”None”,”Allow”
    $SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $AdminACLPermission
    $CurrentACL.AddAccessRule($SystemAccessRule)
    Set-Acl -Path $Folder -AclObject $CurrentACL
    }

    function Test-Folder($FolderToTest)
    {
    $error.Clear()
    $ErrorArray = @()
    Get-ChildItem $FolderToTest -Recurse -ErrorAction SilentlyContinue | Select FullName
    if ($error)
    {
    $ErrorArray = $error + $ErrorArray
    foreach ($err in $ErrorArray)
    {
    if($err.FullyQualifiedErrorId -eq “DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand”)
    {
    Write-Host Unable to access $err.TargetObject -Fore Red
    Write-Host Attempting to take ownership of $err.TargetObject -Fore Yellow
    Take-Ownership($err.TargetObject) Test-Folder($err.TargetObject)
    }
    }
    }
    }
    Start-Transcript $Log
    $Folderlist= Get-ChildItem $RootPath | ?{ $_.PSIsContainer } | Select-Object FullName | ForEach-Object {$_.FullName} | Out-String -stream | select-object -skip 1
    foreach ($Folder in $Folderlist)
    {
    Take-OwnerShip $Folder
    Test-Folder $Folder
    }
    Stop-Transcript
    }
    main

    Reply
  • 27/02/2013 at 2:42 pm
    Permalink

    sorry, parameter $Log has to be modified
    Top of script should look like this :

    Function main
    {
    param ([Parameter(Mandatory=$true, Position=0)][String]$RootPath,
    [Parameter(Mandatory=$true, Position=1)][string]$Log)

    Reply
    • 27/02/2013 at 3:02 pm
      Permalink

      Thanks for this Ruben, it’s great to have feedback and improvements, I will integrate them into the post.

      Sam

      Reply
  • 26/03/2013 at 8:06 pm
    Permalink

    This script looks like exactly what I need. When deleting an AD account I then need to delete the user dir, roaming profile, and term services profile. I’m usually doing this for 30-40 users at a time.

    Please forgive my ignorance but I’ve just started using powershell. Is there an easy way to pipe a list of folder names via text file to this script?

    Reply
    • 27/03/2013 at 11:09 am
      Permalink

      If you stick the folder names into a CSV file, you can then use something like:

      foreach($Folder in (Import-CSV 'Folders.csv')){
      Take-Ownership($Folder)
      }

      Sam

      Reply
  • 04/04/2013 at 6:08 pm
    Permalink

    I’m a powershell newbie and am receiving the following result:

    Unable to access V:\Users\ACampos\Citrix Docs
    Attempting to take ownership of V:\Users\ACampos\Citrix Docs
    …Adding NT Authority\SYSTEM to V:\Users\ACampos\Citrix Docs
    …Adding AdminGroup to V:\Users\ACampos\Citrix Docs
    Exception calling “AddAccessRule” with “1″ argument(s): “Some or all identity references could not be translated.”
    At C:\library\set-ownerrecursively.ps1:20 char:27
    + $CurrentACL.AddAccessRule <<<< ($SystemAccessRule)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    The only modification I made to the script is as follows:

    $AdminACLPermission= "JS\Administrators","FullControl","ContainerInherit,ObjectInherit","None","Allow"

    Where "JS" is the Active Directory domain to which the file server is a member.

    Here is how I invoked the script:

    ./Set-OwnerRecursively.ps1 "V:\Users\ACampos\Citrix Docs" C:\Library\Log.txt

    Thank you in advance for your help Sam.

    Reply
    • 05/04/2013 at 2:53 pm
      Permalink

      …Adding AdminGroup to V:\Users\ACampos\Citrix Docs
      Exception calling “AddAccessRule” with “1″ argument(s): “Some or all identity references could not be translated.”

      ^^ unless you have a group called “AdminGroup”, this is your problem :)

      You can remove:

      write-host …Adding Infrastructure Services to $Folder -Fore Yellow
      $AdminACLPermission = “DEFINIT\AdminGroup”,”FullControl”,”ContainerInherit,ObjectInherit”,”None”,”Allow”
      $SystemAccessRule = new-object System.Security.AccessControl.FileSystemAccessRule $AdminACLPermission

      Reply
  • 05/04/2013 at 3:47 pm
    Permalink

    Hello Sam,
    Thanks for the response. As I mentioned in my original post, I don’t have such as group called “AdminGroup”; hence the modification:

    $AdminACLPermission= “JS\Administrators”,”FullControl”,”ContainerInherit,ObjectInherit”,”None”,”Allow”

    My goal is for the group JS\Administrators to take full control. Whereby JS is the name of the domain for which the file server is a member. I’m receiving the error message I noted.

    Thanks

    Reply
  • 11/09/2013 at 7:46 am
    Permalink

    Hi Sam,
    thanks for that script. I use it to fix profile folders which have wrong ACLs. Sadly it just work for folder an not for files. The files are not touched, I still have no access to them. Can you help me with that?

    Reply
  • 17/02/2014 at 4:05 pm
    Permalink

    Same as with Dave – This only seems to work for folders when I run it? Files aren’t getting modified. Any thoughts?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>