This may draw the ire of some big environment silo Exchange Admins, but our environment gets very little “administration”. Oh there’s patches done regularly, users added and removed, but that’s about it. I keep an eye on Event Logs for errors and disk space to make sure we’re not running out and make sure the DAGs are replicating. Done – end of list. There are simply too many other irons in the fire to deal with small “wouldn’t it be nice” things.
One of those things on the One Day List is the .PST files resulting from Outlook Auto-Archive. Back in the old days, we had to use archiving to keep file sizes down for performance reasons. However these days with newer versions of Outlook, setting the sync time on your cache keeps the amount of data loaded into Outlook to a manageable level. This, and the relative inexpensiveness of storage has made the archive unnecessary in our environment at least.
Just because the archive process isn’t needed doesn’t mean we can just seek and destroy all of the .PST files all over the place. No, we need to find them, and import them back into the users’ mailboxes on Exchange. This is a good news/bad news sort of situation here. The good news is that we backup all of our pc’s centrally using a file copy solution so I have copies of all the PST files on a backup drive. One nice easy place to find them. The bad news? We’re working toward a migration to Office 365 and so size does matter. How many of these mailboxes are going to be over 50 GB? Any? Some? All I could do is guess and I hate to guess. I’m paid to know (or at least find out)!
This looks like a job for POWERSHELL!
Allow me to finish setting the technical scene with these little tidbits. I just started running Windows 10 Insider Preview on my production workstation, and while I’ve messed about with PowerShell 5 in a lab or at home, domain-joined production pc feels different. Secondly, we’re still on Exchange 2010 with all that entails. I long ago added implicit remote sessions to a DC and one of my Exchange servers to my PowerShell profile. So I can get-mailbox and set-aduser without having to invoke command or remember to import anything. All set. Good! Right? Not so fast.
I figure I’m going to break this project into 3 pieces.
- I’ll write a script to pull the sizes of all the company mailboxes. Since I have users that seem to forget how to empty the Deleted Items folder, I want to break out the result into Inbox, Sent Items and Deleted Items as well as the total size of each mailbox. I’ll output this to a CSV file called MailboxSizes.csv
- I’ll write another script to scan our backup files for .PST files. I’ll make note of the path and the size and output this to a file called Archives.csv
-
Then I’ll write a third script to import these PST files into their owners’ mailboxes.
Sounds simple right? well in concept, it was simple. Execution however was a wee bit trickier. See I forgot one of the gotchas of PowerShell remoting. See the objects returned from Exchange running in a remote session are not the same objects you get running locally. For example:
PS C:\> (Get-mailboxstatistics -Identity Greg).TotalItemSize.Value.ToMB()
This should return a number of megabytes equal to the total size of my mailbox. No, not remotely. Remotely your return looks like this:
PS C:\> (Get-mailboxstatistics -Identity Greg).TotalItemSize.Value.ToMB()
You cannot call a method on a null-valued expression.
……What the heck? So using my sleuthing hat, I worked backwards. I removed the “.ToMB()” to see if Value had anything in it that perhaps didn’t like the method. Interestingly, I got nothing. There’s your null-valued expression right there, but why is it Null? Let’s back off one more step to see where the problem is hiding.
PS C:\> (Get-mailboxstatistics -Identity Greg).TotalItemSize
642.5 MB (673,721,009 bytes)
….. OK some data. But why is the value property empty?
Hmm with a head scratch I pipe it to Get-Member and light dawns. Lots of methods but only one property and that’s Length.
I see the TotalItemSize property doesn’t even have a Value property, just a length. Also it’s hard to math things with a string value.
That’s when I remembered. Exchange 2010 was an early adoptee of PowerShell but it was PowerShell v2 and remoting wasn’t as mature. So I tried a few other things, Invoke-Command thinking that since the command was running on the Exchange server it might work. Alas, it wasn’t to be. I wasn’t going to RDP onto the server, write and run a v2 script, just to copy the file back to my desk. That would defeat the ‘automation’ part of this exercise. We have people coming and going every week. This reporting was going to be done several times! So after noodling around an hour or more trying to work around this deserialization problem, I decided to take the lemon and make lemonade.
Time to start working with the String I was given. I knew the best way to do this is with Regex (Regular Expressions) and honestly I had no idea how to start building an expression. Time to Bing-Google it. Bingo! An old Hey, Scripting Guy! blog post helped out. While the post was aimed at Exchange Online, the Regex demonstrated worked great for me, so I “re-purposed” that snip. I also added “Learn Regular Expressions” to my <To Learn List>.
With a little polishing I now had a working basis for my scripting project.
PS C:\> [Math]::Round(((Get-MailboxStatistics -Identity greg).totalitemsize -replace ‘(.*\()|,| [a-z]*\)’, ”)/1MB,2)
642.5
In my next post it’s time to put some toolmaking magic on this puppy!