Friday, July 31, 2009

Dedicated Exchange Site - Child Domain Controllers

In a centralized Exchange environment with multiple child AD domains and multiple AD sites, the assumption was that multiple global catalog servers and domain controllers that were all members of the same domain as Exchange would be sufficient as long as they are placed in the same dedicated Exchange site.

http://support.microsoft.com/kb/875427

However, turning MSExchange ADAccess\Topology logging up to expert will reveal information that the site would benefit by having child domain global catalog servers in the site.

Event Type: Information
Event Source: MSExchange ADAccess
Event Category: Topology
Event ID: 2129
Description:
Process w3wp.exe (OWA) (PID=5844). Exchange Active Directory Provider needs a Domain Controller in domain DC=childdomain,DC=corp,DC=mytestnetwork,DC=com. Found server AnOutOfSiteGDC.childdomain.corp.mytestnetwork.com.

Going a bit further and tracking ntlm requests confirmed that under very heavy load the domain controllers will begin timing out when processing request due to the fact they have to send all of their cross domain traffic to out-of-site domain controllers which leads to the CAS servers not being able to log users in, web services being unavailable, etc.

Here's some additonal blogs regarding the issue:

http://blogs.technet.com/mikelag/archive/2009/08/04/the-case-of-the-mysterious-exchange-server-hang.aspx
http://blogs.msdn.com/dgoldman/archive/2009/08/04/the-case-of-the-mysterious-exchange-server-hang.aspx

Monday, March 30, 2009

Most Smart Hosts Look for MX Records First

Specifying a smart host is pretty simple in Exchange, but there are a few tricks that can help you provide the fault tolerance you're looking for.

First, you can specify multiple smart host to spread the load. By doing this Exchange will use each of them in a rotating order so that each smart host will handle and forward email equally. This method provides fault tolerance but if you want to use of one server to handle most of the traffic and fail back to another host if that server is too busy or down you'll need to use some DNS basics.

By design Exchange performs a DNS query on the value specified in the smart-host field to see if there is an MX for it before it moves on to check for the IP via A record. This means you can specify one smart host in Exchange and configure MX records for it to have Exchange route mail a number of different ways.

Configuring them with different MX cost will route all mail to your lowest cost MX record first and if it is "tarpitting" or down for any reason, Exchange will move to the next host.

See http://technet.microsoft.com/en-us/library/cc750376.aspx for more details.

Thursday, March 26, 2009

Outlook Top Level Folders

Occassionaly users will delete or move their calendar, contacts or other top level folders in Outlook. Opening Outlook with the following switches will add the proper top level folders back into Outlook so you can move the items back to where they belong.

Outlook.exe /resetfolders /profilename

Wednesday, March 4, 2009

Reconnecting Disconnected Mailbox to Another Account

“The operation cannot be performed because this mailbox was already reconnected to an existing user.”
ID no: c1034ad6
Exchange System Manager

This is a problem we run into quite a bit and amazingly almost everywhere you search on the internet they will tell you to remove the exchange attributes on the new account then reconnect the old disconnected mailbox to the account, exmerge it, remove the Exchange attributes again, and finally reconnect the new disconnected mailbox.

A much easier solution is to simply change the legacyExchangeDN of the user to something else temporarily and then you can reconnect the disconnected mailbox to any temporary AD account you want. Once the mailbox is connected, change the legacyExchangeDN of the temporary AD account to something else and finally change the legacyExchangeDN of the user back to the original.

Now some might kick and scream because technically you could generate a NDR if someone sends a message to the user during these 45 seconds using a cached address in Outlook. If that is the case, which it probably is, you could prevent the NDRs by adding the legacyExchangeDN attribute as an X.500 address for the user account.

To do this create a Custom Address for the user and enter the legacyExchangeDN as the address with the address type being X500

Once you’ve changed the temporary AD account's legacyExchangeDN to something other than the original and added the legacyExchangeDN back to the user you can remove the X500 address.

Friday, February 27, 2009

Email Exchange 2007 Mailbox Counts using Google Charts

Since Glen Scales (http://gsexdev.blogspot.com/) posted some great information about getting powershell scripts to talk to the Google Charts API, I decided I would give it a try by rewriting one of our existing powershell scripts that provides a report on the Exchange mailbox counts by database.

The original script just walks through each Exchange database, get the total mailbox count using a simple forearch loop before converting (ConvertTo-HTML) and exporting (Out-File) it all to a HTML document on the local hard drive. Then it grabs the HTML document off the drive and emails it out as an HTML email to a distribution list.

After quick re-write of the loop that actually returns the mailbox counts, I started to attack working with the Google Charts API. It was surprisingly easy to get the url formatted, stick it into a HTML email (img src)and send it off.








Problem was the email would basically lock up your email client every time you opened it because (in our environment) there were so many charts it needed to download from the internet each time you opened the message.

So opted to actually submit the data to the Google Charts API in the script and then have the script download the results (.PNG Images) to a temporary folder on the hard drive.

$Client.DownloadFile($url,$File)




Once I had them all downloaded I simply looped through the directory for the images and sent them as embedded images in the email. This worked as expected but the email was now around 500K; which seemed a bit much for a simple daily report.

After some digging on my favorite c#.net sites I finally figured out how to convert the .png images to .gif images.

$img=[Drawing.Image]::FromFile("$WorkingFolder\$Server.png")
$img.Save("$WorkingFolder\$Server.gif",'GIF')
$img.Dispose()

This dramatically reduced the size (and quality) of the charts which cut the size of the email down to something more comforting.

I should have stopped there but I knew there had to be a better way to handle the images rather than downloading them all to a drive, converting them and then finally embedding the converted ones. After a bit more digging enter System.IO.MemoryStrem

$bImage = $Client.DownloadData($url)
$MS = New-Object System.IO.MemoryStream(,$bImage)
$Image = [Drawing.Image]::FromStream($MS)
$Image.Save($File, [System.Drawing.Imaging.ImageFormat]::Gif)
$Image.Dispose()

As always, I know there are probably some tweaks left to be made but if you'd like to Download the File you can.

Tuesday, January 27, 2009

List Exchange Distribution List Members Recursively

Based on the powershell script I found by Hugo Peters; this script not only returns mailboxes but contacts too. I also added the ability to display the name of any Dynamic Distribution Lists that are members but haven't been able to get it to parse through each one of those yet.

Download File

To run the script you will need to specify the Distribution List or Lists you want to extract in a csv file.

Remove Invalid Characters from Exchange Aliases

Everytime a powershell command is run against Exchange you may receive warning such as this:

WARNING: Object my.domain.com/Users/Dough, John has been corrupted and it is in an inconsistent state. The following validation errors have occurred:
WARNING: "John Dough" is not valid for Alias. Valid values are: Strings formed with characters from a to z (uppercase or lowercase), digits from 0 to 9, !, #, $, %, &, ', *, +, -, /, =, ?, ^, _, `, {, , } or ~. One or more periods may be embedded in an alias, but each one of them should be preceded and followed by at least one of the other characters. Unicode characters from U+00A1 to U+00FF are also valid in an alias, but they will be mapped to a best-fit US-ASCII string in the email address which is generated from such an alias


In order to remove the invalid characters from the Exchange Alias (usually a space or a period) you can run the command below.

Get-Mailbox -OrganizationalUnit "mydomain.com/OU/Users"
-IgnoreDefaultScope foreach {$_.alias = $_.alias -replace '
\s\,<>\(\)\[\]'; $_}

But to do it right (logging, import specific OUs, etc) the whole script is below. P.S. Cleaning up DL's is as easy as changing the initial query.


# ==================================================================
param(
[switch]$help,
[string] $file = $(if ($help -eq $false){Read-Host "CSV File"}) # Define CSV file for import
)
# ==============================================================================
# Script to remove invalid characters from user's Exchange 'Alias'
# and trim whitespace from 'DisplayName'
#
# Last Revision 1.30.09 by Derek W. Aude
# ==============================================================================
# List of variable
[int]$Count = $null
[string]$WorkingOU = $null
[array]$SplitOU = $null
[string]$OU = $null
[string]$LoggingDir = $null
[string]$OutputFile = $null
[array]$Mailboxes = $null
[string]$Mailbox = $null
[array]$SplitAlias = $null
[string]$InvalidChar = $null
[string]$NewAlias = $null
[string]$OldAlias = $null
[int]$Count = $null
#===============================================================================
# Test to make sure the file path given is valid
if ((Test-Path $file) -eq $false){Write-Host "Could not find CSV file $file" -ForegroundColor Red;exit}
# Import the users to move from CSV into $csv
$Mailboxes = import-csv $file -erroraction stop
foreach ($WorkingOU in $Mailboxes) {
$LoggingDir = $null
$Count = 0
$WorkingOU = $WorkingOU.Name
# Get OU to clean and create log file
$SplitOU = [regex]::Split($WorkingOU, "/")
foreach ($OU in $SplitOU) {
$LoggingDir = $LoggingDir + "$OU\"
}
$OutputFile = New-Item -ItemType file "Logging\$LoggingDir$(Get-Date -UFormat '%Y%m%d%H%M%S').log" -Force
Write-Output "Mailbox Clean-up for $WorkingOU" >> $OutputFile
# Get all mailboxes in OU
$Mailboxes = Get-Mailbox -OrganizationalUnit $WorkingOU
Foreach ($Mailbox in $Mailboxes) {
if($Mailbox.DisplayName -like "* ") {
Set-Mailbox -Identity $Mailbox.Identity -DisplayName $Mailbox.DisplayName.Trim() -EmailAddressPolicyEnabled:$false
$Count = $Count + 1
Write-Output "Issue $Count - Removed leading or trailing whitespace from the display name: $Mailbox.DisplayName" >> $OutputFile
}
$OldAlias = $Mailbox.Alias
$NewAlias = $Mailbox.Alias -replace '\s\,<>\(\)\[\]'; $_
If ($NewAlias -ne $OldAlias) {
$Count = $Count + 1
Write-Output "Issue $Count - Removed invalid characters from $OldAlias" >> $OutputFile
}
if($NewAlias -like "*@*") {
$SplitAlias = [regex]::Split($NewAlias, "@")
$InvalidChar = "@" + $SplitAlias[1]
$NewAlias = $NewAlias -replace $InvalidChar; $_
$Count = $Count + 1
Write-Output "Issue $Count - Removed invalid @ in $OldAlias" >> $OutputFile
}
If ($NewAlias -ne $OldAlias) {
Set-Mailbox -Identity $Mailbox.Identity -Alias $NewAlias -EmailAddressPolicyEnabled:$false
Write-Output "Updated alias $OldAlias with new alias $NewAlias" >> $OutputFile
}
}
Write-Output "Completed Successfully" >> $OutputFile