Archive | Exchange

After removing last Exchange 2003 Server, mail enabled public folders no longer work

We decommissioned our last Exchange 2003 server the other day. All seamed well and I thought we were done.

Nope.

A few days later we had our scheduled downtime to apply patches, and I rebooted all three of our Exchange 2010 servers (for the record we are Exchange 2010 SP1 UR3 v3). Next morning people were getting bounce backs from messages destined to mail enabled public folders. The error  in the bounce back was:

#554 5.2.0 STOREDRV.Deliver.Exception:ObjectNotFoundException; Failed to process message due to a permanent exception with message The Active Directory user wasn’t found. ObjectNotFoundException: The Active Directory user wasn’t found. ##

I found this thread and this post. The error (as both posts suggested) boiled down to an empty server container from the decommissioned Exchange 2003 infrastructure. This blog post recommend against cleaning all the old entries, so we were nervous about making the change. Seemed unlikely that a single EMPTY container would be causing such a large issue.

We contacted technical support (without mentioning our findings) and they said the exact same thing as our findings above – to remove the empty servers container. When I asked why this single container is causing the problem, they said “This happens because we assume that if a Servers container exists, there will be a System Attendant object somewhere inside it.” To me, this really does not explain “why”, but it was all they offered. We also asked if the deletion of the servers container would impact any other systems and they said “no”. Our final question was: once deleted, did we need to restart services/servers, and they said “no”.

We backed up AD (system state and the C: drive of the PDC) and deleted the empty servers container, held our breath, and . . . all was good. Mail immediately started flowing to the mail enabled public folders.

I thought I would write this up to supplement the above posts, and to add to our experience. Maybe this will help some one? Leave a comment if this helped!

PowerShell script to change default prf imported when Outlook starts up for the first time

In this previous post, I talk about how we use Office Customization Tool (OCT) and “.prf” files to deploy Office 2010. Continuing with the idea that  I want to know if a person is visiting from another office, I want to be able to switch from our default of “cached mode” to “online mode” for that visitor. I wrote this script with the logic of: IF {visiting from other office}, THEN {JBMURPHY-Install-ChangeDefaultOutlookPRF -CachedMode $false}.

This script would change where the ImportPRF registry entry points (in this example to a “.prf” file with cached mode disabled)

function JBMURPHY-Install-ChangeDefaultOutlookPRF {
 PARAM($CachedMode=$TRUE)
foreach ($PATH in (gci "HKLM:\SOFTWARE\Microsoft\Office\14.0\User Settings\*{*")){
 $ImportPRFRegPATH=$PATH.Name.Replace("HKEY_LOCAL_MACHINE","HKLM:")+"\Create\Software\Microsoft\Office\14.0\Outlook\Setup"
 If (Test-Path $ImportPRFRegPATH){
  $ImportPRFPath=$(get-itemproperty($ImportPRFRegPATH)).ImportPRF
  write-host -NoNewline "`nIportPRF=$ImportPRFPath - "
  if ($CachedMode) {
	if ($ImportPRFPath -eq "C:\PROGRA~1\MICROS~1\WITHCA~1.PRF") { write-host "Already in CachedMode"}
	else {write-host "Enabling CachedMode"
	Set-ItemProperty $ImportPRFRegPATH -Name ImportPRF -Value "C:\PROGRA~1\MICROS~1\WITHCA~1.PRF"
	write-host "Now IportPRF=$($(get-itemproperty($ImportPRFRegPATH)).ImportPRF)"
	}
  }
  else {
	if ($ImportPRFPath -eq "C:\PROGRA~1\MICROS~1\WITHOU~1.PRF") { write-host "CachedMode already turned off"}
	else {write-host "Turning Off CachedMode"
	Set-ItemProperty $ImportPRFRegPATH -Name ImportPRF -Value "C:\PROGRA~1\MICROS~1\WITHOU~1.PRF"
	write-host "Now IportPRF=$($(get-itemproperty($ImportPRFRegPATH)).ImportPRF)"
	}
  }
 }
}

PowerShell script to add users to a group

In this previous post : PowerShell wrapper for creating a new distribution group, I created a script for creating a new distribution group. I wanted to take that a step further and prompt the SysAdmin to add users. I created a new recursive function called AddToDistributionGroup. In this code, I prompt for a group name, and a user to add. The SysAdmin types in the first few parts of the name (I could have used samaccountname) and then I then loop through ADusers with that name asking the SysAdmin if that is the user they want to add.

function JBMURPHY-EXCHANGE-AddToDistributionGroup {
Param(	[parameter(Mandatory = $true)]$GroupName,
	[parameter(Mandatory = $true)]$UserToAdd)
JBM-EXCHANGE-StartPSSESSION
if (!($GroupName)) {write-host "you need to specify a group name"
break}
if (($UserToAdd)) {
 $UserToAdd=$UserToAdd+"*"
 Get-aduser -filter {(name -like $UserToAdd) -and (Enabled -eq $true)} | foreach-object {
  $UserName=$_.Name
  $message = "Add $UserName to the group: $GroupName"
  $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes add $UserName to the group $GroupName"
  $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","No, don't add $UserName to the group $GroupName?"
  $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
  $result = $host.ui.PromptForChoice($title, $message, $options, 0) 
  if ($result -eq 0){
   write-host "Adding $UserName"
   Add-DistributionGroupMember -Identity $GroupName -Member $UserName
  }
 }
JBM-EXCHANGE-AddToDistributionGroup $GroupName
}
}

* Note, there is not any error checking to see if the group exists. I am mainly using this code to be called from a NewDistributionGroup script, where I know the name of the group. I may add a lookup to see if the group exists at some point.

** Now that I think about it, this is for any type group, not just distribution groups.

PowerShell wrapper for creating a new distribution group

Unknown to me, in Exchange 2010 when you create a new distribution group in EMC, by default, the group will not receive email from external recipients – the setting “Require that senders are authenticated” is checked. We use distribution groups to communicate with clients, so unauthenticated senders need to email these groups.  This setting is on the Mail Flow Setting – Message Delivery restrictions page.  I wrote a simple wrapper script to create a new distribution group and turn off the “Require that senders are authenticated” setting:

function JBMURPHY-EXCHANGE-NewDistributionGroup {
Param([parameter(Mandatory = $true)]$GroupName)
Write-host "Creating group named $GroupName"
new-DistributionGroup -Name $GroupName -OrganizationalUnit 'site.name/OUName' -SamAccountName $GroupName -Alias $GroupName
Set-DistributionGroup $GroupName -RequireSenderAuthenticationEnabled $false
write-host "The $GroupName distribution group has been created."
}

Simple one. I know.

PowerShell wrapper scripts for Exchange 2010 – first step: make a connection

As I talked about in this previous post, I like to write wrapper scripts that collect input and pass it along to the actual provided functions. I call these wrapper scripts because they are not really doing anything ground breaking, they are just a series of conditionals and commands that I put together, with a common naming convention. Then, all we have to do is tab completion through the scripts that I have written.

I wanted to do the same for creating new distribution groups in Exchange 2010, but first ,I needed to make  the Exchange 2010 PowerShell functions available on our local machines. I wrote the following function that starts a PSSession on the exchange server. This function will be called at the beginning of every Exchange wrapper script, guaranteeing that we have a connection to the Exchange PowerShell functions.

Here is that function:

Function JBMURPHY-EXCHANGE-StartPSSESSION {
if(! (Get-PSSession | Where-Object { $_.ComputerName -like "servername.company.com" })){
Write-Host "Createing PSSession to SVNYEXCH01.SARDVERB.LOCAL" -ForegroundColor Green
Import-PSSession (New-PSSession -Configurationname Microsoft.Exchange –ConnectionUri http://servername.company.com/powershell) | out-null
}
}

PowerShell, Event Triggers, MailboxMoves and email notification

We are in the process of moving our Mailboxes from Exchange 2003 to Exchange 2010. I am using this script to automate the moves. I wanted to find a way to get an email notification when the move is complete. I figured I could keep the PowerShell script  running in a “while loop” until Get-MailBoxMoveStatistics reports “Completed”, but I wanted to write a more generic notification script.

One of the features that I had been itching to play with in 2008 is the “attach a task to this event”, so I wrote the following generic PowerShell script to receive a set of values from a triggered event. This could be used for any event that you can attach a task to. Big picture is that I am passing the event id to the script, and then using PowerShell to query the event log to get the rest of the log entry. This is then emailed.

Before we get to the script, by default, the scheduled task created by the wizard does not pass any values to the script it is running. I found this article that described how to modify your “Event Viewer Task” to pass values to the attached action. Use the method he describes to export the task and the re-import with the following :

      <ValueQueries>
        <Value name="eventData">Event/EventData/Data</Value>
        <Value name="eventRecordID">Event/System/EventRecordID</Value>
      </ValueQueries>

The actual command that is attached to the event is:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command SendEventRecord.ps1 -subject "MailBox Move Completed" -eventid $(eventRecordID)

And here is the PowerShell script called SendEventRecord.ps1. This script received paramaters from the command above. This script uses the eventRecordID in the event log item to look up the actual error and email the contents.

Param($Subject, $Body,$EventID)

$Body += Get-EventLog -LogName Application | Where-Object {$_.Index -eq $EventID} | fl | out-string

$emailFrom = "[email protected]"
$emailTo = "[email protected]"
$emailsubject = $Subject
$emailbody = $Body
$smtpServer = "server.ip.address"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $emailsubject, $emailbody)

Now, when a 1107 appears in the event log, the eventRecordID is passed to a PowerShell script that looks up the record/event and emails it to the right people! I like this one.

PowerShell to assign Exchange 2010 Retention Policies based on AD group membership

In exchange 2003 we maintained a 6 month and 12 month purge policy that was applied based on group membership. I described that configuration here. I wanted to migrate our purge policies to the new 2010 Recipient policies. I needed to find the members of an AD group and assign a retention policy to their mailbox. Here is my script:

Get-ADGroupMember mailboxpurge-6m | foreach {
if ((get-mailbox $_.samaccountname).ServerName -eq "server01" -OR (get-mailbox $_.samaccountname).ServerName -eq "server02" )
{
write-host "User" + $_.samaccountname + "'s current policy is: " + (get-mailbox $_.samaccountname).RetentionPolicy
set-mailbox $_.samaccountname -RetentionPolicy 6MonthPurge
}
}

Retention Policies in Exchange 2010

I have to say that retention policies in Exchange 2010 are a bit confusing. I understand that they are more powerful and it gives users more control in marking items that should not be purged. But, it is just easier in exchange 2003, you can select the folders you want and select “delete immediately”. For users created folders, you select “all other mail folders”

In 2010, it took me a while to realize that, to create a purge policy that only removed mail items, I needed to create Calendar, Contacts, and Notes policies and “Disable this Tag” .

Get-RetentionPolicyTag Calendar-NeverDelete | fl Name,MessageClass, RetentionEnabled,Type
Name             : Calendar-NeverDelete
MessageClass     : *
RetentionEnabled : False
Type             : Calendar

. . .  and in the gui:

That seems backwards to me.

Scheduled PowerShell script to resume Mailbox Moves (New-MoveRequest)

Moving mailboxes from 2003 to 2010 is easy, but there is not a “Gui” to schedule it. So I created the following PowerShell script to find all the suspended MoveRequest(s) with the correct date, and resume them.
MailboxMove.ps1:

$TodaysDate = (get-date).day.ToString() + (get-date -format MMMM)
Get-MoveRequest -MoveStatus Suspended | Get-MoveRequestStatistics | Where {$_.BatchName -like "*$TodaysDate*"} | Resume-MoveRequest

Next I create a scheduled task with the following command to run a script with the code above:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command ". 'C:\Program Files\Microsoft\Exchange Server\V14\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto; c:\Scripts\MailboxMove.ps1"

Now when I create a New-MoveRequest,

New-MoveRequest -Identity username -TargetDatabase MailboxDatabase02 -BatchName "19August" -BadItemLimit 10 -Suspend

I know that a scheduled task will resume the move a the correct time.

To monitor progress, I have been using this script:

Get-MoveRequest  | Get-MoveRequestStatistics | ft -auto alias,Status,TotalMailboxSize,PercentComplete,TargetDatabase,TotalInProgressDuration,BytesTransferred,BatchName

Force the Light Version of Exchange 2010 OWA on a Cisco ASA WebVPN with SSO

We have a Cisco ASA in front of our Exchange 2010 OWA application. We needed to create a “bookmark” to point to OWA. Since we are using Forms based authentication, we did not want users to have to re-login after just logging into the ASA. I found this link on how to setup the Bookmark for SSO and Exchange 2010 OWA. We wanted to take it a step further and force the “Light version” of OWA until we could upgrade the ASA code (we are using an older rev and OWA JavaScript is not working correctly). To do this you need to change the “flags” post value to “1”.

So, to “Force the Light Version of Exchange 2010 OWA on a Cisco ASA WebVPN with SSO” you need to do the following:

Configuration -> Remote Access VPN -> Clientless SSL VPN Access -> Portal -> Bookmarks -> Add/Edit your Bookmarks
URL: https :///owa/auth/owaauth.dll

Advanced Options: Post
destination : https:///owa/
flags : 1
forcedownlevel : 0
trusted : 0
username : <yourdomain>\CSCO_WEBVPN_USERNAME
password : CSCO_WEBVPN_PASSWORD
SubmitCreds : Login
isUtf8 : 1