Powershell Needful Things put that in your pipeline


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.


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.


Bulk export calendars from Exchange mailboxes

Posted by Jean Louw

I have never really had the need for a script like this, so when our catering manager at the office logged a support call, requesting an export of all calendars for all of our meeting rooms, I had to investigate the possibilities. He basically needed this information in order to determine how busy the individual meeting rooms were during the last year.
Following a quick, unsuccessful, Internet probing for tools or scripts that could do this, my initial feeling was to say “No sorry, can’t be done, or if we do it, it was going to be a manual task.”
A manual task, which involves, granting access to the room mailbox, logging onto the mailbox using Outlook, and exporting the calendar data to Excel. Sounds easy, but doing that a hundred times is very unproductive and torturous to say the least.

I decided to attempt to script it, and the result is something I am both proud of and ashamed of at the same time, as I am convinced there must be a better way.

It’s a very rough method, which involves the following process:

  • Get a list of rooms from a text file (as it was emailed to me). You could use get-mailbox instead.
  • Add-mailbox permission to the current user
  • Create an Outlook profile
  • Logon to the profile
  • Export the Calendar to CSV
  • Remove-MailboxPermission

I could automate most of the above, but creating new profiles on demand is something I’ve never had to do, and frankly, I had no idea how to get around this problem. After speaking to some of the developers at work, who promised me some dotnet code which could do it (which I am still waiting for might I add :)), I decided to use PRF files.

I have used PRF files very successfully in the past, on Terminal server deployments to automatically setup Outlook profiles.

I downloaded the ORK and created a PRF which I used as a template for the script. The blank PRF is attached to this post to save you the time and effort of using ORK.

The script finds and replaces the UserName and HomeServer in the PRF, although any Exchange server should resolve you to your mailbox server. It then creates a PRF and starts Outlook with the /importPRF switch. Some extra information, for anyone wanting to actually deploy or use the PRF file; the %HomeServer% variable in the PRF does not work the same way %UserName% works, if you want use the PRF, you need to specify one of your mailbox servers instead.

While Outlook is open on that profile, the script attaches to Outlook using a COM object and downloads the calendar for the specified date.

The calendar fields can be customised to suit your needs. In my case we simply needed the Start and End date, the duration, and the Organizer.

The export data is saved and the PRF is removed, sadly the swarm of profiles will remain, and you have to manually remove them. You could remove them from HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles but I have not added that to the script.

I hope this can help you, if you ever get a freaky request like this.

The script and the PRF template can be downloaded from here:


Collecting Exchange database white space from the event log using .NET

Posted by Jean Louw

A recent comment from a reader, prompted me to do some updates and bug fixes to my Exchange 2007 audit script. As a part of this process, I decided to add the white space count into the mailbox store check.

I discovered an extremely helpful post, as usual, from Shay Levy, which pointed me in the right direction.

Although this function does get exactly what I needed, I did however want to search for the white space by mailbox store name, in order to get the value, as each mailbox store was passed during the script processing.

I changed my script to use .NET instead of WMI for event logs so I decided to continue using this method for the white space as well.
The basic script to collect the white space sizes from the event log using .NET is as follows:

$now = Get-Date
$colMailboxStores = Get-MailboxDatabase -Server SERVER -Status | Sort-Object Name
$spaceLog=[System.Diagnostics.EventLog]::GetEventLogs('SERVER') | where {($_.LogDisplayName -eq "Application")}

foreach ($objMailboxStore in $colMailboxStores)
Write-Host "..Getting database white space for" $objMailboxStore.Name
$store = @{Name="store";Expression={$_.ReplacementStrings[1]}}
$freeMB = @{Name="freeMB";Expression={[int]$_.ReplacementStrings[0]}}
$whiteSpace = @()
$whiteSpace += $spaceLog.entries | where {($_.TimeWritten -ge $now.AddDays(-1))} | where {($_.EventID -eq "1221")} | where {($_.ReplacementStrings[1] -match $objMailboxStore.Name)} | select $store,$freeMB -last 1

This method is very slow, as it has to dredge through the entire event log for every database. It’s really not a problem if you have a small number of databases, but in a large environment like ours, with multiple mailbox servers, this could take ages to complete.

It was was painful during testing to wait for the above script to complete and I really felt that the speed of this process should be increased, so instead I came up with the following solution:

$now = Get-Date
$spaceLog=[System.Diagnostics.EventLog]::GetEventLogs('SERVER') | where {($_.LogDisplayName -eq "Application")}
$db = @{Name="database";Expression={$_.ReplacementStrings[1]}}
$freeMB = @{Name="MB";Expression={[int]$_.ReplacementStrings[0]}}
$whiteSpace = $spaceLog.entries | where {($_.TimeWritten -ge $now.AddDays(-1))} | where {($_.EventID -eq "1221")} | select $db,$freeMB

($whitespace | where {$_.database -match $objMailboxStore.Name} | select -last 1).mb

The code above will collect all of the Event ID 1221’s for the last day and store them in a variable with the customised place holders from the expressions.
This happens once per server only and any subsequent searches can be performed against the variable instead.

The select statement at the end, also selects the last item in the list to ensure that you also look at the latest event for each database. This literally reduces the runtime of the script by a factor equal to the amount of databases on your server.

I will be posting an update to the Exchange 2007 audit script soon, so stay tuned.


Maximize My SendSize

Posted by Jean Louw

Someone asked me the other day, “How could I go about using Security Groups, to control users’ send size limits?” He basically had a limit of 2mb for all users, and wanted to allow users in a specific Security Group to send up to 50mb messages. Here is a basic breakdown of the process I suggested: Firstly, you need to confirm that the global transport limit is raised to 50mb.

You can view and set these limits using get-transportconfig and set-transportconfig respectively:

Get-TransportConfig | select MaxSendSize

The next step would involve setting the send connector to allow 50mb messages. You can use get-sendconnector to get a list of all send connectors, and their respective limits.

Get-SendConnector | Select Name, MaxMessageSize

And then use set-sendconnector to set the MaxSendSize Set-SendConnector “Connector Name” –MaxSendSize 50mb Finally, you need to control the individual users’ send limits. If you have to control it via groups, you can use the following command to first enumerate the users in the group, and then pipe that to the set-mailbox command. Replace testsizegroup with the group you need to control the size limits for.

((get-group "testsizegroup").members) | foreach {set-mailbox -identity $_.Name -maxsendsize 52428800}

This will set the MaxSendSize for all users in that group to 50 mb. This command will have to be rerun every time to add users to the group, so it would be advisable to schedule this command to run hourly / daily etc.


The OWA saga continues…

Posted by Jean Louw

After solving the msExchVersion mystery, it has become apparent that even more of our Exchange 2007 users were unable to access OWA.

After logging onto the site, a very similar error is displayed: Exception type: Microsoft.Exchange.Data.Storage.StoragePermanentException Exception message: There was a problem accessing Active Directory. My first step was obviously to verify the msExchVersion.

After ensuring that this was correct, and that the users were still unable to use OWA, I had to do more digging. Deeper delving into this issue, yielded the following KB from Microsoft: http://support.microsoft.com/kb/949527

To use OWA the Exchange Servers group must have write permissions to the msExchUserCulture attribute. Easy to resolve, just allow inheritable permissions from the parent to filter to the faulty object / objects, as per the KB article.

Easy enough on one account, but if you had to change this setting manually on multiple accounts, you could use Set-QADObjectSecurity –UnlockInheritance to accomplish the task.

For more information see Dimitri’s blog


Legacy mailboxes on Exchange 2007

Posted by Jean Louw

One of our users had a problem logging onto OWA today, and I noticed that the icon for his mailbox in the GUI displayed as a legacy mailbox, although he was located on an Exchange 2007 mailbox server.

After countless searches, I came across this article: http://support.microsoft.com/kb/941146. It explains that the msExchVersion property on the AD object is not set correctly, and that using set-mailbox –ApplyMandatoryProperties would resolve the problem. Looking at the help information on set-mailbox this could also be caused by users being created on Exchange 2007 server using the Exchange 2003 tools, although these users were migrated from Exchange 2003.

So, how to correct this? First get a list of all mailboxes on the Exchange 2007 server with the incorrect version. Using get-mailbox, the incorrect Exchange version displays as 0.0 (6.5.6500.0) The following command returns a list of these mailboxes by server (where SERVER1 is your Exchange 2007 server):

get-mailbox -server SERVER1 -resultsize unlimited | where {$_.ExchangeVersion -like "*0.0*"} | select Name, ExchangeVersion

Once you verify the list, pipe them to set-mailbox.

get-mailbox -server SERVER1 -resultsize unlimited | where {$_.ExchangeVersion -like "*0.0*"} | Set-Mailbox –ApplyMandatoryProperties

This resolved the problem for me, easily, on multiple Exchange mailboxes. Running the get-mailbox command again, returned no results after applying set-mailbox to the problematic mailboxes.


Updated: Exchange Mailbox Cleaner

Posted by Jean Louw

I have really been busy lately and have not had much time to spend on scripting. I did however find a litlle time to update this utility.

Here is a list of changes:

  • After completing a query, the utility will now show you the total amount of data used by the mailboxes. (this obviously ignores single instance storage etc.)
  • You can now use the utility to move selected mailboxes to another store (This was a request from Aaron)
  • I have force removed the mandatory “confirm” on the Exchange verbs (move, disabled and delete)

Full information and the download location for this script can be found here: