More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  noble musingsPhotosProfileFriendsMore Tools Explore the Spaces community

noble musings

posted when I'm taking a break from PowerShell, Identity Management, SQL Server, IIS, ASP.Net, Exchange Server, etc...

Jonathan

View spaceSend a message
Occupation:
Location:
Interests:
I work with Windows systems; specifically Server 2003/8 and Vista; supporting SQL Server 2000/5/8, Exchange 2003/7, IIS 6/7 andActive Directory; scripting in PowerShell and VBScript.

Public folders

Folders shared with the world

Xbox Live GamerCard

j7seven
Xbox Live GamerCard
Rep:
Reputation:Reputation:Reputation:Reputation:Reputation:
Score:
13145
Zone:
Recreation
GTA IVPuzzle QuestMarble Blast UltraThe Orange BoxCall of Duty 4
7/21/2008

Get-ActiveSyncDeviceStatistics (32-bit) cmdlet forces device re-sync

Update (solution):
A very helpful Microsoft support person told me that there was a problem in this area which was fixed by Exchange 2007 SP1, so the problem is more specifically that I was apparently using a pre-SP1 version of the cmdlet, even though the rest of the Exchange 2007 infrastructure was patched to SP1. I've now applied the 32-bit version of Exchange 2007 SP1 to the 32-bit server and everything now works fine.

Note to self - if you've got client tools installed somewhere for remote managment, make sure you patch them as well as the servers you're managing!

Original Post:
Here's a bit of a gotcha that I've not seen documented elsewhere, so I wanted to get it out there for anyone else stumbling across it...

Last week I was prompted by Adam Smith's post to check how many iPhones running the new 2.0 software had begun to use Exchange ActiveSync against our systems. I looked to tweak a script that I'd pulled together previously by editing in PowerShell Plus (my PowerShell tool of choice), but I was working on 64-bit Server 2008 and PowerShell Plus won't use 64-bit plug-ins like the Exchange cmdlets. I decided that it would be easier to edit the script in my old trusty 32-bit Server 2003 machine which has the 32-bit Exchange 2007 Management Tools (from the Microsoft Download Center) installed.

I've worked with those tools in PowerShell Plus frequently in the past - it's a handy environment to develop scripts; although I now run everything in production on the 64-bit server, so I rarely actually use the 32-bit versions of the Exchange cmdlets to do anything. I'd figured that using a "getter" to grab some statistics couldn't do any harm. Guess again!

Every time I ran Get-ActiveSyncDeviceStatistics from from the 32-bit tools, the next time a device would try to use EAS, it'd receive the message "There has been a change made on your server that requires you to re-synchronise all items on your device. Please perform a manual sync." with support code 0x80883001.

When the support queries started coming in shortly after, I initially thought it must be a coincidence since I wasn't making any changes to the server, but I had a nagging feeling that my script must be triggering something in the Exchange infrastructure that was having this effect.

I started testing running the cmdlet against mine and a couple of other mailboxes and true enough it was recreating the problem totally consistently. It was almost by accident that part way through my testing I found myself using a shell on my 64-bit server and the problem stopped occurring. Going back and forth between testing the two: the 64-bit version of the cmdlet is fine; the 32-bit version always causes a problem.

I'm not sure if you'd call this a bug or a perfectly reasonably explainable feature, but it had me stumped for a little while!

7/4/2008

New-TeamBlog -post "Introduction to PowerShell"

My team has started a blog talking about a) the products that we work with, b) the services we provide, and c) anything else that we think would be of interest to people reading about a and b. I'd imagine that at a good percentage of my contributions are going to be looking at PowerShell, so I figured I'd pull together a few bits and pieces that I've written here and there and post an Introduction to Windows PowerShell. This is it...

PowerShell is a command shell and language focusing on Windows system administration. It can be used interactively to get immediate results, or you can write complex scripts and do batch processing. Although it is still not used as much as it should be, PowerShell isn't a brand new product; it's been around for a couple of years and version 2 is currently available in its 2nd Community Technical Preview.

Now that PowerShell is part of Microsoft's Common Engineering Criteria (meaning that product teams pretty-much have to incorporate PowerShell into their new releases), and being incorporated as a feature in Windows Server 2008, you'll see PowerShell usage sky-rocket! (It's worth also saying that Microsoft aren't the only ones adding PowerShell support to their products - VMWare, IBM, Citrix and others see the potential of managing their products this way.)

The thing that sets PowerShell apart from other shells is that it uses pipelines of objects, not of text. It is build on the .NET Framework, but you don't need to have a developer's appreciation of .NET to work with PowerShell. In fact one of the best ways of getting started with PowerShell is to just run it each time you were about to run cmd.exe - many of the things you'd want to do there work in PowerShell. If you've been a *nix admin in the past, you'll find that some of the commands you're familiar with work in PowerShell too.

For example, the PowerShell cmdlet (pronounced "command-let") to list the contents of a folder, Get-ChildItem, has aliases built in, so that you can use either dir or ls in its place. The interesting thing with PowerShell is that it has providers which let you access other repositories (referred to as PSDrives) in the same way as the file system, so you can do this:

cd HKLM:\software
dir

...and see the contents of a registry hive! (Note that if you want to do a recursive directory listing, the parameters are different to the dir in cmd.exe, so you'll want to check the help to see what you can do with the cmdlets)

Give it a go and I'm sure you'll soon find your way around. To get started, Get-Command gives you a list of the available cmdlets; Get-Help [cmdlet] tells you what they do; Get-Member lists the properties and methods of an object; Get-PSDrive lists the PSDrives that are available to you. Knowing those cmdlets is enough to get you quite a distance.

The TechNet Script Center has a load of great resources for IT Pros at and there's a fantastic community building around PowerShell, with a UK User Group (run by Richard Siddaway who is a PowerShell MVP - details on his blog), numerous PowerShell bloggers, and community sites. There are also a bunch of PowerShell books, most of which are pretty good, but if you're just looking for one I'd recommend Lee Holmes' PowerShell Cookbook.

If you don't find that there's support built in to products or PowerShell itself for what you want to do, there may be a 3rd party snap-in that could help. I'm making used of the free Active Directory cmdlets from Quest Software, the free Group Policy management cmdlets from SDM Software and the PowerShell Community Extensions - a large suite of additional cmdlets, providers, functions and more.

I'll leave you with a quick PowerShell example. This is a cut down version of something that we used yesterday to enumerate the members of all the AD groups in a particular OU and save the listing for each group in a separate file named after the group:

Get-QADGroup -SearchRoot "OU=Groups,OU=ISS,DC=campus,DC=ncl,DC=ac,DC=uk" | %{$name = $_.name; Get-QADGroupMember $name | Out-File "$name.txt"}

It has probably wrapped in your browser, but that's just with one line of PowerShell and frankly a good chunk of it is specifying the OU that we're looking in! If you want to run that, you'll just need to change the OU and have the Quest AD cmdlets installed. I won't explain how it works here - I just wanted to show how much you can do with so little PowerShell. How much effort/code would it take to achieve that task another way?!

7/1/2008

Updated PowerShell resources from TechNet Script Center

If you write scripts on Windows systems, you should definitely already have the TechNet Script Center bookmarked as they have a huge amount of high quality resources for the full range of Windows admin scripting technologies (as well as the fun of the Scripting Games). Their PowerShell Script Center is a great hub of links and free tools, articles and tips and should be one of the first stops you make in learning PowerShell.

Two of the more comprehensive resources on Script Center have just been updated:

The Windows PowerShell Owner's Manual, which has previously had chapters on Getting Started, Customizing the Console, Shortcut Keys, Piping and the Pipeline and Running Scripts, has had two additional chapters published - The Windows PowerShell Profile and Windows PowerShell Aliases. This is great material, written with the trademark humour of the Scripting Guys and well worth a read.

The second updated resource is The VBScript-to-Windows PowerShell Conversion Guide. If you're already a fluent VBScript scripter and want to update your skills to PowerShell then this is a useful reference, although I think from the PowerShell purist's point of view it probably isn't the best way to learn PowerShell since there's a paradigm shift that translating VBScript directly doesn't take advantage of. That said, if you know how to do something in VBScript and you want to know how to do it in PowerShell, this is the place to come.

The Conversion Guide has been expanded from just VBScript Commands, with new sections covering Dictionary Object Methods and Properties, FileSystemObject Methods and Properties and Windows Script Host Methods.

6/30/2008

Replacing text in all the files in a folder

There are plenty of examples on the web of how to replace text in a single file using PowerShell, but there may be times when you want to replace some text in a bunch of files; say you've moved a load of scripts to a different drive and some of them have reference to the old drive/folder path in them. It's practically as easy to work with a whole folder as it is with one file.

Firstly, make sure you're working in the folder where the files exist and then it's just a one liner...

cd d:\scripts
dir -name | %{$content = (gc $_) -replace "c:\\scripts","d:\scripts"; sc $_ $content}

Breaking that down, the dir command gets a directory listing for the current folder. (NB dir is just an alias to the Get-ChildItem cmdlet; equally, I could've used ls, which is also an alias). We don't need the whole FileInfo object that Get-ChildItem returns, just the file name, so I'm using the -name parameter.

We pipe those file names to a ForEach loop (% is an alias for the ForEach-Object cmdlet), so we're working with one file at a time. Here we grab the content of each file using the Get-Content cmdlet...

(gc $_)

...so if the current file on the pipeline is test1.ps1 this is the same as us typing Get-Content test1.ps1. This is enclosed in parentheses so that it is done before the replace operator takes effect.

-replace "c:\\scripts","d:\scripts"

...replaces any instance of "c:\scripts" with "d:\scripts", but note the double backslash in the text that's being replaced. This is because the replace operator is searching for the text using a regular expression and the backslash is the escape character, so you need to escape it.

The file's content, with the path replaced, is placed into a variable called $content, then, before we leave the script block that's dealing with each file, we use the Set-Content cmdlet to write the contents of $content into the file whose name is still held in the automatic $_ variable.

Working on a folder tree

If you want to replace the text in all the files within a whole folder structure you can add the -recurse parameter to your dir/ls/Get-ChildItem. However, this will pass the sub-folders themselves (and not just the files contained within) to the ForEach loop, so you'll get errors when you try to Get-Content and Set-Content on a folder. We need to filter the sub-folders out of the pipeline, and in order to do this we need to look at the full object, not just the name...

dir -recurse | ?{!$_.PSIsContainer} | %{$content = (gc $_) -replace "c:\\scripts","d:\scripts"; sc $_.PSPath $content}

These objects that our directory listing returns are filtered by...

?{!$_.PSIsContainer}

...which uses the (? alias to the) Where-Object cmdlet to check the PSIsContainer property and only allow the objects which are not (hence the !) containers to pass along the pipeline (so we just have the files, not the folders).

The other change that I've had to make is interesting: while Get-Content is happy to be given a FileInfo object as a parameter, Set-Content is not. To get round this, I've picked out the PSPath property of the file (the file's path, fully qualified with the provider), which Set-Content is happy to accept.

6/18/2008

Scripting/Sysadmin Meme

I've been called out by Richard to fill in Steven Murawski's Scripting/Sysadmin Meme, so here it is...

How old were you when you started using computers?

I'm only guessing here, but I think I was about 8. I was in junior school anyway.

What was your first machine?

The first machine I used was the BBC Micro Model B at school, then my friend got a BBC Master with a whole 128k which we put to good use playing Elite. The first machine that was actually mine was the spiritual successor to the BEEBs, the Acorn Archimedes A3000.

What was the first real script you wrote?

The first code of any kind I wrote was in BBC BASIC - it was a text adventure game and was neither long nor good. I'm not sure what my proper sysadmin script was, but I have a feeling that it could well have been one that populated our brand new Active Directory with user accounts in 1999. A bit like jumping in at the deep end!

What scripting languages have you used?

I mostly used VBScript (with a bit of JScript from time to time) for a bunch of years before PowerShell came along. Now it's all PowerShell, all the time.

What was your first professional sysadmin gig?

Well I (along with a friend) was the IT support for the school (when I was about 8) because the teachers didn't know what they were doing, but I wasn't paid so it was hardly professional, although it did get me out of assemblies, so that was some reward. Then I suppose I could count some work I did on some Apple machines in the theatre/sports centre where I worked in my teens. It's probably more reasonable to say the job that I've been in for the last 9 years, which is a proper sysadmin gig.

If you knew then what you know now, would have started in IT?

If I knew then that all the hard slog around the cluster rooms, building hundreds of NT4 workstations with Ghost boot floppies and disk images on CD, would lead on to the success and growth of our systems in the Active Directory era, and that we'd be able to expand into different areas and services with basically the same number of people running them; firstly I wouldn't have believed you, but I think I'd have been as excited about being part of the industry as I am now! This isn't a great job for someone who fears change, but if you love technology, it's a great roller coaster ride!

If there is one thing you learned along the way that you would tell new sysadmins, what would it be?

Definitely get involved in the IT community. Join user groups, go to the TechEd event in your region (or, what the heck, why not another region) and try to learn as much from people in the hallways or the dining room as you do in the formal sessions (I think a week at TechEd is better value than any training course). Communicate with peers any way you can. If you're not in a position to meet people physically, chat online with them; comment on blogs, participate in online forums. It may not pay off immediately, but it will pay off.

What’s the most fun you’ve ever had scripting?

Ever since I started to really get PowerShell, it's been almost non-stop fun. Finding out how very much you can achieve with how very little code is like a game on top of the puzzle of actually solving the original problem.

Who am I calling out?

Jonathan Walz and Chris Warwick

5/23/2008

R2 or not R2, that is the question

If you want to access the OS version of a computer you've got a few options. The easiest way in PowerShell is to use the Get-QADComputer cmdlet from the excellent free set of Quest AD cmdlets. However, you'll find that you can't tell the difference between Windows Server 2003 and Windows Server 2003 R2.

In order to find out which of your servers are running R2 (if you want to audit your licences for example), you'll have to use WMI and access the OtherTypeDescription property of the Win32_OperatingSystem class. This one-liner will do the job for you (just alter the search root to match your Active Directory structure):

get-QADComputer -searchroot "yourdomain.com/servers/sql servers" | `
%{gwmi -Class win32_OperatingSystem -Namespace "root\CIMV2" -ComputerName $_.Name `
-ea silentlycontinue} | `
sort version,othertypedescription -Descending | `
ft @{label="SERVER";expression={$_.csname}},@{label="OS";expression={$_.caption}}, `
@{label="R?";expression={$_.othertypedescription}}, `
@{label="SP";expression={$_.servicepackmajorversion}} -a

What it will give you is a handy table like this:

SERVER    OS                                                       R? SP
------    --                                                       -- --
SPURS     Microsoft(R) Windows(R) Server 2003, Standard Edition    R2  2
PISTONS   Microsoft(R) Windows(R) Server 2003, Standard Edition    R2  2
LAKERS    Microsoft(R) Windows(R) Server 2003 Standard x64 Edition R2  2
CELTICS   Microsoft(R) Windows(R) Server 2003, Standard Edition        2
CAVS      Microsoft(R) Windows(R) Server 2003, Standard Edition        2
HORNETS   Microsoft(R) Windows(R) Server 2003, Standard Edition        2

Footnote:
That's fine if all of your servers are going to allow that incoming WMI query. You may have servers which are tightly locked down and you'd prefer not to open them up. If that's the case, you can do what we do and query the WMI locally on the server and write the results out to a database as part of a startup script (we currently do this in VBScript). If you take this approach, especially if you have a lot of servers, be sure to make the date part of the record so you have an idea how good it is.

5/17/2008

Pipelines of Objects: Adding Power to the Shell

I'm writing this to help anyone wondering about PowerShell, and those just getting started and struggling with the paradigm-shift that the PowerShell pipeline represents. What does it mean to have a pipeline of objects? It's a prominent bullet-point in the PowerShell marketing, and while it may instantly make sense to developers and computer scientists, it's not a straight forward concept for everyone. I thought of an analogy which I think will help...

Say you're on your way home from work and your spouse calls to ask you to stop on the way for a new light bulb to replace a dead one in the bed-side lamp. In order to make sure that you get the right bulb, your spouse is going to have to describe it in a fair amount of detail: bulb style=candle, bulb height=short, wattage=40w, fitting type=screw, fitting size=small, glass type=pearl, glass col=white. Basically, your spouse is going to give you a load of strings of text to describe the different properties of the bulb that you need.

I'm going to assume we have a cmdlet (a PowerShell command, pronounced "command-let") called Buy-LightBulb and we'll use PowerShell syntax to describe what our spouse is saying on the phone, but this basically is the pre-PowerShell way of working:

Buy-LightBulb -bulbstyle candle -bulbheight short -wattage 40 -fittingtype screw -fittingsize small -glasstype pearl -glasscol white

Now let's imagine that we got home before our spouse got round to calling. Instead of describing all of those properties for the replacement bulb, they could give us the old one as a reference. After all, it has all of those properties.

So let us assume that we've got the old bulb in a PowerShell variable called $oldbulb. If we piped $oldbulb to Get-Member, we'd see something like this, showing that it has the properties and methods that we'd expect from a light bulb:

PS > $oldbulb | Get-Member

   TypeName: System.Illumination.LightBulb

Name        MemberType   Definition
----        ----------   ----------
bulbstyle   Property  bulbstyle=candle
glasstype   Property  glasstype=pearl
wattage     Property  wattage=40
....

We can pass (pipe) that light bulb object to the Buy-LightBulb cmdlet with the result that we'll buy a new light bulb with the same properties as the old one (except that we'd hope it will work!). If we assign that pipeline to a variable $newbulb...

$newbulb = $oldbulb | Buy-LightBulb

...then $newbulb will be a new light bulb object with the same properties as the old bulb. This is the PowerShell way of working.

Still, our spouse hasn't forgotten the time we came back from the store with the wrong sized refuse sacks, so we've got to to pass the new bulb through their Check-SpousePurchase cmdlet.

Before PowerShell, you'd expect that Buy-LighBulb would have returned some code to signify success, or the name of the light bulb, or perhaps a set of strings describing the properties of the light bulb we've bought. In order for your untrusting spouse to accept the product you've bought, you'd have to give strings describing all the properties of it to Check-SpousePurchase.

In PowerShell however, the results of Buy-LightBulb is a rich .Net object of type System.Illumination.LightBulb. The Check-SpousePurchase cmdlet can take this object from the pipeline, so the whole process can be expressed in PowerShell by passing objects along the pipeline and never having to describe properties with strings...

Your spouse hands you the bulb to replace, you purchase a replacement and hand the new bulb back to your spouse who confirms it's the right one and we avoid the problem we had with the refuse sacks were we didn't have a full understanding of the properties and bought the wrong ones:

$oldbulb | Buy-LightBulb | Check-SpousePurchase

Really, if you forget about light bulbs, the difference is between passing the physical object from one step to the next, or describing over the phone what has come out of one stage to go into the next. I hope that's helped clear things up for some of you and also shown why pipelines of objects have significant advantages over passing strings from one command to another.

5/16/2008

Sapien PowerShell Training: update

The ScriptingAnswers.com University Windows PowerShell Full Set, which I blogged about a couple of weeks ago, arrived yesterday. We only got round to sorting out the purchase order on Tuesday, so that delivery has been extraordinarily fast!

Not sure if I'll get a chance to try it over the weekend, but I should be able to provide something of a review in the next week or so.

5/14/2008

How much space are orphaned mailboxes taking up on my Exchange servers?

If your enterprise has a high turnover of people, then at any one time you'll likely have numerous disconnected mailboxes on your Exchange Servers. That's because Exchange, by default, will not purge the mailbox until 30 days after the user it was connected to has disappeared from Active Directory, i.e. it has become orphaned.

Keeping those mailboxes there for a month can be a handy safety net, so I'm not suggesting purging them sooner (although that may suit you), but if you're going to allocate new users to mailbox stores based on how much space is in use/available it can be handy to look at how much space disconnected mailboxes are taking up, i.e. how much white space will be freed up in your .edb file after the disconnected mailboxes are purged (and an online defrag has taken place).

Since we have to talk to Exchange 2003 and Exchange 2007 in different ways, I'm going to deal with them separately, then if you want to combine them into a single script, you can (it's just a case of not re-initialising the hashtable).

Ok, so I'm going to assume that we've got a text file Ex03Servers.txt which contains a list of the Exchange 2003 servers we want to query. We need to setup a hashtable to hold the results, but then the rest of the work is done by a one liner:

$space = @{}
get-content Ex03Servers.txt | `
%{gwmi -co $_ -namespace "root\MicrosoftExchangeV2" -query "SELECT * FROM Exchange_mailbox WHERE DateDiscoveredAbsentInDS IS NOT NULL" | `
%{$space[$_.ServerName+"\"+$_.StoreName]+=$_.Size}}

Thanks to Gaurhoth in the #powershell IRC channel on irc.freenode.net for pointing me in the right direction for finding disconnected mailboxes on Exchange 2003 with WMI.

$space now contains the amount of free space and you can just type $space to see it, although you'll get the output in a random order, so you may want use the enumerator to sort by size:

$space.GetEnumerator() | Sort value -desc

Or by server and store name alphabetically:

$space.GetEnumerator() | Sort name

So, to do the same thing for disconnected mailboxes on Exchange 2007, do this:

$space = @{}
get-content Ex07Servers.txt | `
%{get-mailboxstatistics -Server $_ | `
?{$_.DisconnectDate -ne $null} | `
%{$space[$_.Database]+=$_.TotalItemSize.Value.ToKB()}}

5/13/2008

Using PowerShell in a Mixed 2003/7 Exchange Environment

Most Exchange admins will have learned, to their distress or delight, that Exchange 2007 has PowerShell at the core of its management. In fact, if you're not aware of that, the GUI seems really odd - you've got to adjust your thinking somewhat and remember it's actually closer to the command line than the old Exchange System Manager. Actually, that was really the catalyst for me getting into PowerShell so much.

Anyway, Exchange 2007 and PowerShell work great together and I wish that I was in a position to take full advantage, but I guess like a lot of people, we're in the process of migrating to Exchange 2007 and will have some Exchange 2003 servers for a while yet. That means that we have our hands tied to a certain extent, but it doesn't mean that we can't do a lot with PowerShell!

The PowerShell function that I've written here is a good example of how you can use the new Exchange 2007 cmdlets to a certain extent and the limitations of that backwards compatiblity. Since we get a lot of requests for quota increases, what I've set out to do is find out the quota limits and current usage when given a username. We also have a slightly strange situation with SMTP addresses (more on that later), so I'm listing those out, as well as the mailbox location...

function get-mailboxinfo ([string]$user = $(Throw "USAGE: get-mailboxinfo username"))
{
$usermb = Get-Mailbox $user
"{0,-15}{1,-2}{2,-20}" -f "User",":",$user
"{0,-15}{1,-2}{2,-40}" -f "Name",":",$($usermb.DisplayName)
"{0,-15}{1,-2}{2,-20}" -f "Server",":",$($usermb.ServerName.ToUpper())
"{0,-15}{1,-2}{2,-40}" -f "Database",":",$($usermb.Database)
"-- SMTP Addresses --"
($usermb.EmailAddresses |?{$_.IsPrimaryAddress -and ($_.PrefixString -eq "SMTP")}|select addressstring).AddressString + " (primary)"
$usermb.EmailAddresses |?{!($_.IsPrimaryAddress)}|select addressstring | %{$_.AddressString}
"-- Quota & Usage --"
if ($usermb.UseDatabaseQuotaDefaults -eq $TRUE){
    write-Host "Using database default quotas"
    $WarningPreference = "SilentlyContinue" #get-mailboxdatabase generates warnings if .edb in a root directory
    #Need to use get-mailboxdatabase in this way to work with Exchange 2003...
    #If only using Exchange 2007, get-MailboxDatabase $usermb.Database is better...
    $mbdatabase = Get-MailboxDatabase -IncludePreExchange2007 -Server $usermb.ServerName | ?{$_.name -eq $usermb.Database}
    $iwq = $mbdatabase.IssueWarningQuota.Value.ToKB()
    $psq = $mbdatabase.ProhibitSendQuota.Value.ToKB()
    $psrq = $mbdatabase.ProhibitSendReceiveQuota.Value.ToKB()}
else{
    $iwq = $usermb.IssueWarningQuota.Value.ToKB()
    $psq = $usermb.ProhibitSendQuota.Value.ToKB()
    $psrq = $usermb.ProhibitSendReceiveQuota.Value.ToKB()}
if ($usermb.RecipientTypeDetails -eq "LegacyMailbox"){
    #Use WMI to find size of Exchange 2003 mailbox
    $filter = "MailboxGUID='{" + $usermb.ExchangeGuid + "}'"
    $wmimb = gwmi -ComputerName $usermb.Servername -Namespace "root\MicrosoftExchangeV2" -Class "Exchange_mailbox" -filter $filter
    $mbsize = $wmimb.size}
else{
    #WMI MicrosoftExchangeV2 namespace replaced by get-mailboxstatistics cmdlet in Exchange 2007
    $mbsize = (Get-MailboxStatistics $user).TotalItemSize.Value.ToKB()}
"{0,-15}{1,-2}{2,7}" -f "Using (KB)",":",$mbsize
"{0,-15}{1,-2}{2,7}{3,8}{4,8}" -f "Quotas",":",$iwq,$psq,$psrq
"{0,-15}{1,-2}{2,7:p}{3,8:p}{4,8:p}" -f "Quota %",":",$($mbsize/$iwq),$($mbsize/$psq),$($mbsize/$psrq)
}

What that will give you, is a quick report like this, irrespective of where the mailbox is:

User : fbloggs
Name : Fred Bloggs
Server : EXSERVER1
Database : Mailbox Store 6
-- SMTP Addresses --
fred.bloggs@yourdomain.com (primary)
fred.bloggs@marketing.yourdomain.com
-- Quota & Usage --
Using (KB) : 385911
Quotas : 500000 600000 1000000
Quota % : 77.18 % 64.32 % 38.59 %

You may not find the SMTP address list so helpful, but our users each have four (and an X400 address) like:
username@theexchangesystem.domain.com (initially set as the primary by the recipient update policy)
username@forestrootdomain.domain.com
emailname@shortdomain.com (external address with abbreviated domain name, so most used)
emailname@longdomainname.com (the primary external formal email address)
so you can see how it's useful in our case!

If you want to take advantage of Exchange 2007 and the extra info that you can easily get about those mailboxes, add this into the function and wish that migration would move a little faster...

if ($usermb.RecipientTypeDetails -eq "UserMailbox")       
    {get-MailboxStatistics $user | FT ItemCount,StorageLimitStatus,@{label="TotalItemSize (KB)";expression={$_.TotalItemSize.Value.ToKB()} },LastLogonTime -auto
    write-Host "Largest folders for $($user):"
    get-MailboxFolderStatistics $user | Sort foldersize,folderpath -desc | Select-Object -first 10 | FT FolderPath,ItemsInFolder,@{label="FolderSize (KB)";expression={$_.FolderSize.ToKB()}} -auto
    }

View more entries
 
Updated 2/27/2008
Updated 5/7/2008
Updated 11/3/2006
Updated 10/12/2006
Updated 9/6/2006
Updated 10/12/2006
Updated 8/6/2006