Archiving email attachments
Posted by: hbr in script, powershell, outlook, attachments, archive on
Jan 12, 2011
I had a discussion last week with a colleague about archiving messages in outlook. I have a 7GB mailbox on the server because I never delete mail. That may seem overdone but it has served me well a couple of times in discussions we had. Besides, I see absolutely no point in archiving mail away from the mail server to another store on some fileserver that eventually lands on the same central storage.
But a lot of the older mail contains attachments that are outdated and no longer needed. The reason I keep older mail is because the text that's in there. All the old documents can go. A nice side effect of having less attachments in my older mail is that with a reinstall of my client, I need less data to download.
So I started looking for products that could remove attachments from a certain size and a certain age and store it somewhere so I can easily filter it for later reference. So far I haven't found one that does what I need. So I figured I'd write one myself.
Nowadays, everything that Microsoft products do can be controlled by Powershell so I choose to write the tool in the form of a script. The core of it was quickly written last night (I have done things like that before) apart from one tiny pitfall; if attachments are removed in a loop, the attachment list gets confused as to which is next and it skips every other attachment if one is deleted. It took me a while to figure out why and how to fix it. But with a little ugly hack and a for loop instead of a foreach, it works perfectly now.
This script will remove attachments of a given size and a given age and store them in a given directory, leaving a link inside the email. I choose to remove attachments with an age of over 1 year and with a size of 1MB and up. It sized my 7GB outlook.ost file down to 4GB (after using the 'compact' function in outlook and waiting 8 hours!). If I run it with a 180 day age and 500kB size, it goes down to less than 2GB.
Useful? Maybe not for everyone, but it was a fun night of scripting which beats the hell out of the reality soaps on TV :)
Here's the script. Save it as a .ps1 file, sign it or set-executionpolicy to unrestriced and have a go. Oh, and backup your mail first ;)
#
# CONFIG:
#
$daysold=365
$minsize=1024000
$attstorage="D:\attachments\"
###############################################
# No need to edit below this line:
#
#
[System.Reflection.Assembly]::LoadWithPartialName("System.web") | out-null
Add-Type -AssemblyName microsoft.office.interop.outlook
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
$outlook = new-object -comobject Outlook.Application
$namespace = $outlook.GetNamespace("MAPI")
$inbox = $namespace.getDefaultFolder("olFolderInbox")
$sentitems = $namespace.getDefaultFolder("olFolderSentMail")
$oldmail = (date).AddDays(-$daysold)
$logfile = ($attstorage + (date -uformat %Y-%m-%d) + ".log")
$global:total = 0
function log ($str) { write-output $str | out-file $logfile -append }
function good ($str) { write-host -foregroundcolor green $str }
function bad ($str) { write-host -foregroundcolor red $str }
function ok ($str) { write-host -foregroundcolor yellow $str }
function encode ($str) { return [system.web.httputility]::urlencode($str) }
function decode ($str) { return [system.web.httputility]::urldecode($str) }
function proc
{
param($folder, $name)
$list=$folder.items|?{$_.receivedTime -lt $oldmail}
foreach ($mail in $list)
{
$str=""
$max=$mail.attachments.count
for ($i = 1; $i -le $max; $i++ ) {
$att = $mail.attachments.item($i)
if ($att.size -gt $minsize) {
$global:total = $global:total + $att.size
$filename=encode($name + "-" + $att.filename)
$att.SaveAsFile($attstorage + $filename)
if (test-path($attstorage + $filename)) {
if ((get-item($attstorage + $filename)).length -eq 0) {
bad ("Error writing file '" + $filename + "' to " + $attstorage)
exit 1
}
good ("" + $attstorage + $filename + " saved succesfully." )
log ("From: " + $mail.sendername + " (" + $mail.senderemailaddress + "), Date: " + (date $mail.ReceivedTime -uformat "%Y-%m-%d %H:%M") + "; " + $attstorage + $filename + " (" + ([int](100*$att.size/1024/1024)/100) + " MB) saved succesfully." )
$str=$str + $crlf + 'Attachment removed and saved to ' + $filename + "
"
$att.delete()
$max--
$i--
} else {
bad ("" + $attstorage + $filename + " not saved." )
exit 2
}
}
}
if ($str -ne "") {
$mail.htmlbody = $mail.htmlbody -ireplace "(]*>)", ("`$1" + $str)
$mail.save()
}
}
}
function deeper
{
param($folder, $name)
ok ( "- Processing: " + $name )
proc $folder $name
$folder.folders|%{deeper $_ ($name + "-" + $_.name)}
}
if (! (test-path $attstorage)) { mkdir $attstorage }
if (! (test-path $attstorage)) { throw "ERROR!! Can't create " + $attstorage }
deeper $inbox "Inbox"
deeper $sentitems "Sent-Items"
"" + ([int](10*$global:total/1024/1024)/10) + " MB removed. "
