Hey, Scripting Guy! How Can I Use Windows PowerShell to Determine the Owner of a File?

Hey, Scripting Guy! How Can I Use Windows PowerShell to Determine the Owner of a File?

  • Comments 15
  • Likes
Hey, Scripting Guy! Question

Hey, Scripting Guy! Is it possible to determine the owner of a file using Windows PowerShell?
-- GF

SpacerHey, Scripting Guy! AnswerScript Center

Hey, GF. Well, today is April 15th, which, in the US, can mean only one thing: it’s time to celebrate the birthday of Italian mathematician Pietro Antonio Cataldi, best known for discovering the sixth and seventh Mersenne primes. Pietro, who developed the first notation for continued fractions, was born on this day in 1552. Happy birthday, Pietro!

Coincidentally, April 15th is also Tax Day in the US, the last day on which Americans can submit their income tax returns for the previous year. Needless to say, for many Americans April 15th is a very stressful day. For other Americans, however, April 15th isn’t the least bit stressful; that’s because the US has a long history of people who believe that the government has no right to collect income taxes and therefore decide not to pay their taxes. For example, in 1997 actor Wesley Snipes (recently convicted on three counts of failure to pay income tax) reported an income of $19,238,192. Not only did Wesley decline to pay any taxes on that income, he actually demanded a refund of $7,360,755. Interestingly enough, his own lawyers termed his positions on income tax “kooky,” “crazy” and “dead wrong.”

Which, coincidentally enough, are the exact same phrases that were sprinkled throughout the mid-year performance review of the Scripting Guy who writes this column.

Oh, and did we mention that Wesley Snipes was recently convicted on three counts of failure to pay income tax? That’s usually what happens to people who decline to pay their taxes or file a tax return.

As it turns out, the Scripting Guy who writes this column isn’t stressing out today, either; that’s because he submitted his tax return well in advance of today’s deadline. (On Sunday, April 13th, to be exact.) Admittedly, that might sound like he was cutting it a little close. He wasn’t concerned, however, because he knew he could complete his tax return in less than hour; needless to say, it doesn’t take him anywhere near as long to count his money as it takes Wesley Snipes to count his.

And no, that’s not because the Scripting Guy who writes this column is a really fast counter.

Best of all, getting his taxes done early turned out to have multiple benefits for the Scripting Guy who writes this column. For one thing, submitting his tax return helped him avoid going to prison for income tax evasion; that’s usually a plus. For another, filing early also gave him time to figure out how to determine the owner of a file (or folder) using Windows PowerShell.

Although, in all honesty, he didn’t need all that much time to do that, either:

Get-Acl C:\Scripts\Test.txt

Believe it or not, that’s the entire script; all we have to do to determine the owner of a file is call the Get-Acl cmdlet, passing Get-Acl the path to the file in question. In turn, Get-Acl will report back information similar to this:

Directory: Microsoft.PowerShell.Core\FileSystem::C:\Scripts

Path                          Owner                         Access
----                          -----                         ------
Test.txt                      FABRIKAM\kenmyer              BUILTIN\Administrators Allow  FullCo...

Not bad, huh? If all you care about is the name of the owner then pipe the results to the Select-Object cmdlet, like so:

Get-Acl C:\Scripts\Test.txt | Select-Object Owner

That will give you information similar to this:

Owner
-----
FABRIKAM\kenmyer

Or, if you’d like to see the complete security descriptor, pipe the output to the Format-List cmdlet:

Get-Acl C:\Scripts\Test.txt | Format-List
Path   : Microsoft.PowerShell.Core\FileSystem::C:\Scripts\Test.txt
Owner  : FABRIKAM\kenmyer
Group  : FABRIKAM\Domain Users
Access : BUILTIN\Administrators Allow  FullControl
         NT AUTHORITY\SYSTEM Allow  FullControl
         FABRIKAM\kenmyer Allow  FullControl
         BUILTIN\Users Allow  ReadAndExecute, Synchronize
Audit  :
Sddl   : O:S-1-5-21-1454471165-1004336348-1606980848-8183G:DUD:AI(A;ID;FA;;;BA)(A;ID;FA;;;SY)
         (A;ID;FA;;;S-1-5-21-1454471165-1004336348-1606980848-8183)(A;ID;0x1200a9;;;BU)

If we had to guess, we’d guess that Wesley Snipes didn’t mean to ignore the April 15th deadline for filing his tax return. He probably just got caught up in the fun and excitement of working with file ownership scripts, and forgot all about paying his taxes.

For example, suppose Wesley wanted to get a list of owners for all the files in the folder C:\Scripts. That’s no problem; after all, the Get-Acl cmdlet does accept wildcard characters:

Get-Acl C:\Scripts\*.*
    Directory: Microsoft.PowerShell.Core\FileSystem::C:\Scripts

Path                          Owner                         Access
----                          -----                         ------
Example.txt                   FABRIKAM\kenmyer              BUILTIN\Administrators Allow  FullCo...
Test.txt                      FABRIKAM\pilarackerman        BUILTIN\Administrators Allow  FullCo...
Trial.txt                     FABRIKAM\kenmyer              BUILTIN\Administrators Allow  FullCo...

Pretty cool, huh? Of course, while Get-Acl does accept wildcard characters, what it doesn’t accept is some sort of –recurse parameter that would enable you to retrieve the owners of all the files located in any subfolders of C:\Scripts. But that’s OK, too; after all, the Get-ChildItem cmdlet does accept the –recurse parameter. That means we can retrieve the file owners for all the files in C:\Scripts and its subfolders by using this command:

Get-ChildItem C:\Scripts -recurse | ForEach-Object {Get-Acl $_.FullName}

There’s nothing particularly complicated about that command, either: we simply use Get-ChildItem and the –recurse parameter to retrieve the collection of files found in C:\Scripts and its subfolders, then pipe that collection to the ForEach-Object cmdlet. In turn, we ask ForEach-Object to run the Get-Acl cmdlet against each and every file in that collection, using the value of the FullName property as Get-Acl’s file path parameter.

Is that going to work? Hey, come on: have you ever known the Scripting Guys to do something that didn’t work?

Well, OK. But the command we showed you will work. Promise.

Who would have guessed that file ownership scripting could be so much fun, eh? In fact, like Wesley Snipes, we’re having such a good time today we thought we’d try one more script. It is pretty cool that you can determine the owner of a file by running a simple little Windows PowerShell script. But you know what would be really cool? It would be really cool if you could take ownership of a file by running a simple little Windows PowerShell script. You know, maybe a script like this one:

$objUser = New-Object System.Security.Principal.NTAccount("fabrikam", "kenmyer")
$objFile = Get-Acl C:\Scripts\Test.txt
$objFile.SetOwner($objUser)
Set-Acl -aclobject $objFile -path C:\Scripts\Test.txt

Much like the Scripting Guy who writes this column’s income for the year 2007, there’s really not much to this script. In line 1 we use the New-Object cmdlet to create an instance of the System.Security.Principal.NTAccount class, a .NET Framework class used to represent a user account. When creating an instance of this class we need to pass two parameters: the name of our domain (fabrikam) and the name of our user account (kenmyer).

Note. That’s a good question, and as far as we know the answer is this: assuming you want to stay out of jail then, yes, you do have to pay income tax in the US. As for your other question, the answer is no: although you can take ownership of a file using Windows PowerShell, we don’t believe that you can give ownership of a file to someone else. To transfer ownership to another user you’ll need to use the Windows Resource Kit utility Subinacl.exe.

As far as we know, anyway.

After we create an instance of the NTAccount class we use the Get-Acl cmdlet to retrieve the security descriptor from the file C:\Scripts\Test.txt; that’s what we do here:

$objFile = Get-Acl C:\Scripts\Test.txt

Once we have the security descriptor we can use the SetOwner method to assign ourselves ownership of the file:

$objFile.SetOwner($objUser)

Well, sort of. What the SetOwner method does is assign ownership to the virtual copy of the security descriptor that we retrieved using Get-Acl. To take ownership of the actual file itself we need to use the following Set-Acl command:

Set-Acl -aclobject $objFile -path C:\Scripts\Test.txt

That should give you ownership of the file.

Give these scripts a try, GF; with any luck they should help you with your management of files and file owners. As for the Scripting Guy who writes this column, he’s going to take the rest of the day off; after all, it is Pietro Antonio Cataldi’s birthday, you know.

Note. Why isn’t Pietro’s birthday a national holiday in the US? Beats us; after all, the man did hold the record for the largest known prime number for 184 years, until Leonhard Euler came along in 1772 and discovered that 231 - 1 was the eighth Mersenne prime. The largest known Mersenne prime number as of this writing is 232,582,657−1, which – by yet another amazing coincidence – is also the amount of money the Scripting Guy who writes this column requested as an income tax refund for the year 2007. We’ll let you know how that goes.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Does this same approach work for granting ownership to a group?

  • Take a common case for sysadmin - user leaves, but you can't delete his/her home folder, even when using an elevated command prompt on the server hosting the home folder volume. Usually a permissions or long file/pathname problem. Trying to get the ACLs with:

    Get-ChildItem E:\{problem folder} -recurse | ForEach-Object {Get-Acl $_.FullName} | Format-List

    will fail, if you don't have permission to read the ACL of one or more of the child objects - you will get red lines starting:

    Get-Acl: Attempted to perform an unauthorized operation.

    You need to get the file/folder name:

    Get-ChildItem E:\{problem folder} -recurse

    by itself should list all the children, even if these files (or folders) are hidden system files (i.e. have hs attributes set - which is often the case when you hit a problem deleting a folder).

    Then, if you try running the script as above on one of the problem files, the Get-Acl line will throw a similar error. So back to Windows Explorer (view hidden & system files; properties > security - take ownership, close; reopen properties, give yourself permissions, then delete).

  • Some things still better in the old fashioned command prompt! On Server 2003 and later:

    takeown /F "{problem folder}\*" /R

    will give you ownership quickly!

  • @Nathan there is a set-acl Windows PowerShell cmdlet

    @WestNab I agree, I love using the takeown command. It works well. I have described using the tool in other Hey Scripting Guy blog articles. In fact, I wrote about the exact scenario you describe.

  • ScriptingGuy1 you are hilarious, I'm sad I just now found your blog posts!

  • I am wondering if you know a way to set remote file ownership with Powershell. I didn't find a way to do it, as you can see in my last blog post (www.happysysadm.com/.../how-to-remotely-modify-windows-acl.html) but I might very well have missed something... I hope you can help.

    Anyways your blog rocks!!!

  • @Carlo:

    # set the owner on a file on a remote system.

    $acl=get-acl \\SYSTEM01\test\test.log

    $owner=[System.Security.Principal.NTAccount]'MYDOMIAN\user01'

    $acl.SetOwner($owner)

    set-acl -Path \\omega2\test\test.log -AclObject $acl

  • @jrv

    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 suggestion?

  • Hey there.  Great site.  I've played with the tools you gave me but I'm new to this (long time PERL and other Un*x scripting guy though) and missing some of the umm methods that hang from the objects.

    I'm trying to solve this.  I've got some Security Groups that seem to be orphaned.  I want to take a spin through the filesystem and see if they pop up anywhere

    Get-ChildItem .\*.* -recurse | ForEach-Object {(Get-ACL).AccessToString}

    So that gives me a list of all the Access of every file below where I am.  Great.  This solves my immediate problem, I can then search that file and know IF it is orphaned.  But say I find one.  So now I really need my output list to contain the filename too.  I can't seem to find a way to Filter Get-ACL to give me a filename (with full path preferably but I can script around the path shown at node changes and then filenames shown as local to the node) and the access.

    And obviously piping this to a grep afterwards (which doesn't seem to work like I'd expect) or setting a filter saying only if group is domain\somegroup do you print anything is the ultimate goal here.

    Thanks,

    SiD

  • @WestNab: This confused me plenty, and there are plenty of examples of forum threads that ended in 'use a 3rd party' app. I pieced together my pure PowerShell solution below, partly with help from another Scripting Guy blog (http://blogs.technet.com/b/heyscriptingguy/archive/2012/07/05/use-powershell-to-duplicate-process-tokens-via-p-invoke.aspx) - ahhh, it feels good to give back :) Basically it uses the P/Invoke technique to employ C# to call system DLL procedures. It would be nice if .Net included this necessary function, but despite the length of this code sample, it is pretty straightforward to reproduce :) The "whoami /priv" lines are a sample showing that the admin token privileges were changed. Put your Cheers, Barnaby www.computingimprovements.com

  • $code = @" using System; using System.Runtime.InteropServices; public class TokenManipulator { [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); [DllImport("advapi32.dll", SetLastError = true)] internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct TokPriv1Luid { public int Count; public long Luid; public int Attr; } internal const int SE_PRIVILEGE_DISABLED = 0x00000000; internal const int SE_PRIVILEGE_ENABLED = 0x00000002; internal const int TOKEN_QUERY = 0x00000008; internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; public static bool AddPrivilege(string privilege) { try { bool retVal; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); IntPtr htok = IntPtr.Zero; retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_ENABLED; retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); return retVal; } catch (Exception ex) { throw ex; } } public static bool RemovePrivilege(string privilege) { try { bool retVal; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); IntPtr htok = IntPtr.Zero; retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_DISABLED; retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); return retVal; } catch (Exception ex) { throw ex; } } } "@

  • <# Part 2 #> add-type $code "`nInitial privileges" whoami /priv | Select-String "SeRestorePrivilege" whoami /priv | Select-String "SeBackupPrivilege" whoami /priv | Select-String "SeTakeOwnershipPrivilege" "`nAdding privileges" #Activate necessary admin privileges to make changes without NTFS perms [void][TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions [void][TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking [void][TokenManipulator]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override FilePermissions whoami /priv | Select-String "SeRestorePrivilege" whoami /priv | Select-String "SeBackupPrivilege" whoami /priv | Select-String "SeTakeOwnershipPrivilege" #Initiate the process at a base folder which contains all homedrives $Folder = Get-Item "C:\Temp\HomeSharesCopy" #example for testing - you will need to add your code to recurse through folders, applying intended NTFS and final Ownership values #Activate necessary admin privileges to make changes without NTFS perms [void][TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions [void][TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking [void][TokenManipulator]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override FilePermissions #Create a new ACL object for the sole purpose of defining a new owner, and apply that update to the existing folder's ACL $NewOwnerACL = New-Object System.Security.AccessControl.DirectorySecurity #Establish the folder as owned by BUILTIN\Administrators, guaranteeing the following ACL changes can be applied $Admin = New-Object System.Security.Principal.NTAccount("BUILTIN\Administrators") $NewOwnerACL.SetOwner($Admin) #Merge the proposed changes (new owner) into the folder's actual ACL $Folder.SetAccessControl($NewOwnerACL) "`nRemoving privileges just added before terminating" [void][TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions [void][TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking [void][TokenManipulator]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override whoami /priv | Select-String "SeRestorePrivilege" whoami /priv | Select-String "SeBackupPrivilege" whoami /priv | Select-String "SeTakeOwnershipPrivilege"

  • Hey Scripting Guy, thanks for posting this. I have a question, what if I'm only interested in the files owned by a particular user, how would I pipe or filter this script to accomplish this.

    Get-ChildItem C:\Scripts -recurse | ForEach-Object {Get-Acl $_.FullName}

    Thanks for your input



  • Hi All

    I have tried the following with powershell the owners and users with full access on a certain path folders
    im sure you can manipulate it to get files and folders accessed by a certain user

    Get-ChildItem -Path "" -Recurse | Where {$_.psIsContainer -eq $true} | get-acl | select path -expand access | where-object {$_.FileSystemRights -eq "FullControl"} | export-csv E:\Fullcontrol.csv


    - Where {$_.psIsContainer -eq $true}: is to get folders only (i did so to get small size ACL report you can delete it if you need for files )
    - select path -expand access: this to formulate the permissions into rows so it can be copied into excel
    - where-object {$_.FileSystemRights -eq "FullControl": this part will extract what you need from ACL report for your case you can replace $_.FileSystemRights with $_.IdentityReference -eq "username" ((user name must be domain\username))


    hope you find this useful

  • If you can help out, I'm looking for a way to recursively look through file shares and match on a filter where it is an unresolved SID something like S-5-21*, and pipe it to a CSV file