Powershell Needful Things put that in your pipeline


Exchange 2010 Audit Script – Version 1

Posted by Jean Louw

I have had a couple of requests from a few readers for access to test the Exchange 2010 version of the Audit script, and I made a commitment to have this ready in January 2011. Although the script is nowhere near complete, this is a full working version which can be used to collect information from Exchange 2010 servers.

I have been very busy between work and family commitments, so I will continue to add features as and when time permits.

A couple of bugs I am aware of:

  • You cannot pipe server names to the script yet – this worked on the 2007 script so I need to spend time troubleshooting this.
  • You need to create the test mailboxes manually – refer to my post here for more info: http://www.powershellneedfulthings.com/?p=124
  • You can use the "-output s" parameter to force multiple scans onto a single report. There is a bug in the HTML code which causes the expand function on the HTML report to work incorrectly.

A couple of features I still plan to add:

  • Add volume mount points into the disk report.
  • Database Availability Group reports - I plan to add some reports with regards to the DAG if the server is a member of a DAG.
  • If a database is currently active on a server report on the number of mailboxes and total mailbox count for the current server.
  • Report on uncleared move requests and their status.

I know development is a little slow at the moment, but please be patient, when I start Exchange 2010 in a VM on my current laptop its like I have kicked it in the stomach. I will need to upgrade soon, and I am considering creating a donation option on to enable you guys to contribute to the hardware, but at this stage it is still debatable.

Other than that, I hope you can use the script in your environment, and please send me your feedback and enhancement requests so that I can add them to the wish list.

More complete information, and the download link for the script can be found here:


Automatic Custom Mailbox Limits

Posted by Jean Louw

This little script runs through the Exchange mailboxes and reads the current mailbox size, and sets a new warning limit and send limit for each mailbox, based on the current size.

So basically it looks at the current size of the mailbox, and sets a warning, at current size + 250MB and a send limit, at current size + 350MB. This was a quick workaround for us in an unmanaged environment to quickly establish some method of control without granting over sized limits, in order to accommodate large mailboxes.

This scripts should be run from the Exchange Management Shell.

The code can be copied from here:

$boxes = get-mailbox -resultsize unlimited | Get-MailboxStatistics

Foreach ($box in $boxes)
        $name = $box.DisplayName
        $currentSize = $box.TotalItemSize.ToString().Split("(")[1].Split(" ")[0].Replace(",", "")
        $WarningQuota = ([int]$currentSize + 262144000)
        $SendQuota = ([int]$currentSize + 367001600)

        Set-Mailbox -Identity $name -IssueWarningQuota $WarningQuota -ProhibitSendQuota $SendQuota

Repair Exchange mailbox display name case

Posted by Jean Louw

We had a problem at one of our customers, where the users were created (by someone else :)) in ALL CAPS. This is more of an annoyance that anything else so I wrote a little script to read the names, and set the case from UPPER CASE to Proper Case.

You obviously have to run this from the Exchange Management Shell.

You can copy the code for the script from here:

$mailboxes = Get-Mailbox

foreach ($mbx in $mailboxes)
        write "..currenntly processing:" $mbx.Name
        $oldName = $mbx.Name.ToString().Split(' ')
        $newName = ($oldName | foreach {$_.Substring(0,1) + $_.SubString(1).ToLower()})
        $mbx | set-mailbox -name $newName -displayname $newName

Creating Test Mailboxes for Mailbox Servers

Posted by Jean Louw

After a couple of requests for more information with regards to the CAS monitoring mailbox, and how to create them, I am going to do a quick cut / paste from Technet:

Create Test Mailboxes for OWA, ActiveSync, and Exchange Web Services Connectivity Monitoring.

The Exchange Server 2007 Management Pack uses cmdlets to test Microsoft Office Outlook Web Access (OWA), Microsoft ActiveSync, and Exchange Web Services connectivity from Client Access servers to Mailbox servers.

These cmdlets require that a test mailbox be created on each Exchange Server 2007 Mailbox server that is to be tested. In this procedure you create test mailboxes for OWA, ActiveSync, and Exchange Web Services connectivity monitoring by using PowerShell to run the New-TestCasConnectivityUser.ps1 script. The appropriate mailbox is created on each Mailbox server by piping in the results of get-mailboxServer.

To create a test mailbox for OWA, ActiveSync, and Exchange Web Services connectivity monitoring

1. On a Mailbox Server, open PowerShell and change directory to the C:\ Program Files\Microsoft\Exchange Server\Scripts folder.
2. Execute the following command: get-mailboxServer | .\new-TestCasConnectivityUser.ps1

Follow the on-screen installation instructions to complete the creation of the test mailbox. When requested, provide a temporary password for the test mailbox. The password must conform to your domain password policy.

This will create a new CAS_ user for each mailbox server. The number following the _ is the GUID of the mailbox server which the test user belongs to.
Remember, this user account is used commands like Test-ActiveSyncConnectivity, Test-OWAConnectivity etc. These mailboxes are also required when using the Exchange 2007 Management Pack for MOM.

These accounts are also required when using my Exchange 2007 Audit Script, as it relies on the test-* cmdlets.


Exchange 2010 Audit Script

Posted by Jean Louw

I have started development on the Exchange 2010 version of my Audit script.

If all goes well I intend to add the following features:

1. Location of database in the DAG
2. Mailbox count per database.
3. Current Mailbox count / server
4. Uncleared move requests and their status.
5. Exchange service pack and roll information
6. A new look and feel - more Exchange 2010-ish.

If time permits, I am aiming to have this ready by the 1st of January 2011.

Watch this space for updates, or join the email feed to stay updated.


Check free space on volume mount points

Posted by Jean Louw

Wow! It’s been a while since I have posted any scripts! This is mainly due to the fact that I am rather busy at work, and also working hard at completing my MCITP.

A while back a client of mine, asked if there was an easy way to use one computer to check the free space of mount points. This was a real problem for them, as the administrators would come in every morning and manually logon to each server, and use disk management to check the free space.

I was certain that there had to be a WMI object for mount points, so after a little digging, I came up with the following script:

$TotalGB = @{Name="Capacity(GB)";expression={[math]::round(($_.Capacity/ 1073741824),2)}}
$FreeGB = @{Name="FreeSpace(GB)";expression={[math]::round(($_.FreeSpace / 1073741824),2)}}
$FreePerc = @{Name="Free(%)";expression={[math]::round(((($_.FreeSpace / 1073741824)/($_.Capacity / 1073741824)) * 100),0)}}

function get-mountpoints {
$volumes = Get-WmiObject -computer $server win32_volume | Where-object {$_.DriveLetter -eq $null}
$volumes | Select SystemName, Label, $TotalGB, $FreeGB, $FreePerc | Format-Table -AutoSize

$servers = (Get-Content .\servers.txt)

foreach ($server in $servers){

The script is written to collect server names from a text file, but you could use any other method to supply you server names.

Hope this helps someone else!


Find and delete duplicate Outlook Contacts

Posted by Jean Louw

I have been fairly busy at work with little or no time to write. I did however manage to write a neat script on Friday which I thought I had to share.

My Nokia decided last week that it felt the need to duplicate all my Outlook contacts after I changed something on the ActiveSync profile. Now, normally this is not a big deal, as you can simply sort the contact items by creation date, and delete the duplicates, that is unless you mess around with them, and recreate them all from scratch. (insert curse word here)

I took one look at this problem and thought that it would be far too easy to just delete them manually. I decided to write a script to do the work for me. I have been playing a lot with the Outlook COM object lately so I already had most of the code to get this done quickly.

The script will collect all your contacts, and do a unique sort on the FullName. It then creates a temp folder under your default contacts folder, and moves the unique contacts (remember sorted by FullName only), to the temp folder.

It then dumps all the duplicates in the default contacts to a CSV and deletes them from the contacts.

At this point I stopped the script, as it made sense to check the CSV and the temp folder, and move your contacts back manually if you are happy with the results.

As usual, be very careful with this one. Automatic deletes always have the potential to end in tears. Make a backup of all your contacts before you start with the script.

I hope this script can help you.

You can download the script from here:


Determine the source site of Outlook clients on Exchange server

Posted by Jean Louw

We have been toying with the idea of centralising our Exchange environment for a while now, and as part of this project, we needed to audit our Outlook clients, to determine which source site they were connecting from.

I was tasked with this, and was able to quickly gather this information, from both the Exchange 2003 and the 2007 environment, without too much hassle.

For Exchange 2007 simply use get-logonstatistics and select the information that you need. I added some additional spice, which exports each server to a separate CSV file.

foreach ($server in get-mailboxserver){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
Get-LogonStatistics -server $server | select UserName, ClientIPAddress | sort UserName -Unique | Export-Csv $filename

Exchange 2003 is very similar, but as you can probably guess by now, you need to use WMI.

foreach ($server in (Get-ExchangeServer | Where {$_.IsExchange2007OrLater -eq $false})){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Logon -Computer $server | select MailboxDisplayName, ClientIP | sort MailboxDisplayName -Unique | Export-Csv $filename

My job done, I sent the CSV files of the project managers, only to find out that they thought it would be nice, to see exactly which site each IP address belonged to.

This proved to be a little more tricky, but after a few minutes of probing the Interwebs, I found a post where Shay uses nltest to get the site information for a computer.

I assimilated this into my script with a little DNS lookup to find the host name and came up with a function which will retrieve the site information for each IP address on the fly and add that to the CSV file.

function Get-ComputerSite ($ip){
Write-Host "Current IP:" $ip
$site = $null
$computer = [System.Net.Dns]::gethostentry($ip)
$site = nltest /server:$($computer.hostname) /dsgetsite
Return $site[0]

$ADSiteWMI = @{Name="ADSite";expression={Get-ComputerSite $($_.ClientIP)}}
$ADSite = @{Name="ADSite";expression={Get-ComputerSite $($_.ClientIPAddress)}}

foreach ($server in get-mailboxserver){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
$LogonStats = Get-LogonStatistics -server $server | sort UserName -Unique
$LogonStats | select UserName, ClientIPAddress, $ADSite | Export-Csv $filename

foreach ($server in (Get-ExchangeServer | Where {$_.IsExchange2007OrLater -eq $false})){
write-host "Current server: " $server
$filename = "." + $server + ".csv"
$LogonStats = Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Logon -Computer $server | sort MailboxDisplayName -Unique
$LogonStats | select MailboxDisplayName, ClientIP, $ADSiteWMI | Export-Csv $filename

This does take some time to complete on servers with many connections, but it gets the results required. I have already noticed a few issues, and the script can do with a little more refinement.

I will post these updates as soon as I get round to adding them. For now, I hope this script can help someone else with a similar problem.

The complete script can be downloaded from here:


Using SCL to prevent messages from going to Junk Mail

Posted by Jean Louw

In our environment, we have a number of email addresses which are managed by automated programs and systems and even some home grown applications.

Most of these systems use POP3 to connect to the mailboxes and download incoming email. Obviously POP3 does not give you access to subfolders like “Junk Mail”. It has come to our attention recently, that the Junk email rule has been flagging valid client messages as Junk Mail, and sending these messages to the Junk Mail folder. The result is that these instructions / client information never make it to the back office workflow systems.

To prevent this from happening, you first need to understand SCL or Spam Confidence Level.

The SCL, in a nutshell is basically a score based on a number of criteria, which determine how likely a message is to contain spam. The higher the score (maximum 9) the more confident Outlook is that the message is spam.

An awesome way to view the SCL for individual messages is to install a custom form, which displays an additional column with this information. More information about that here: http://msexchangeteam.com/archive/2004/05/26/142607.aspx

After installing the form, I needed to start sending some spam to myself. This would establish the same message is either blocked or cleared by the Transport Rule. I grabbed an obvious spam message from my Gmail account and turned it into a Powershell spambot:

$messageParameters = @{

Subject = "Vicodin ES (Hydrocodone) 650mg x 30 pills $209 -VISA- tbrkl rqg" Body = " -== The Best Painkillers available ==- Buy Hydrocodone, Vicodin ES, Codeine, Phentermin, Norco, Valiuml, Xanaxl Online You pay & we ship, Absolute NO question asked No PrescriptionNeeded (No doctor approval needed!) 100% deliver your order to your house We have been in business since 1998 This is a rare bargain online to obtain these UNIQUE products. No prior order needed. Limited supply of these hard to get pills, so hurry! "

From = "spambot9k@spam-the-planet.com"
To = "spambots@spam-the-planet.com"
Bcc = "jean.louw@domain.com"
SmtpServer = ""
Send-MailMessage @messageParameters –BodyAsHtml

Confirmed! My spam message was being trapped by the Junk Mail rule with SCL 9 and moved to the Junk Mail folder.

OK, next we needed to create the Transport Rule. Now, if you are new to Powershell / Exchange I would suggest creating the rule in the GUI, as the interface / wizard used in that process is similar to the Outlook rules wizard.

Once you have the rule created it is very easy to add additional addresses using Powershell. More about that later. For the purposes of this post, I will however create the rule using the shell.

$condition = Get-TransportRulePredicate SentTo
$condition.Addresses = @((Get-Mailbox "*jean.louw*"))
$action = Get-TransportRuleAction SetSCl $action.SclValue = "-1"
$warning = "WARNING: Adding mailboxes to this rule will prevent the Junk Mail rule from detecting possible spam."

New-TransportRule -name "Set SCL level to -1" -Conditions @($condition) -Action @($action) -Comments $warning

This script will create the rule to set the SCL for all messages to matching addresses to -1. You can replace "(Get-Mailbox "*jean.louw*")" with any expression or command, which will give you the mailboxes you need to add to the rule.

Now that we have the rule in place, we need to confirm that it is working. Yet again, I sent a control “spam” message ala spambot9000.

This time the message SCL was -1, as we predicted, and the message was not moved to Junk Mail as before.

In future, should you need to add additional email addresses to your rule, you can use the following:

$condition = Get-TransportRulePredicate SentTo
$condition.Addresses = @((Get-Mailbox "*system*"))
$condition.Addresses += @((Get-Mailbox "*louw, jean*"))
Set-TransportRule "Set SCL level to -1" -Conditions @($condition)

Remember that you have to add all of your address searches, each time, as the conditions are overwritten by set-transportrule. This is a really easy way to get around the problem of false positives in mailboxes where humans don't manage mailboxes, and are unable to notice that valid emails are being sent to Junk Mail.


Exchange 2007 Audit Script – Version 3

Posted by Jean Louw

I have updated the Exchange 2007 audit script yet again!

Included in this update are two MAJOR changes, firstly, the script uses and publishes information using the new HTML format, as created by Virtu-Al.

This script, and the functions which create its HTML output are far more efficient and literally cut the number of lines in the script down by half. Not only is the code leaner, but it is also infinitely more legible, and adding new tests to the current script is a breeze. This version of the HTML output is also compatible with multiple browsers, including Mozilla and Chrome.

Secondly, the script will now detect pipeline input. You can still use a server list as a parameter to the script, but now, you can also pipe content to the script. This content can include your server list, or output from Exchange commands such as get-transportserver or get-mailboxserver etc. Be careful though, because commands like get-exchangeserver could include Exchange 2003 servers.
If no server list is specified or piped, the script will still get all Exchange 2007 servers.

As another minor addition, I have added an additional test (Test-OutlookWebServices) to the CAS servers.

As always, your comments and feedback is always welcome.

Complete version and download information can be found here: