More Fun with File Aging

A few days ago, I posted an entry on how to run a file aging report using PowerShell. The script as written was pretty and all, but a little limiting. You really can’t do anything with the results. In many regards it is a VBScript rewritten for PowerShell (not that there’s anything wrong with that). But PowerShell can do so much more, especially when you think about piping objects between cmdlets. The original script just produces string output so you can’t do much else with it. What the script needs is a way to produce an object that can then be used by other PowerShell cmdlets. Thus was born FileAging2.ps1.  I’ve attached the file as a text file so you don’t have to copy and paste from the blog entry.

The script is essentially the same as before. (I did update my formatting expressions per Jeffrey Snover’s suggestion about using 1MB instead of 1048576)

# FileAging2.ps1
# Jeffery Hicks SAPIEN Technologies, Inc.
# jhicks@sapien.com
# http://www.SAPIEN.com  http://www.ScriptingAnswers.com
# http://sapien.eponym.com/blog

# This script developed and tested with PowerShell RTM

# !! TEST IN A NON-PRODUCTION ENVIRONMENT !!

Param($dir)
if ($dir -isnot [string]) {
Write-Host “ERROR: You must specify a directory path!” `
-foregroundcolor “RED” -backgroundcolor “Black”;`
return
}

$now=Get-Date
$files=Get-ChildItem -path $dir -recurse | `
where {($_.GetType()).name -eq “FileInfo”}

clear-host

#initialize
$Total2yr=0
$Total90=0
$Total1yr=0
$Total30=0
$Total7=0
$TotalCurrent=0
$2yrs=0
$1yr=0
$3mo=0
$1mo=0
$1wk=0
$current=0
$count=0

#enumerate files and get information
foreach ($file in $files) {
  $age=($now.subtract(($file.LastWriteTime))).days
  $count=$count+1
  Write-Progress -Activity “File Aging Report” `
  -status $file.DirectoryName -currentoperation $file.name
  switch ($age) {
    {$age -ge 730} {$2yrs=$2yrs+1;$Total2yr=$Total2Yr+$file.length;break}
    {$age -ge 365} {$1yr=$1yr+1;$Total1yr=$Total1Yr+$file.length;break}
    {$age -ge 90} {$3Mo=$3Mo+1;$Total90=$Total90+$file.length;break}
    {$age -ge 30} {$1Mo=$1Mo+1;$Total30=$Total30+$file.length;break}
    {$age -ge 7} {$1wk=$1wk+1;$Total7=$Total7+$file.length;break}
    {$age -lt 7}  {$current=$current+1;$TotalCurrent=$TotalCurrent+`
    $file.Length;break}
  }
}

#format file size totals to MB
$Total2yr=”{0:N2}” -f ($Total2yr/1MB)
$Total90=”{0:N2}” -f ($Total90/1MB)
$Total1yr=”{0:N2}” -f ($Total1yr/1MB)
$Total30=”{0:N2}” -f ($Total30/1MB)
$Total7=”{0:N2}” -f ($Total7/1MB)
$TotalCurrent=”{0:N2}” -f ($TotalCurrent/1MB)

#create new object to hold values
$obj = New-Object System.Object
Add-Member -inputobject $obj -membertype NoteProperty -Name Age -value “2Yrs”
Add-Member -inputobject $obj -membertype NoteProperty -Name Count -value $2yrs
Add-Member -inputobject $obj -membertype NoteProperty -Name MB -value $Total2yr
Add-Member -inputobject $obj -membertype NoteProperty -Name DirPath -value $dir.ToString()

Write-Output $obj

$obj = New-Object System.Object
Add-Member -inputobject $obj -membertype NoteProperty -Name Age -value “1Yr”
Add-Member -inputobject $obj -membertype NoteProperty -Name Count -value $1yr
Add-Member -inputobject $obj -membertype NoteProperty -Name MB -value $Total1yr
Add-Member -inputobject $obj -membertype NoteProperty -Name DirPath -value $dir.ToString()

Write-Output $obj

$obj = New-Object System.Object
Add-Member -inputobject $obj -membertype NoteProperty -Name Age -value “3Mo”
Add-Member -inputobject $obj -membertype NoteProperty -Name Count -value $3mo
Add-Member -inputobject $obj -membertype NoteProperty -Name MB -value $Total90
Add-Member -inputobject $obj -membertype NoteProperty -Name DirPath -value $dir.ToString()

Write-Output $obj

$obj = New-Object System.Object
Add-Member -inputobject $obj -membertype NoteProperty -Name Age -value “1Mo”
Add-Member -inputobject $obj -membertype NoteProperty -Name Count -value $1mo
Add-Member -inputobject $obj -membertype NoteProperty -Name MB -value $Total30
Add-Member -inputobject $obj -membertype NoteProperty -Name DirPath -value $dir.ToString()

Write-Output $obj

$obj = New-Object System.Object
Add-Member -inputobject $obj -membertype NoteProperty -Name Age -value “1Wk”
Add-Member -inputobject $obj -membertype NoteProperty -Name Count -value $1wk
Add-Member -inputobject $obj -membertype NoteProperty -Name MB -value $Total7
Add-Member -inputobject $obj -membertype NoteProperty -Name DirPath -value $dir.ToString()

Write-Output $obj

$obj = New-Object System.Object
Add-Member -inputobject $obj -membertype NoteProperty -Name Age -value “Curr”
Add-Member -inputobject $obj -membertype NoteProperty -Name Count -value $current
Add-Member -inputobject $obj -membertype NoteProperty -Name MB -value $TotalCurrent
Add-Member -inputobject $obj -membertype NoteProperty -Name DirPath -value $dir.ToString()

Write-Output $obj
#EOF

The primary improvement to the script is using New-Object to create a generic .NET object and then calling Add-Member to populate the object with data. Write-Object sends the object data to PowerShell so it can present it. But that also means I have an object I can pipe to other cmdlets.

Here’s a screen shot of what I get when I run a the command:

.\fileaging2.ps1 s:\ | format-table -autosize

Or I could do something like:
.\fileaging2.ps1 s:\ | export-csv “c:\reports\S-age.csv”

and save the information to a csv file.

I can even take this a step further and use PowerGadget‘s out-chart cmdlet. I can run an expression like this:
.\fileaging2.ps1 s:\ | out-chart -values Count,MB -label Age -Title “File Aging Report: S:\” -floating -AllSeries_PointLabels_Visible True
 
and get something like this:

Or suppose I wanted a break down of files by age as a percentage of the total:
.\fileaging2.ps1 s:\out-chart -values Count -label Age -gallery pie -AllSeries_PointLabels_Visible True -galleryAttributes_LabelsInside -title “File Aging Report: S:\ (by count)” -floating

Once my script starts acting like an object, so many other possibilities present themselves. I’ve given up the string color coding that the original script had, but I believe “objectifying” the script more than covers the “loss.”

[Thanks to the folks at PowerGadgets for helping me get on the right track]