Delete old files in recycle bin with powershell - powershell

Ok, I have a script I am writing in powershell that will delete old files in the recycle bin. I want it to delete all files from the recycle bin that were deleted more than 2 days ago. I have done lots of research on this and have not found a suitable answer.
This is what I have so far(found the script online, i don't know much powershell):
$Path = 'C' + ':\$Recycle.Bin'
Get-ChildItem $Path -Force -Recurse -ErrorAction SilentlyContinue |
#Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-3) } |
Remove-Item -Recurse -exclude *.ini -ErrorAction SilentlyContinue
It is working great with one exception, it checks the file parameter "LastWriteTime". That is awesome if the user deletes the file they same day they modify it. Otherwise it fails.
How can I modify this code so that it will check when the file was deleted, not when it was written.
-On a side note, if I run this script from an administrator account on Microsoft Server 2008 will it work for all users recycle bins or just mine?
Answer:
the code that worked for me is:
$Shell = New-Object -ComObject Shell.Application
$Global:Recycler = $Shell.NameSpace(0xa)
foreach($item in $Recycler.Items())
{
$DeletedDate = $Recycler.GetDetailsOf($item,2) -replace "\u200f|\u200e",""
$dtDeletedDate = get-date $DeletedDate
If($dtDeletedDate -lt (Get-Date).AddDays(-3))
{
Remove-Item -Path $item.Path -Confirm:$false -Force -Recurse
}#EndIF
}#EndForeach item
It works awesome for me, however 2 questions remain...How do I do this with multiple drives? and Will this apply to all users or just me?

WMF 5 includes the new "Clear-RecycleBin" cmdlet.
PS > Clear-RecycleBin -DriveLetter C:\

These two lines will empty all the files recycle bin:
$Recycler = (New-Object -ComObject Shell.Application).NameSpace(0xa)
$Recycler.items() | foreach { rm $_.path -force -recurse }

This article has answers to all your questions
http://baldwin-ps.blogspot.be/2013/07/empty-recycle-bin-with-retention-time.html
Code for posterity:
# -----------------------------------------------------------------------
#
# Author : Baldwin D.
# Description : Empty Recycle Bin with Retention (Logoff Script)
#
# -----------------------------------------------------------------------
$Global:Collection = #()
$Shell = New-Object -ComObject Shell.Application
$Global:Recycler = $Shell.NameSpace(0xa)
$csvfile = "\\YourNetworkShare\RecycleBin.txt"
$LogFailed = "\\YourNetworkShare\RecycleBinFailed.txt"
function Get-recyclebin
{
[CmdletBinding()]
Param
(
$RetentionTime = "7",
[Switch]$DeleteItems
)
$User = $env:USERNAME
$Computer = $env:COMPUTERNAME
$DateRun = Get-Date
foreach($item in $Recycler.Items())
{
$DeletedDate = $Recycler.GetDetailsOf($item,2) -replace "\u200f|\u200e","" #Invisible Unicode Characters
$DeletedDate_datetime = get-date $DeletedDate
[Int]$DeletedDays = (New-TimeSpan -Start $DeletedDate_datetime -End $(Get-Date)).Days
If($DeletedDays -ge $RetentionTime)
{
$Size = $Recycler.GetDetailsOf($item,3)
$SizeArray = $Size -split " "
$Decimal = $SizeArray[0] -replace ",","."
If ($SizeArray[1] -contains "bytes") { $Size = [int]$Decimal /1024 }
If ($SizeArray[1] -contains "KB") { $Size = [int]$Decimal }
If ($SizeArray[1] -contains "MB") { $Size = [int]$Decimal * 1024 }
If ($SizeArray[1] -contains "GB") { $Size = [int]$Decimal *1024 *1024 }
$Object = New-Object Psobject -Property #{
Computer = $computer
User = $User
DateRun = $DateRun
Name = $item.Name
Type = $item.Type
SizeKb = $Size
Path = $item.path
"Deleted Date" = $DeletedDate_datetime
"Deleted Days" = $DeletedDays }
$Object
If ($DeleteItems)
{
Remove-Item -Path $item.Path -Confirm:$false -Force -Recurse
if ($?)
{
$Global:Collection += #($object)
}
else
{
Add-Content -Path $LogFailed -Value $error[0]
}
}#EndIf $DeleteItems
}#EndIf($DeletedDays -ge $RetentionTime)
}#EndForeach item
}#EndFunction
Get-recyclebin -RetentionTime 7 #-DeleteItems #Remove the comment if you wish to actually delete the content
if (#($collection).count -gt "0")
{
$Collection = $Collection | Select-Object "Computer","User","DateRun","Name","Type","Path","SizeKb","Deleted Days","Deleted Date"
$CsvData = $Collection | ConvertTo-Csv -NoTypeInformation
$Null, $Data = $CsvData
Add-Content -Path $csvfile -Value $Data
}
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
#ScriptEnd

Had to do a bit of research on this myself, the recycle bin contains two files for every file deleted on every drive in win 10 (in win 7 files are as is so this script is too much and needs to be cut down, especially for powershell 2.0, win 8 untested), an info file created at time of deletion $I (perfect for ascertaining the date of deletion) and the original file $R, i found the com object method would ignore more files than i liked but on the up side had info i was interested in about the original file deleted, so after a bit of exploring i found a simple get-content of the info files included the original file location, after cleaning it up with a bit of regex and came up with this:
# Refresh Desktop Ability
$definition = #'
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
public static void Refresh() {
SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);
}
'#
Add-Type -MemberDefinition $definition -Namespace WinAPI -Name Explorer
# Set Safe within deleted days and get physical drive letters
$ignoreDeletedWithinDays = 2
$drives = (gwmi -Class Win32_LogicalDisk | ? {$_.drivetype -eq 3}).deviceid
# Process discovered drives
$drives | % {$drive = $_
gci -Path ($drive+'\$Recycle.Bin\*\$I*') -Recurse -Force | ? {($_.LastWriteTime -lt [datetime]::Now.AddDays(-$ignoreDeletedWithinDays)) -and ($_.name -like "`$*.*")} | % {
# Just a few calcs
$infoFile = $_
$originalFile = gi ($drive+"\`$Recycle.Bin\*\`$R$($infoFile.Name.Substring(2))") -Force
$originalLocation = [regex]::match([string](gc $infoFile.FullName -Force -Encoding Unicode),($drive+'[^<>:"/|?*]+\.[\w\-_\+]+')).Value
$deletedDate = $infoFile.LastWriteTime
$sid = $infoFile.FullName.split('\') | ? {$_ -like "S-1-5*"}
$user = try{(gpv "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\$($sid)" -Name ProfileImagePath).replace("$(gpv 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList' -Name ProfilesDirectory)\",'')}catch{$Sid}
#' Various info
$originalLocation
$deletedDate
$user
$sid
$infoFile.Fullname
((gi $infoFile -force).length / 1mb).ToString('0.00MB')
$originalFile.fullname
((gi $originalFile -force).length / 1mb).ToString('0.00MB')
""
# Blow it all Away
#ri $InfoFile -Recurse -Force -Confirm:$false -WhatIf
#ri $OriginalFile -Recurse -Force -Confirm:$false- WhatIf
# remove comment before two lines above and the '-WhatIf' statement to delete files
}
}
# Refresh desktop icons
[WinAPI.Explorer]::Refresh()

This works well also as a script with the task scheduler.
Clear-RecycleBin -Force

Related

Powershell Script is printing out duplicate entries of the same path

My objective is to write a powershell script that will recursively check a file server for any directories that are "x" (insert days) old or older.
I ran into a few issues initially, and I think I got most of it worked out. One of the issues I ran into was with the path limitation of 248 characters. I found a custom function that I am implementing in my code to bypass this limitation.
The end result is I would like to output the path and LastAccessTime of the folder and export the information into an easy to read csv file.
Currently everything is working properly, but for some reason I get some paths output several times (duplicates, triples, even 4 times). I just want it output once for each directory and subdirectory.
I'd appreciate any guidance I can get. Thanks in advance.
Here's my code
#Add the import and snapin in order to perform AD functions
Add-PSSnapin Quest.ActiveRoles.ADManagement -ea SilentlyContinue
Import-Module ActiveDirectory
#Clear Screen
CLS
Function Get-FolderItem
{
[cmdletbinding(DefaultParameterSetName='Filter')]
Param (
[parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias('FullName')]
[string[]]$Path = $PWD,
[parameter(ParameterSetName='Filter')]
[string[]]$Filter = '*.*',
[parameter(ParameterSetName='Exclude')]
[string[]]$ExcludeFile,
[parameter()]
[int]$MaxAge,
[parameter()]
[int]$MinAge
)
Begin
{
$params = New-Object System.Collections.Arraylist
$params.AddRange(#("/L","/S","/NJH","/BYTES","/FP","/NC","/NFL","/TS","/XJ","/R:0","/W:0"))
If ($PSBoundParameters['MaxAge'])
{
$params.Add("/MaxAge:$MaxAge") | Out-Null
}
If ($PSBoundParameters['MinAge'])
{
$params.Add("/MinAge:$MinAge") | Out-Null
}
}
Process
{
ForEach ($item in $Path)
{
Try
{
$item = (Resolve-Path -LiteralPath $item -ErrorAction Stop).ProviderPath
If (-Not (Test-Path -LiteralPath $item -Type Container -ErrorAction Stop))
{
Write-Warning ("{0} is not a directory and will be skipped" -f $item)
Return
}
If ($PSBoundParameters['ExcludeFile'])
{
$Script = "robocopy `"$item`" NULL $Filter $params /XF $($ExcludeFile -join ',')"
}
Else
{
$Script = "robocopy `"$item`" NULL $Filter $params"
}
Write-Verbose ("Scanning {0}" -f $item)
Invoke-Expression $Script | ForEach {
Try
{
If ($_.Trim() -match "^(?<Children>\d+)\s+(?<FullName>.*)")
{
$object = New-Object PSObject -Property #{
ParentFolder = $matches.fullname -replace '(.*\\).*','$1'
FullName = $matches.FullName
Name = $matches.fullname -replace '.*\\(.*)','$1'
}
$object.pstypenames.insert(0,'System.IO.RobocopyDirectoryInfo')
Write-Output $object
}
Else
{
Write-Verbose ("Not matched: {0}" -f $_)
}
}
Catch
{
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
Catch
{
Write-Warning ("{0}" -f $_.Exception.Message)
Return
}
}
}
}
Function ExportFolders
{
#================ Global Variables ================
#Path to folders
$Dir = "\\myFileServer\somedir\blah"
#Get all folders
$ParentDir = Get-ChildItem $Dir | Where-Object {$_.PSIsContainer -eq $True}
#Export file to our destination
$ExportedFile = "c:\temp\dirFolders.csv"
#Duration in Days+ the file hasn't triggered "LastAccessTime"
$duration = 800
$cutOffDate = (Get-Date).AddDays(-$duration)
#Used to hold our information
$results = #()
#=============== Done with Variables ===============
ForEach ($SubDir in $ParentDir)
{
$FolderPath = $SubDir.FullName
$folders = Get-ChildItem -Recurse $FolderPath -force -directory| Where-Object { ($_.LastAccessTimeUtc -le $cutOffDate)} | Select-Object FullName, LastAccessTime
ForEach ($folder in $folders)
{
$folderPath = $folder.fullname
$fixedFolderPaths = ($folderPath | Get-FolderItem).fullname
ForEach ($fixedFolderPath in $fixedFolderPaths)
{
#$fixedFolderPath
$getLastAccessTime = $(Get-Item $fixedFolderPath -force).lastaccesstime
#$getLastAccessTime
$details = #{ "Folder Path" = $fixedFolderPath; "LastAccessTime" = $getLastAccessTime}
$results += New-Object PSObject -Property $details
$results
}
}
}
}
ExportFolders
I updated my code a bit and simplified it. Here is the new code.
#Add the import and snapin in order to perform AD functions
Add-PSSnapin Quest.ActiveRoles.ADManagement -ea SilentlyContinue
Import-Module ActiveDirectory
#Clear Screen
CLS
Function ExportFolders
{
#================ Global Variables ================
#Path to user profiles in Barrington
$Dir = "\\myFileServer\somedir\blah"
#Get all user folders
$ParentDir = Get-ChildItem $Dir | Where-Object {$_.PSIsContainer -eq $True} | where {$_.GetFileSystemInfos().Count -eq 0 -or $_.GetFileSystemInfos().Count -gt 0}
#Export file to our destination
$ExportedFile = "c:\temp\dirFolders.csv"
#Duration in Days+ the file hasn't triggered "LastAccessTime"
$duration = 1
$cutOffDate = (Get-Date).AddDays(-$duration)
#Used to hold our information
$results = #()
$details = $null
#=============== Done with Variables ===============
ForEach ($SubDir in $ParentDir)
{
$FolderName = $SubDir.FullName
$FolderInfo = $(Get-Item $FolderName -force) | Select-Object FullName, LastAccessTime #| ft -HideTableHeaders
$FolderLeafs = gci -Recurse $FolderName -force -directory | Where-Object {$_.PSIsContainer -eq $True} | where {$_.GetFileSystemInfos().Count -eq 0 -or $_.GetFileSystemInfos().Count -gt 0} | Select-Object FullName, LastAccessTime #| ft -HideTableHeaders
$details = #{ "LastAccessTime" = $FolderInfo.LastAccessTime; "Folder Path" = $FolderInfo.FullName}
$results += New-Object PSObject -Property $details
ForEach ($FolderLeaf in $FolderLeafs.fullname)
{
$details = #{ "LastAccessTime" = $(Get-Item $FolderLeaf -force).LastAccessTime; "Folder Path" = $FolderLeaf}
$results += New-Object PSObject -Property $details
}
$results
}
}
ExportFolders
The FolderInfo variable is sometimes printing out multiple times, but the FolderLeaf variable is printing out once from what I can see. The problem is if I move or remove the results variable from usnder the details that print out the folderInfo, then the Parent directories don't get printed out. Only all the subdirs are shown. Also some directories are empty and don't get printed out, and I want all directories printed out including empty ones.
The updated code seems to print all directories fine, but as I mentioned I am still getting some duplicate $FolderInfo variables.
I think I have to put in a condition or something to check if it has already been processed, but I'm not sure which condition I would use to do that, so that it wouldn't print out multiple times.
In your ExportFolders you Get-ChildItem -Recurse and then loop over all of the subfolders calling Get-FolderItem. Then in Get-FolderItem you provide Robocopy with the /S flag in $params.AddRange(#("/L", "/S", "/NJH", "/BYTES", "/FP", "/NC", "/NFL", "/TS", "/XJ", "/R:0", "/W:0")) The /S flag meaning copy Subdirectories, but not empty ones. So you are recursing again. Likely you just need to remove the /S flag, so that you are doing all of your recursion in ExportFolders.
In response to the edit:
Your $results is inside of the loop. So you will have a n duplicates for the first $subdir then n-1 duplicates for the second and so forth.
ForEach ($SubDir in $ParentDir) {
#skipped code
ForEach ($FolderLeaf in $FolderLeafs.fullname) {
#skipped code
}
$results
}
should be
ForEach ($SubDir in $ParentDir) {
#skipped code
ForEach ($FolderLeaf in $FolderLeafs.fullname) {
#skipped code
}
}
$results

Keyword Search Across All Servers - Powershell

Afternoon All,
I need to run a search across all of our servers.
I have the list of servers in a text document and a list of keywords in another
$Servers = get-content -path 'C:\support\Server Search\Server Test.txt'
$Keywords = get-content -path "C:\Support\Server Search\Keyword Test.txt"
Foreach ($Server in $Servers){
Foreach ($Keyword in $Keywords){
Get-ChildItem "$Server" -Recurse | Where-Object {$_.Name -like "$Keyword"}
$i++
Write-Host "$found: $i - Current $ $_"
New-Object -TypeName PSCustomObject -Property #{
Directory = $_.Directory
Name = $_.Name
Length = $_.Length /1024
CreationTime = $_.CreationTime
LastWriteTime = $_.LastWriteTime
LastAccessTime = $_.LastAccessTime}|
select Directory,Name,Length,CreationTime,LastWriteTime,LastAccessTime |
Export-Csv "C:\support\server search\$Server.csv" -Append -NoTypeInformation
}}
$i = 0
Is there a way to indicate when a Keyword has been located and total keywords found? I feel like I need to change this Line but I cannot fathom what I would actually put, I've tried $Keywords but that just changes keyword everytime the directory changes
$i++
Write-Host "$found: $i - Current $ $_"
I'm assuming your $server is set up something like "\\servername\c$\"
when a Keyword has been located and total keywords found:
$Servers = get-content -path 'C:\support\Server Search\Server Test.txt'
$Keywords = get-content -path "C:\Support\Server Search\Keyword Test.txt"
$num = 0 #Total Keyword files Found
Foreach ($Server in $Servers){
Foreach ($Keyword in $Keywords){
#Keyword found Check
$Found = Get-ChildItem -Path "$Server" -Recurse -Include "$Keyword"
if($Found){
Foreach($File in $Found){
$num++ #increment num of keyword files found by 1
Write-Host "found: $num - $File"
New-Object -TypeName PSCustomObject -Property #{
Directory = $File.Directory
Name = $File.Name
Length = $File.Length /1024
CreationTime = $File.CreationTime
LastWriteTime = $File.LastWriteTime
LastAccessTime = $File.LastAccessTime}|
select Directory,Name,Length,CreationTime,LastWriteTime,LastAccessTime |
Export-Csv "C:\support\server search\$Server.csv" -Append -NoTypeInformation
}
}
}
}
Please let me know if this helps you progress. I can assist further if requested.

Check files on remote computers for time stamp older than X hours and export results to CSV

We are trying to run a script against a pile of remote computers to check the date stamps of files in a fixed folder that are older than say 12 hours and return the results to a CSV. The date range needs to be flexible as its a set time of 6pm yesterday which will move as the time moves on.
$computers = Get-Content -Path computers.txt
$filePath = "c:\temp\profile"
$numdays = 0
$numhours = 12
$nummins = 5
function ShowOldFiles($filepath, $days, $hours, $mins)
{
$files = $computers #(get-childitem $filepath -include *.* -recurse | where {($_.LastWriteTime -lt (Get-Date).AddDays(-$days).AddHours(-$hours).AddMinutes(-$mins)) -and ($_.psIsContainer -eq $false)})
if ($files -ne $NULL)
{
for ($idx = 0; $idx -lt $files.Length; $idx++)
{
$file = $files[$idx]
write-host ("Old: " + $file.Name) -Fore Red
}
}
}
Write-output $computers, $numdays, $numhours, $nummins >> computerlist.txt
You could run the follow script on all of your remote machines:
$computers = Get-Content -Path computers.txt
$logFile = "\\ServerName\C$\Logfile.txt"
$date = "12/03/2002 12:00"
$limit = Get-Date $date
$computers | %{
$filePath = "\\$_\C$\temp\profile"
$files = $null
$files = Get-ChildItem -Path $filePath -Recurse -Force | `
Where-Object {$_.CreationTime -lt $limit }
If($files -ne $null){
"-------------------------[$($_)]------------------------">> $logFile
$files | Foreach {$_.FullName >> $logFile}
}
}
This will check the folder given ($filePath) for files that are older than the limit given. Files older than the limit will have there full file path logged in the given network location $logFile.
with a small alteration to #chard earlier code I managed to get a workable solution.
The output log file only returns the files that are older than the date in the code.
this can be manipulated in Excel with other outputs for our needs.
I will try the updated code above in a bit.
$computers = Get-Content -Path "C:\temp\computers.txt"
$logFile = "\\SERVER\logs\output.txt"
$numdays = 3
$numhours = 10
$nummins = 5
$limit = (Get-Date).AddDays(-$numdays).AddHours(-$numhours).AddMinutes(-$nummins)
$computers | %{
$filePath = "\\$_\C$\temp\profile\runtime.log"
Get-ChildItem -Path $filePath -Recurse -Force | `
Where-Object {$_.LastWriteTime -lt $limit } |
foreach {"$($_)">> $logFile}
}

How do I modify this powershell script so it creates a log of the date time and names of all files that were deleted or previously deleted?

I wanted to make a PowerShell script that deleted any type of file in a certain folder when they were 7 days old. The problem I am having is creating a log file with the date, time, and names of all the files that were deleted or were previously deleted when the script ran.
I am wondering if there is a way of modifying the answer found on this page: https://stackoverflow.com/questions/12326171/powershell-script-to-delete-sub-folders-and-files-if-creation-date-is-7-days-bu?lq=1
I would like it so that instead of sending an email with the report it will create one log file of all the files deleted with the names, time, and dates in the folder that I am storing the script (Please note I'd like it to append the log file each time not overwrite it). I am sorry because I do not know how to code so I have been stuck trying to do this myself for a long time now and I have been asking questions and such but still can't seem to get it to work. So yea if anyone can modify that script it would be very appreciated! Thank you!
Here is what I did with the script (if it helps) , but it doesn't work (Again I do not know how to code):
$report_items = #()
# set folder path
$dump_path = "C:FileDeleter\AutoDeleteFilesInThisFolder"
# set min age of files
$max_days = "-7"
# get the current date
$curr_date = Get-Date
# determine how far back we go based on current date
$del_date = $curr_date.AddDays($max_days)
# get the sub directories
$sub_dirs = Get-ChildItem $dump_path | Where-Object {!$_.PSIsContainer }
$sub_dirs | % {
if (Get-ChildItem $_.FullName -Recurse | Where-Object { $_.LastWriteTime -gt $del_date } ) {
$report_items += New-Object -Type PSObject -Property #{
Time = Get-Date -f "hh:mm:ss"
Message = "Skipping " + $_.FullName + " because it contains items newer than " + $del_date
}
}
else {
Remove-Item $_.FullName -Recurse
$report_items += New-Object -Type PSObject -Property #{
Time = Get-Date -f "hh:mm:ss"
Message = "Deleting " + $_.FullName + " because it contains no items newer than " + $del_date
}
}
}
$report_items | out-file "C:\FileDeleter\PruningReport.txt"
This should do it:
Get-ChildItem -Path "C:\FileDeleter\AutoDeleteFilesInThisFolder" -Filter *.torrent -Recurse | Where { $_.CreationTime -lt (Get-Date).AddDays(-7) } | %{Remove-Item -Force; if ($?) {"$(Get-Date -Format 'MM-dd-yy HH:mm:ss.ff') $_.Name" | Add-Content 'C:\FileDeleter\PruningReport.txt'}}
(The reason for the if ($?) block is to only write a log entry if the file deletion was successful - you don't want to assume that it was.)
Not as succinct, but I'm a fan of not over-pipelining in PowerShell.
[String] $strBaseDir = "c:\temp";
[String] $strFileFilter = "*.txt";
[Int] $intDayThreshold = 7;
[System.IO.FileSystemInfo] $objFile = $null;
[Int] $intLastWriteDays = 0;
[String] $strLogFileName = "d:\data\yourlogfile.log";
Get-ChildItem -LiteralPath $strBaseDir -Filter $strFileFilter -Recurse | Where-Object{ ! $_.PSIsContainer } |
Foreach-Object {
$objFile = $_;
$intLastWriteDays = [Math]::Round((New-TimeSpan -Start $objFile.LastWriteTime -End (Get-Date)).TotalDays);
if ( $intLastWriteDays -ge $intDayThreshold ) {
Write-Output -InputObject ( "{0:G} : Deleting file [{1}] with last write time of [{2:G}], which is [{3}] day(s) ago." -f (Get-Date), $objFile.FullName, $objFile.LastWriteTime, $intLastWriteDays ) | Add-Content -Path $strLogFileName -PassThru;
$objFile | Remove-Item -Force -ErrorAction silentlyContinue;
if ( ! $? ) {
Write-Output -InputObject "ERROR : Failed to remove file." | Add-Content -Path $strLogFileName -PassThru;
} #if
} else {
Write-Output -InputObject ( "{0:G} : Skipping file [{1}] with last write time of [{2:G}], which is [{3}] day(s) ago." -f (Get-Date), $objFile.FullName, $objFile.LastWriteTime, $intLastWriteDays ) | Add-Content -Path $strLogFileName -PassThru;
} #else-if
} #Foreach-Object

Powershell get total size of files which user owns

I need a powershell script, which will go through all users in system and will find total size of all files which any user own... I have script which is going through all users, but then I've no idea to continue with counting total size which user owns for each user
Here is a script, which I`ve now:
$users = Get-WmiObject -class Win32_UserAccount
foreach($user in $users) {
$name = $user.Name
$fullName = $user.FullName;
if(Test-Path "C:\Users\$name") {
$path = "C:\Users\$name"
} else {
$path = "C:\Users\Public"
}
$dirSize = (Get-ChildItem $path -recurse | Measure-Object -property length -sum)
"{0:N2}" -f ($dirSize.sum / 1Gb) + " Gb"
echo "$dirSize"
Add-Content -path "pathototxt..." -value "$name $fullName $path"
}
I would be more than happy If somebody know the answer and tell me it...
Thank you
If there's a lot of files, you might want to consider:
$oSIDs = #{}
get-childitem <filespec> |
foreach {
$oSID = $_.GetAccessControl().Sddl -replace '^o:(.+?).:.+','$1'
$oSIDs[$oSID] += $_.length
}
Then resolve the SIDs when you're done. Parsing the owner SID or well-know security principal ID from the SDDL string saves the provider from having to do a lot of repetitive name resolution to give you back the "friendly" names.
I have no idea what you're asking for here.
"to continue with counting total size which user owns for each user". huh? Do want to check every file on the system or just the userfolder as you currently do?
Your script works fine if you just tweak it to include the filesize in the output. Personally I'd consider using a csv to store this because not all users will have e.g. a full name(admin, guest etc.). Also, atm. your script is counting the public folder multiple times(each time a user doesn't have a profile). E.g. admin(if it has never logged in), guest etc. might both get it specified.
Updated script that outputs both textfile and csv
$users = Get-WmiObject -class Win32_UserAccount
$out = #()
#If you want to append to a csv-file, replace the $out line above with the one below
#$out = Import-Csv "file.csv"
foreach($user in $users) {
$name = $user.Name
$fullName = $user.FullName;
if(Test-Path "C:\Users\$name") {
$path = "C:\Users\$name"
} else {
$path = "C:\Users\Public"
}
$dirSize = (Get-ChildItem $path -Recurse -ErrorAction SilentlyContinue | ? { !$_.PSIsContainer } | Measure-Object -Property Length -Sum)
$size = "{0:N2}" -f ($dirSize.Sum / 1Gb) + " Gb"
#Saving as textfile
#Add-Content -path "pathototxt..." -value "$name $fullName $path $size"
Add-Content -path "file.txt" -value "$name $fullName $path $size"
#CSV-way
$o = New-Object psobject -Property #{
Name = $name
FullName = $fullName
Path = $path
Size = $size
}
$out += $o
}
#Exporting to csv format
$out | Export-Csv "file.csv" -NoTypeInformation
EDIT: Another solution using the answer provided by #mjolinor and #C.B. modified to scan your c:\ drive while excluding some "rootfolders" like "program files", "windows" etc. It exports the result to a csv file ready for Excel.:
$oSIDs = #{}
$exclude = #("Program Files", "Program Files (x86)", "Windows", "Perflogs");
Get-ChildItem C:\ | ? { $exclude -notcontains $_.Name } | % { Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue | ? { !$_.PSIsContainer } } | % {
$oSID = $_.GetAccessControl().Sddl -replace '^o:(.+?).:.+','$1'
$oSIDs[$oSID] += $_.Length
}
$out = #()
$oSIDs.GetEnumerator() | % {
$user = (New-Object System.Security.Principal.SecurityIdentifier($_.Key)).Translate([System.Security.Principal.NTAccount]).Value
$out += New-Object psobject -Property #{
User = if($user) { $user } else { $_.Key }
"Size(GB)" = $oSIDs[$_.Key]/1GB
}
}
$out | Export-Csv file.csv -NoTypeInformation -Delimiter ";"