Where oh where, did my AD site go…[Alternate title: It’s the DNS, stupid.]

I recently had a very confusing issue arise at one of my Exchange 2007 clients and I decided to share it with you. At this particular company, an Active Directory site is reserved for Exchange, and there are two domain controllers (both global catalog servers) in that AD site. The front-end is two CAS (CAS1 and CAS2) load-balanced by an ISA enterprise array with a CCR backend.

The week before, we had replaced all of the domain controllers in the forest with Windows Server 2008 R2 domain controllers, and bumped both the domain functional level and the forest functional level to Server 2008 R2 (we are going to enable the AD Recycle Bin). The new DCs replaced the old DCs and kept the original IP addresses.

That’s the setup.

An onsite technician was applying patches late one night (good for him!). Unfortunately, he patched and rebooted both of the Exchange AD site DCs at the same time (bad for him!). As you may already know – that makes Exchange very unhappy. System Center Operations Manager is also running in the environment and it immediately started to generate alerts about the missing domain controllers.

Sidebar: In Exchange 2003 and above, Exchange executes an Active Directory Topology discovery every 15 minutes. The specifics vary between versions of Exchange, but suffice it to say that, within 15 minutes, Exchange will find another DC/GC set (if they exist). In that case, your best bet is just to wait out that 15 minutes.

The technician reacted to the alerts from OpsMgr by rebooting the Client Access Servers. They both found out-of-site DCs and began working.

Then, the fun began. When the in-site DCs came back online (just a few minutes later), CAS1 reassociated with the in-site DCs and reset its secure channel to one of the in-site DCs. CAS2 did not.

The symptom of this is that all users connected to CAS1 through ISA were fine. However, the users that ISA connected to CAS2 were redirected through the same URL that they had already used – and CAS-to-CAS proxying did not work, so they couldn’t access any Exchange services – OWA, Exchange, anything. Quick workaround: remove CAS2 from the webfarm and RPC publishing in ISA so everything was routed through CAS1. However, redundancy is now lost.

Why this problem happened – I don’t know. The NetLogon service is responsible for maintaining the AD site a computer identifies itself with and maintaining the secure channel to a proper DC. However, for CAS2, NetLogon refused to reassociate to an in-site DC.

NetLogon bases site affinity on DNS. Both servers, CAS1 and CAS2, were configured identically for DNS. NetLogon uses a Windows API call named DsGetSiteName. In Windows Server 2008 and Windows Server 2008 R2 (and in Windows 7), you can use the nltest.exe utility to check this value. To wit:

PS C:\> nltest.exe /dsgetsite
Default-First-Site-Name
The command completed successfully
PS C:\>

Sidebar: nltest is available for Windows Server 2003 and Windows Server 2003 R2 as well, you just have to download and install the Windows Support Tools.

NetLogon does its check-and-reset once an hour, and upon startup. Once you know that, it should be easy to just restart the NetLogon service, right? Well, that didn’t make any difference.

So, we have the capability of forcing a particular secure channel for a server, and this also will set its AD site. To wit:

PS C:\> netdom.exe reset cas2 /server:DsEx2
The secure channel from CAS2 to DOMAIN was reset.
The command completed successfully.
PS C:\>

Note: nltest.exe has this functionality too, but netdom.exe has been around longer and I was more familiar with its parameters. See the SC_RESET parameter to nltest.

The AD site is updated, the secure channel is updated, and everything looks great. I declare success, put the server back into ISA, and move on.

An hour later, the client calls and says it is broken again.

Well, he’s right. The AD site has flipped back again and CAS2 is thus not operating properly. Obviously this has happened because NetLogon did its cycle.

C-r-a-p.

OK. Now its time to buckle down. AD sites are based on DNS. We know that. So, I ran dcdiag on all the servers. replmon on all the servers. Everything is clean.

But then visually examine the DC locator records in DNS – and… I find an extra one.

During the process of standing up all the new DCs, and configuring the new DCs with old permanent IP addresses, the OLD DCs ended up with the temporary IP addresses of the new DCs. Then, the old DCs were demoted.

All of the DCs but one cleaned up after themselves. The extra locator record was one of those DCs, and shockingly, now has stale DNS.

The fix? Remove the stale DC locator record. Reset the secure channel again, just to ensure it gets to the right place.

And Voila! It’s fixed.

If you’ve ever been to one of my installation seminars or read many of my articles, I talk about the importance of DNS in both Active Directory and Exchange Server. Here, yet again, is another example of that. Sometimes, you just have to take a look in the right place to find the problem.

Until next time…

If there are things you would like to see written about, please let me know.


Follow me on twitter: @EssentialExch

Speeding Reboot When Exchange is on a DC/GC

As I’ve noted in several previous blog entries (such as here and here), installing Exchange on a domain controller and/or a global catalog server is not a best practice. However, if you are running SBS (Small Business Server) or EBS (Essential Business Server) or if you only have a single server in your environment – you may not/don’t have much choice.

Given that you or your company may have no choice in the decision, it still may come as a disappointment (disgust?) that it takes so long to reboot your Exchange server.

This typically happens because of two primary reasons:

  • When Exchange is installed on a DC/GC, that Exchange server will refer to no other DC/GCs in the Active Directory, and
  • When a shutdown or reboot request is received, it isn’t possible for Exchange to terminate prior to Active Directory shutting down.

Now, you may think “poor poor Exchange, do what _I_ want anyway!” Well, in Exchange’s defense, that may be harder than you think. Consider a common scenario that may occur:

  • A VSS backup is running against your server and it’s just entered the Freeze stage against all writers
  • Exchange is running
  • RPC/HTTP is up
  • OWA is up
  • SQL is running\
  • …a shutdown request comes in

What is the right order to shut things down in that ensure everything gets shut down before AD starts shutting down?

The answer is – can’t be done!

Exchange and Active Directory have no mechanism for terminating the right things in the right order. So, it is up to a human brain to help them out.

I suggest you create a directory on your combination Exchange / Active Directory server named c:\scripts. Within that directory, create a file named shutdown.cmd. In that file, place the following commands:

echo %DATE% %TIME% Shutting Down Services >>c:\scripts\shutdown.txt
net stop msexchangeadtopology /y
echo %DATE% %TIME% Shut Down MSExchangeADTopology >>c:\scripts\shutdown.txt
net stop msftesql-exchange /y
echo %DATE% %TIME% Shut Down MSFteSQL-Exchange >>c:\scripts\shutdown.txt
net stop msexchangeis /y
echo %DATE% %TIME% Shut Down MSExchangeIS >>c:\scripts\shutdown.txt
net stop msexchangesa /y
echo %DATE% %TIME% Shut down MSExchangeSA >>c:\scripts\shutdown.txt
net stop iisadmin /y
echo %DATE% %TIME% Shut down IISAdmin >>c:\scripts\shutdown.txt
echo %DATE% %TIME% Shut down services script complete >>c:\scripts\shutdown.txt

Note that the echo statements are completely optional. They are simply present to allow you to record the sequence of events that does occur during a shutdown.

Once you have created this file, open Administrative Tools -> Group Policy Management.

Expand the domains node, then expand the node for your domain, and then expand the Group Policy Objects node.

Under the GPO node, right click on the Default Domain Controllers Policy and select Edit…

Expand Computer Configuration -> Policies -> Windows Settings and then click on Scripts.

In the right pane, double click on Shutdown, then click on Add in the dialog that opens. Browse to the shutdown.cmd that you created earlier and click OK.

Now, click OK until you are back to the group policy main window and close it and then close the Group Policy Management window.

If you have a single DC, you are done. Otherwise, wait for 15-20 minutes to allow your modified group policy to replicate to other DCs in your Active Directory.

Now, each time your DC that has Exchange Server installed on it reboots (or shuts down), it will execute the above script. This will reduce the required reboot time 50% – 75%.

Enjoy!

Until next time…

If there are things you would like to see written about, please let me know.


Follow me on twitter: @EssentialExch

The Experts Conference – TEC’2010

For the third time, I’ll be speaking at the upcoming The Experts Conference, sponsored by Quest.

I’ll be discussing using Exchange Web Services (EWS) from PowerShell — and for some reason, my abstract isn’t yet posted on the conference website! I need to get that corrected.

TEC’2010 is being held in Los Angeles, CA this year, from April 25 – 28. The Exchange track will have it’s strongest expert content ever, with MVPs and Microsoft personnel from all over the United States and Europe. Of course, as always, TEC’2010 will have a huge Directory Services and Identity Management track and is introducing a SharePoint track.

See the official TEC’2010 website for more information.

I hope you’ll come join me and many other people at TEC’2010. It’s a great time with lots of talk-time with some of the best folks in the business.

Until next time…

If there are things you would like to see written about, please let me know


Follow me on twitter: @EssentialExch

Exchange Server 2010 RTM and Organizational Health miscalculation

Exchange Server 2010 is starting to get some traction, with companies beginning to install it and put it into their test labs.

An issue – that is obviously a flat out bug – is that the Exchange Management Console (EMC) for Exchange Server 2010 misreports the number of Enterprise Client Access Licenses (CALs) that are required. It does this due to miscalculating Exchange ActiveSync policies.

To calculate the number of Enterprise CALs required, open the EMC and click on the Microsoft Exchange On-Premises node in the left-most pane of the console. After it completes expansion, in the right-hand Action pane, click “Collect Organizational Health Data” and then follow the prompts in the wizard. When the wizard is done, you’ll see an image similar to the one below. Note in the image that the License Summary reports needing 2003 Standard CALs and Enterprise CALs.

Organizational health for a single server with 2003 mailboxes

If you look at the Default Exchange ActiveSync policy, you can determine why. See the following two pictures:

Default Exchange ActiveSync policy property sheet, Device TabDefault Exchange ActiveSync policy property sheet, Device Applications tab

Note the text in both that indicates that MODIFYING the policies on the tab requires an Enterprise CAL. That is correct. The default policies, included in the Standard CAL (which are illustrated in the above two images), are available on this page. The specific licensing page for Exchange Server 2010 CALs is here.

However, Organizational Health currently reports that an Enterprise CAL is required if any of the above policies are set. That is incorrect.

In summary, there has been no change to the licensing of Exchange ActiveSync and the policies that were associated with Standard CALs in Exchange Server 2007 SP1 continue to be the same for Exchange Server 2010. Hopefully, the Organizational Health tool will be quickly repaired.

Until next time…

If there are things you would like to see written about, please let me know!


Follow me on twitter: @EssentialExch

Exporting Mailboxes Larger Than 2 GB On An Exchange Server

At one point, the MAPI used by Exchange was the same as the MAPI used by Outlook. But many years ago (literally – pre-Exchange 5.5) the MAPI used by Exchange server began to diverge from the MAPI used by Outlook. This isn’t particularly surprising, as the needs of a MAPI server are the inverse to the needs of a MAPI client. By Outlook 2003/Exchange 2003, a significant item was that client-MAPI (the MAPI used by Outlook) supports Unicode PSTs. Server-MAPI (the MAPI used by Exchange) only supports ANSI PSTs.

While there are MANY under-the-hood differences between the two types of PSTs, the key issue for most people is that ANSI PSTs are limited to 2 GB in size (the actual limit is about 1.8 GB of data, but this leads to a file size of just about 2 GB). Unicode PSTs do not have that limitation and can of any “reasonable” size. (They are limited by default to 20 GB, but can grow beyond that by adding a registry key for Outlook’s MAPI.)

This leads to a challenge on Exchange 2003 or Exchange 2007 servers when using ExMerge (yes, yes, ExMerge isn’t officially supported against Exchange 2007 but it works just fine). ExMerge can only use server MAPI. However, mailboxes may be larger than 2 GB. So what do you do?

Glen Scales, an Exchange MVP with a developer bent, developed a script in early 2007 to address this problem. Glen’s original script is here.

I’ve recently been working on a project for a large company and we needed to do this export against thousands of mailboxes. I started with Glen’s script and ran into a few issues, so I’ve more-or-less rewritten it; but the basic concept is the same – scan a mailbox on an Exchange server and break it into chunks. Each chunk will not be larger than 1.8 GB and each chunk will not contain any folder that contains more than 16,300 items (16K items per folder was another limit of ANSI PSTs).

I give great thanks to Glen for his original script, without his script this project would’ve been much harder.

If you actually want to know how the script works – I refer you to Glen’s original blog on the topic. The mechanism has not changed.

Without further ado…

''
'' ExMBspanPst.vbs
''
'' Based on a script from Glen Scales
'' http://gsexdev.blogspot.com/2007/01/exporting-mailbox-larger-then-2-gb-and.html
''
'' Requires Outlook Redemption, but not Outlook
'' http://www.dimastr.com/redemption
''
'' Fixes a few bugs:
''	orig. script didn't split at 16K messages in a folder
''	orig. script didn't report progress in 2, 3, ... n PSTs
''	orig. script could create two copies of a message in output PST
''	orig. script didn't send all status output to output file
''	orig. script didn't check for the presence of existing PST
'' Adds a feature or two:
''	accepts input mailbox as parameter
''	a number of stability improvements (error checks)
''	added "option explicit" and updated code for support of same
''	copies HiddenItems (Associated Items) and DeletedItems as well as normal items
'' Almost a full source reformat (so I could understand the code better)
'' Removed a fair bit of unused code (although I may have added more of my own)
'' Release resources whenever possible
'' Use RDO for all things, don't fall back to CDO
''
'' Update published with permission of Glen.
''
'' Michael B. Smith
'' The Essential Exchange
'' michael@TheEssentialExchange.com
''
Option Explicit

Dim mbMailbox      '' name of the mailbox (Exchange alias/mailNickname works best)
Dim servername     '' name of the Exchange server hosting the mailbox
Dim bfbaseFilename '' prefix used to name the new PST
Dim pfFilePath     '' directory in which to store PSTs

mbMailbox = WScript.Arguments(0)
''
'' these should be the only values you need to change
''
servername = "exchserver"
bfBaseFilename = "set1-" & mbMailbox
pfFilePath = "c:\temp\"
''
'' end change area
''

Dim fnFileName '' name of the output PST (set by CreatenewPst; uses pfFilePath, bfBasefileName and mbMailbox)
Dim fNumber    '' index of the output PST (will be updated to start at 1 by CreateNewPst)

fnFileName = ""
fNumber = 0

Dim doDictionaryObject '' scripting.dictionary, contains list of entry-ids present in current PST
Dim fso                '' scripting.filesystemobject
Dim RDOSession         '' redemption.rdosession

Set doDictionaryObject = CreateObject("Scripting.Dictionary")
Set fso                = CreateObject("Scripting.FileSystemObject")
set RDOSession         = CreateObject("Redemption.RDOSession")

Dim tsize       '' the next time I report the size of the new PST (that is, it's calculated size)
Dim tnThreshold '' maximum size (in MB) of a PST, before I switch to a new one

tsize = 10
tnThreshold = 1800

Dim PST
Dim IPMRoot
Dim pfPstFile            '' object for the new PST
Dim PstRootFolder        '' object pointing to the root of the current PST

PST           = Empty    '' PST is the Redemption pointer to the PST
IPMRoot       = Empty    '' IPMRoot is the root of the IPM subtree in the mailbox
pfPstFile     = Empty    '' fso.GetFile(fnFileName) returns the object for this file

PstRootFolder = Empty    '' This variable never actually gets set, but removing it would've
                         '' called for refactoring too much code - when the code is fixed
                         '' to set this value properly, other stuff breaks. That's why the
                         '' return values are commented out in ProcessFolder[Root | Sub].

Dim wfile                '' file we write to for informational messasges
Dim dfDeletedItemsFolder '' the deleted items folder in the current input mailbox
Dim miLoop               '' used for looping through IPMRoot.Folders
Dim fld                  '' used for looping through IPMRoot.Folders
Dim iMessageCount        '' total number of messages processed

iMessageCount = 0

	''
	'' MAIN code
	''

	On Error Resume Next
	Set wfile = fso.opentextfile(pfFilePath & bfBaseFilename & ".txt", 2, true)
	If Err Then
		WScript.Echo "Main: Error: Could not open " & pfFilePath & bfBaseFilename & ".txt"
		WScript.Quit 1
	End If
	On Error Goto 0

	msg "Main: debug output text file is " & pfFilePath & bfBaseFilename & ".txt"
	msg "Main: will attempt login to mailbox " & mbMailbox & " on server " & servername

	RDOSession.LogonExchangeMailbox mbMailbox, servername
	Set dfDeletedItemsFolder = RDOSession.GetDefaultFolder(3)
	Call CreateNewPst

	msg "Main: Enumerating Mailbox " & wscript.arguments(0)

	For miLoop = 1 to IPMRoot.Folders.Count
		Set fld = IPMRoot.Folders(miLoop)
		Call ProcessItems(fld)
		If fld.Folders.count > 0 then
			msg "Main: Calling Enumfolders for " & fld.Name
			Call Enumfolders(fld, PstRootFolder, 2)
		End if
		Set fld = Nothing
	Next

	msg "Main: A total of " & iMessageCount & " messages were processed."
	msg "Main: Done"

	'' clean up and release resources
	Set dfDeletedItemsFolder = Nothing
	RDOSession.Logoff
	wfile.Close
	Set wfile      = Nothing
	Set RDOSession = Nothing
	Set fso        = Nothing

Sub msg(ByVal str)
	WScript.Echo str
	wfile.WriteLine(str)
End Sub

Function Enumfolders(FLDS, RootFolder, ltype)
	''
	'' The current folder in the source mailbox is FLDS
	'' RootFolder should be the parent folder of the current folder
	''
	'' If ltype == 2, then process the non-folder items in the current folder (i.e., messages)
	'' If ltype == 1, then process the sub-folders in the current folder
	''
	Dim fl  '' used for looping through FLDS.Folders
	Dim fld '' used for looping through FLDS.Folders

	For fl = 1 to FLDS.Folders.count
		Set fld = FLDS.Folders(fl)
		If ltype = 1 then
			Call ProcessFolderSub(fld, RootFolder)
		Else
			Call ProcessItems(fld)
		End If

		msg "Enumfolders: " & fld.Name

		If fld.Folders.Count <> 0 then
			Call Enumfolders(fld, fld.EntryID, ltype)
		End if
		Set fld = Nothing
	Next
End function

Function CreateNewPst
	''
	'' conceivably, we should check ERR.number for almost every statement in this routine
	'' realistically, that would make the code almost unreadable and incomprehensible
	''
	Dim pstfld '' used for looping through PstRoot.Folders
	Dim fiLoop '' used for looping through IPMRoot.Folders
	Dim fld    '' used for looping through IPMRoot.Folders

	doDictionaryObject.RemoveAll
	fNumber = fNumber + 1
	fnFileName = pfFilePath & bfBaseFilename & "-" & fNumber & ".pst"

	msg "CreateNewPst: About to create new PST named " & fnFileName

	If fso.FileExists(fnFileName) Then
		msg "CreateNewPst: Error: PST already exists: " & fnFileName
		WScript.Quit 1
	End If

	If Not IsEmpty(PST) Then
		Set PST = Nothing
	End If
	Set PST = RDOSession.Stores.AddPSTStore(fnFileName, 1,  "Exported MailBox-" & now())

	If fnumber = 1 Then
		Dim pstroot

		Set pstroot = RDOSession.GetFolderFromID(PST.IPMRootFolder.EntryID, PST.EntryID)
		For Each pstfld In PstRoot.folders
			If pstfld.Name = "Deleted Items" Then
				doDictionaryObject.add dfDeletedItemsFolder.EntryID, pstfld.EntryID
				msg "CreateNewPst: Added Deleted Items Folder to dictionary"
				Exit For
			End If
		Next
		Set pstroot = Nothing
	End If

	If Not IsEmpty(IPMRoot) Then
		Set IPMRoot = Nothing
	End If
	Set IPMRoot = RDOSession.Stores.DefaultStore.IPMRootFolder

	msg "CreateNewPST: processing each new default folder in new PST"
	For fiLoop = 1 to IPMRoot.Folders.count
		Set fld = IPMRoot.Folders(fiLoop)
		If fld.Name <> "Deleted Items" Then
			PstRootFolder = ProcessFolderRoot(fld, PST.IPMRootFolder.EntryID)
		End If
		If fld.Folders.count > 0 Then
			Call Enumfolders(fld, fld.EntryID, 1)
		End If
		Set fld = Nothing
	Next

	If Not IsEmpty(pfPstFile) Then
		Set pfPstFile = Nothing
	End If
	Set pfPstFile = fso.GetFile(fnFileName)

	tsize = 10 '' back at the beginning now

	msg "CreateNewPst: Created new PST named: " & fnFileName
End Function

Function ProcessFolderRoot(Fld, parentfld)
	Dim newFolder '' next folder to be examined
	Dim CDOPstFld '' a particular folder parent in the PST based on the entryid of the PST

	msg "ProcessFolderRoot: " & fld.Name

	Set CDOPstfld = RDOSession.GetFolderFromID(parentfld, PST.EntryID)
	Set newFolder = CDOPstfld.Folders.ADD(Fld.Name)	
	'''ProcessFolderRoot = newFolder.EntryID
	newfolder.fields(&H3613001E) = Fld.fields(&H3613001E)

	doDictionaryObject.add Fld.EntryID, newfolder.EntryID

	Set newFolder = Nothing
	Set CDOPstfld = Nothing
End Function

Function ProcessFolderSub(Fld, parentfld)
	Dim newFolder '' next folder to be examined
	Dim CDOPstFld '' a particular folder parent in the PST based on the entryid of the PST

	msg "ProcessFolderSub: " & fld.Name

	Set CDOPstfld = RDOSession.GetFolderFromID(doDictionaryObject.item(parentfld), PST.EntryID)
	Set newFolder = CDOPstfld.Folders.ADD(Fld.Name)	
	'''ProcessFolderSub = newFolder.EntryID
	newfolder.fields(&H3613001E) = Fld.fields(&H3613001E)

	doDictionaryObject.add Fld.EntryID, newfolder.EntryID

	Set newFolder = Nothing
	Set CDOPstfld = Nothing
End Function

Sub ReportError(prefix, Fld, item, txt)
	msg prefix & " " & "Error Processing Item #" & item & " in " & Fld.Name & " " & txt
	msg prefix & " " & "EntryID of Item: " & Fld.items(item).EntryID
	msg prefix & " " & "Subject of Item: " & Fld.items(item).Subject
End Sub

Function CalcNewSize(pstFile, item)
	''
	'' calculate what the new physical size of the pstFile will be after adding the next item
	'' to it. do so safely, avoiding all possible faults, and return the value in megabytes,
	'' rounded up.
	''
	Dim pstSize, itemSize, totalSize

	On Error Resume Next
	pstSize = pstFile.Size
	If Err.Number Then
		pstSize = 1048576 '' assume 1 MB for the heck of it
	End If

	Err.Clear
	itemSize = item.Size
	If Err.Number Then
		itemSize = 1048576 '' assume 1 MB for the heck of it
	End If

	Err.Clear
	totalSize = Int ((pstsize + itemSize) / 1048576) + 1
	If Err.Number Then
		totalSize = 3
	End If
	On Error Goto 0

	CalcNewSize = totalSize
End Function	

Sub ProcessItems(Fld)
	Dim strType             '' the IPM type of the input folder
	Dim fiItemLoop          '' used to loop through the input folder
	Dim fiCDOcount          '' how many messages CDO told us to expect
	Dim pfPredictednewSize  '' predicted size of the output PST after the next message is written
	Dim dfDestinationFolder '' output folder in the current output PST
	Dim objMessages         '' collection of messages contained by the source folder
	Dim objMessage	        '' current message of interest from the source folder
	Dim srcFld              '' the source folder
	Dim strName             '' name of the source folder
	Dim i                   '' used as a dummy
	Dim iCount              '' how many messages have been stored in the output folder
	Dim totalMessagesRead
	Dim totalMessagesWritten

	iCount = 0
	totalMessagesRead = 0
	totalMessagesWritten = 0

	Const iCountmax = 16300 '' must be less than 16383, which is the number of messages that CAN be stored
                                '' per output folder in an ANSI PST

	strtype = Fld.fields(&H3613001E)

	'''' frankly, I don't understand the distinction below, it was in the
	'''' original code, but the two should be equivalent.
	If strType = "IPF.Contact" Then
		Set srcFld = Fld
	Else
		Set srcFld = RDOSession.GetFolderFromID(Fld.EntryID)
	End If
	strName = srcFld.Name

	For i = 1 to 3
		''' there are 3 collections in every folder that we might be interested in
		Select Case i
			Case 1
				Set objMessages = srcFld.Items
				msg "ProcessItems: " & strType & ": Processing Folder: " & strName & _
					" (contains " & objMessages.Count & " normal items)"
			Case 2
				Set objMessages = srcFld.HiddenItems
				msg "ProcessItems: " & strType & ": Processing Folder: " & strName & _
					" (contains " & objMessages.Count & " hidden/associated items)"
			Case 3
				Set objMessages = srcFld.DeletedItems
				msg "ProcessItems: " & strType & ": Processing Folder: " & strName & _
					" (contains " & objMessages.Count & " deleted items)"
		End Select

		fiCDOcount = objMessages.Count

		Set dfDestinationFolder = RDOSession.GetFolderFromID(doDictionaryObject.item(Fld.EntryID), PST.EntryID)

		For fiItemloop = 1 to fiCDOcount
			iCount            = iCount + 1
			totalMessagesRead = totalMessagesRead + 1

			If 0 = (fiItemLoop Mod 100) Then
				wscript.echo "... processing message " & fiItemLoop & " of " & fiCDOcount
			End If

			'' I SO wish VBScript had a Continue statement
			On Error Resume Next
			Err.Clear
			Set objMessage = objMessages(fiItemLoop)
			If Err.Number <> 0 Then
				msg "ProcessItems: corrupt message in folder, item number " & fiItemLoop & _
					" of " & fiCDOcount & ", 0x" & _
					Hex(Err.Number) & " (" & Err.Description & ")"
			Else
				On Error Goto 0

				pfPredictednewSize = CalcnewSize(pfPstFile, objMessage)
				If pfPredictednewSize >= tsize Then
					Wscript.echo "... additional 10 MB Exported, total size is now " & tsize & " MB" & _
						" (processing item #" & fiItemLoop & " of " & fiCDOcount & ")"
					tsize = tsize + 10
				End if

				If (pfPredictednewSize >= tnThreshold) or (iCount > iCountmax) Then
					msg "ProcessItems: " & strType & ": New PST about to be created - Destination - Number of Items : " & _
						dfDestinationFolder.Items.Count & _
						" (processing item #" & fiItemLoop & " of " & fiCDOcount & ")"

					Call CreateNewPst
					Set dfDestinationFolder = Nothing
					Set dfDestinationFolder = RDOSession.GetFolderFromID(doDictionaryObject.item(Fld.EntryID), PST.EntryID)

					iCount = 0
				End If

				On Error Resume Next
				Err.Clear
				objMessage.CopyTo(dfDestinationFolder)
				If Err.Number <> 0 Then
					Dim rdosrc

					Call ReportError ("ProcessItems: " & strType & ":", Fld, fiItemloop, "(copyto - likely fatal)")
					msg "ProcessItems: 0x" & Hex(Err.Number) & ": " & Err.Description
					Err.Clear

					''' Try to copy a slightly different way before giving up
					Set rdosrc = RDOSession.GetMessageFromID(objMessage.EntryId)
					rdosrc.CopyTo(dfDestinationFolder)
					If Err.Number <> 0 Then
						msg "ProcessItems: " & strType & ": (copyto): Also Failed RDO Copy"
						msg "ProcessItems: 0x" & Hex(Err.Number) & ": " & Err.Description
					Else
						msg "ProcessItems: " & strType & ": (copyto): Copied with RDO Okay"
						totalMessagesWritten = totalMessagesWritten + 1
					End If
					Set rdosrc = Nothing
				Else
					totalMessagesWritten = totalMessagesWritten + 1
				End If
			End If
			On Error Goto 0

			Set objMessage = Nothing
		Next
	Next

	msg "ProcessItems: " & strType & ": Source - Number of Items : " & totalMessagesRead & _
	    " Destination - Number of Items : " & totalMessagesWritten

	iMessageCount     = iMessageCount + totalMessagesRead

	Set dfDestinationFolder = Nothing
	Set objMessages         = Nothing
	Set srcFld              = Nothing
End Sub

Until next time…

If there are things you would like to see written about, please let me know!


Follow me on twitter: @EssentialExch

Exchange Server 2010 – Administrative Access to All Mailboxes

In Exchange 2010, the storage group has disappeared. Instead, the properties of a database and of a storage group have merged – the result being referred to as a database. Effectively, a database has been promoted to be as important as a storage group used to be.

You may could have predicted this coming from changes which happened in Exchange 2007, as a number of features required that a storage group only have a single database contained within those storage groups.

Regardless of which, a mailboxdatabase is unique within an Exchange 2010 organization. That means you can no longer have a mailboxdatabase named “Mailbox Database” or “Mailbox (servername)” on each and every server within your Exchange organization. Instead, each and every mailboxdatabase name is unique. This is guaranteed by a many-digit number suffixed to the end of a mailbox database’s name.

This does simplify some aspects of administration – instead of having to specify server\storage-group\database in order to name a specific database, you can now specify simply the database name. However, the name of that database may be something like “Mailbox Database 1015374940” (which is the name of the mailbox database hosting my production domain). That is somewhat more challenging to remember. Just somewhat. HAH.

One of the changes involved in moving databases to be organizational objects instead of server objects makes it practical to (again – after skipping Exchange 2007) allow a single user or group administrative access to all Exchange 2007 mailboxes.

Of course, this can be done from the GUI – however, the GUI you must use is LDP.exe or ADSIEdit.msc – not the Exchange Management Console (EMC).

However, this is probably easier to do from the Exchange Management Shell (EMS), given that you know a couple of key facts: the distinguishedname of your Active Directory domain and any of three formats for a user/group you want to allow this access.

Note that allowing Administrative Access to all mailboxes can be tracked by logging – but only if that logging is enabled – and that logging is not enabled by default. Also note that there may be legal issues associated with allowing specific users or groups access to all mailboxes in your organization – I recommend that every organization have a information access and security policy that includes corporate access and use of electronic mail. Finally, this information is provided for instructional purposes and I accept no liability for providing this information or to any use to which it may be put.

Now that I’ve covered my rear….

If, for example, your forest is named named example.com, then the distinguished name of that forest is DC=example,DC=com. If your forest is named SBS.Example.Local, then the distinguishedname of the forest is DC=SBS,DC=Example,DC=Local. Now, remember that. 🙂

In terms of specifying a user or group name that you are going to provide access, you have three possible formats:

NetBIOS-domain-name\principal-name

Active-Directory-forest-name/container-or-organizational-unit/principal-name

CN=principal-name,OU=organizational-unit,DC=example,DC=local

For example, if your Active Directory forest name is example.local and the NetBIOS domain name is EXAMPLE, and the security principal is named TEST and that principal is located in the Users container, you would have these examples:

EXAMPLE\TEST

example.local/Users/TEST

CN=TEST,CN=Users,DC=example,DC=local

Finally, using the above example, you would have this PowerShell command:

Add-AdPermission –Identity “CN=Databases,CN=Exchange Administrative Group (FYDIBOHF23SPDLT),CN=Administrative Groups,CN=First Organization,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=example,DC=local” –User EXAMPLE\TEST –InheritedObjectType msExchPrivateMDB –extendedRights Receive-As –inheritanceType Descendents

Or, if we were to expand this out a little bit:

$principal = “EXAMPLE\Test”
$domain = “DC=example,DC=local”
$identity = “CN=Databases,” +
“CN=Exchange Administrative Group (FYDIBOHF23SPDLT),” +
“CN=Administrative Groups,CN=First Organization,” +
“CN=Microsoft Exchange,CN=Services,CN=Configuration,” +
$domain
Add-AdPermission –Identity $identity –User $principal `
–InheritedObjectType msExchPrivateMDB `
–extendedRights Receive-As `
–inheritanceType Descendents

Until next time…

If there are things you would like to see written about, please let me know!


Follow me on twitter: @EssentialExch

Microsoft Exchange Server 2010 Release Candidate

After a wait of almost 24 hours – the download is available.

Yesterday, the Microsoft Exchange team announced the immediate availability of the Exchange Server 2010 Release Candidate with this posting. However…. there was a complication… it wasn’t available!

As of a few minutes ago (1630 EST) the situation was resolved and you can now download the RC here.

A couple of quick notes about this release:

  • This is the Enterprise edition of Exchange Server
  • To use the DAG functionality, you’ll need the Enterprise Edition of Windows Server
  • Windows Server 2008 SP2 and Windows Server 2008 R2 are both supported – but no version of Server 2003 or earlier version of Server 2008
  • You will be able to upgrade to the released version of Exchange Server when it is released, but you CANNOT upgrade from Enterprise RC to Standard RTM
  • You cannot install DAG (which uses Windows Failover Clustering) and NLB on the same server (this is a Windows restriction, not an Exchange restriction)
  • This is an RC – that is, it is feature complete but may still have bugs.

There is a huge amount of additional functionality over Exchange Server 2007 – however, much of it is only available in the Exchange Management Shell. Since this release is feature complete, I don’t expect that to change prior to RTM.

Until next time…

If there are things you would like to see written about, please let me know!


Follow me on twitter: @EssentialExch

Mailbox Permissions: Pulling Back the Curtain…

Let’s talk about mailbox permissions. People often get a little confused between store-level mailbox permissions and Active Directory-level mailbox permissions. They are similar but not the same. For clarity it may help for us to look at them all.

Note: this post is written specifically against Exchange Server 2007. For Exchange Server 2010, storage groups disappear – mailbox stores acquire storage group attributes, they are promoted to equal status (you could see this coming, as a number of features in Exchange Server 2007 only worked when you had a single mailbox store per storage group). So, the contents of this post apply equally to Exchange Server 2010 – just where-ever you see “storage group”, replace that with “mailbox store”.

Mailbox permissions include: FullAccess, SendAs, ExternalAccount, DeleteItem, ReadPermission, ChangePermission, and ChangeOwner. This list does not include “Send on Behalf”. That’s because a user can set “Send on Behalf” for another user by defining the other user as a delegate and that’s handled separately from mailbox permissions.

Relevant Active Directory permissions include: FullControl, SendAs, ReceiveAs, Delete, and ViewInfoStoreStatus. Three of these (SendAs, ReceiveAs, and ViewInfoStoreStatus) are so-called “extended rights”, which means they are handled somewhat differently than standard access rights.

The AD permission ViewInfoStoreStatus allows a specific user or group to do just that – view the status of an information store. It doesn’t map to anything at the mailbox level. I don’t believe that it is used in Exchange 2007 and above. It had applicability in the Exchange 2000 and Exchange 2003 timeframe when administration of Exchange was handled at the “Administrative Group” level, and ViewInfoStoreStatus was assigned to an administrative group for the administrators of that administrative group (and set to inherit down through all the servers and stores in that group).

The AD permission FullControl includes Delete, SendAs, and ReceiveAs at the AD level. At the mailbox level this maps to FullAccess and SendAs.

The AD permission ReceiveAs maps to FullAccess at the mailbox level. Note that this does not include SendAs or ExternalAccount permissions. There is no way (even though some Microsoft documentation states otherwise) to provide read-only access to a mailbox via permissions.

The AD permission SendAs maps to SendAs at the mailbox level. Note that while it is possible to set Send-As on the mailbox itself, without having it set in AD, you will not be able actually Send-As using Outlook – it depends on Send-As being set within AD.

The AD permission Delete maps to DeleteItem at the mailbox level.

The store permission FullAccess includes all mailbox permissions except SendAs and ExternalAccount.

Setting the AD permission does not cause the mailbox permission(s) to be set (AD has no direct knowledge of Exchange). One must presume that the information store service is smart enough to check both. I have no idea of the official precedence map of AD permissions vs. store permissions. However, behavior indicates that AD permissions are evaluated first, and if they produce a “pass” then the store permissions are evaluated to get the final result (similar to the “share” vs. “NTFS” precedence rules).

The AD permissions can be set on a storage group or a mailbox store, and apply to all mailboxes in that storage group or mailbox store (if set for inheritance). There is no mechanism to do that within the mailbox store itself (that is, there is no cmdlet for Add-MailboxDatabasePermission or Add-StorageGroupPermission, nor do I believe that a store has a concept of a security hierarchy above the mailbox level). Also, while you can set SendAs at the storage group or mailbox store level, this just means that you can impersonate the storage group or mailbox store – it does not mean that you can Send-As for all accounts in that storage group or mailbox store. That permission must be set on a per-mailbox basis.

There is a good whitepaper for understanding store and AD permissions written against Exchange 2000 and Exchange 2003. It is still at microsoft.com/downloads : Working with Store Permissions in Microsoft Exchange 2000 Server and Exchange Server 2003. However, it is a little dated and there are a couple of errors in the document. The basics are still good, but Exchange 2007 reintroduced the idea of setting actual permissions on the mailbox (in 2000 and 2003, you could set mailbox permissions only before the mailbox was created, everything else was set against the AD user object). Exclusive of the impact of Role Based Access Control (commonly referred to as RBAC), I believe that Exchange Server 2010 continues to follow the Exchange Server 2007 rules.

While we have not discussed them here, note that the store-level permissions available to mailboxes are the same permissions available to public folders (excepting only ExternalAccount). And, with the exception of SendAs and ExternalAccount, these are the same permissions available to subfolders within a mailbox and a public folder.

Implementation note:

From a technical perspective, the AD attributes actually represent Access Control Entries set within the nTSecurityDescriptor object assigned to a user object within Active Directory.

For more information on Access Control Lists, Access Control Entries, and the nTSecurityDescriptor object, see Displaying Security on Active Directory, Exchange, and Registry Objects and Windows Permissions – Access Control Lists.

Until next time…

If there are things you would like to see written about, please let me know!

P.S. Thanks to Ross Smith, IV of Microsoft for clarifying a couple of points contained in this article.


Follow me on twitter: @EssentialExch

Mailbox Permissions: Pulling Back the Curtain…

<p>Let’s talk about mailbox permissions. People often get a little confused between store-level mailbox permissions and Active Directory-level mailbox permissions. They are similar but not the same. For clarity it may help for us to look at them all.</p>
<blockquote>
<p>Note: this post is written specifically against Exchange Server 2007. For Exchange Server 2010, storage groups disappear – mailbox stores acquire storage group attributes, they are promoted to equal status (you could see this coming, as a number of features in Exchange Server 2007 only worked when you had a single mailbox store per storage group). So, the contents of this post apply equally to Exchange Server 2010 – just where-ever you see “storage group”, replace that with “mailbox store”.</p></blockquote>
<p>Mailbox permissions include: FullAccess, SendAs, ExternalAccount, DeleteItem, ReadPermission, ChangePermission, and ChangeOwner. This list does not include “Send on Behalf”. That’s because a user can set “Send on Behalf” for another user by defining the other user as a delegate and that’s handled separately from mailbox permissions.</p>
<p>Relevant Active Directory permissions include: FullControl, SendAs, ReceiveAs, Delete, and ViewInfoStoreStatus. Three of these (SendAs, ReceiveAs, and ViewInfoStoreStatus) are so-called “extended rights”, which means they are handled somewhat differently than standard access rights.</p>
<p>The AD permission ViewInfoStoreStatus allows a specific user or group to do just that – view the status of an information store. It doesn’t map to anything at the mailbox level. I don’t believe that it is used in Exchange 2007 and above. It had applicability in the Exchange 2000 and Exchange 2003 timeframe when administration of Exchange was handled at the “Administrative Group” level, and ViewInfoStoreStatus was assigned to an administrative group for the administrators of that administrative group (and set to inherit down through all the servers and stores in that group).</p>
<p>The AD permission FullControl includes Delete, SendAs, and ReceiveAs at the AD level. At the mailbox level this maps to FullAccess and SendAs.</p>
<p>The AD permission ReceiveAs maps to FullAccess at the mailbox level. Note that this does <strong>not</strong> include SendAs or ExternalAccount permissions. There is no way (even though some Microsoft documentation states otherwise) to provide read-only access to a mailbox via permissions.</p>
<p>The AD permission SendAs maps to SendAs at the mailbox level. Note that while it is possible to set Send-As on the mailbox itself, without having it set in AD, you will not be able actually Send-As using Outlook – it depends on Send-As being set within AD.</p>
<p>The AD permission Delete maps to DeleteItem at the mailbox level.</p>
<p>The store permission FullAccess includes all mailbox permissions except SendAs and ExternalAccount.</p>
<p>Setting the AD permission does not cause the mailbox permission(s) to be set (AD has no direct knowledge of Exchange). One must presume that the information store service is smart enough to check both. I have no idea of the official precedence map of AD permissions vs. store permissions. However, behavior indicates that AD permissions are evaluated first, and if they produce a “pass” then the store permissions are evaluated to get the final result (similar to the “share” vs. “NTFS” precedence rules).</p>
<p>The AD permissions can be set on a storage group or a mailbox store, and apply to all mailboxes in that storage group or mailbox store (if set for inheritance). There is no mechanism to do that within the mailbox store itself (that is, there is no cmdlet for Add-MailboxDatabasePermission or Add-StorageGroupPermission, nor do I believe that a store has a concept of a security hierarchy above the mailbox level). Also, while you can set SendAs at the storage group or mailbox store level, this just means that you can impersonate the storage group or mailbox store – it does not mean that you can Send-As for all accounts in that storage group or mailbox store. That permission must be set on a per-mailbox basis.</p>
<p>There is a good whitepaper for understanding store and AD permissions written against Exchange 2000 and Exchange 2003. It is still at microsoft.com/downloads : <a href=”http://www.microsoft.com/downloads/details.aspx?familyid=2ae266f0-16b7-40d7-94d9-c8be0e968a57&amp;displaylang=en” target=”_blank”>Working with Store Permissions in Microsoft Exchange 2000 Server and Exchange Server 2003</a>. However, it is a little dated and there are a couple of errors in the document. The basics are still good, but Exchange 2007  reintroduced the idea of setting actual permissions on the mailbox (in 2000 and 2003, you could set mailbox permissions only before the mailbox was created, everything else was set against the AD user object). Exclusive of the impact of Role Based Access Control (commonly referred to as RBAC), I believe that Exchange Server 2010 continues to follow the Exchange Server 2007 rules.</p>
<p>While we have not discussed them here, note that the store-level permissions available to mailboxes are the same permissions available to public folders (excepting only ExternalAccount). And, with the exception of SendAs and ExternalAccount, these are the same permissions available to subfolders within a mailbox and a public folder.</p>
<p><strong>Implementation note:</strong></p>
<p>From a technical perspective, the AD attributes actually represent Access Control Entries set within the nTSecurityDescriptor object assigned to a user object within Active Directory.</p>
<p>For more information on Access Control Lists, Access Control Entries, and the nTSecurityDescriptor object, see <a href=”http://theessentialexchange.com/blogs/michael/archive/2007/11/13/displaying-security-on-active-directory-exchange-and-registry-objects.aspx” target=”_blank”>Displaying Security on Active Directory, Exchange, and Registry Objects</a> and <a href=”http://theessentialexchange.com/blogs/michael/archive/2007/11/13/windows-permissions-access-control-lists.aspx” target=”_blank”>Windows Permissions – Access Control Lists</a>.</p>
<p>Until next time…</p>
<p>If there are things you would like to see written about, please let me know!</p>
<p>P.S. Thanks to Ross Smith, IV of Microsoft for clarifying a couple of points contained in this article.</p>
<hr><p>Follow me on twitter: @EssentialExch</p>

TEC’2009 Berlin (The Experts Conference)

TEC’2009 is coming up soon… I’ll be there and I hope you will be too. TEC is a place to get acquainted with some of the top folks in Active DIrectory, ILM/FIM, and Exchange.

Here is what Gil Kirkpatrick, the founder of the conference had to say:

 

I just wanted to remind all the list denizens that I will again be hosting The Experts Conference Europe this September 14-16 in Berlin, Germany.
TEC is comprised to two conferences this year. TEC/Identity and Access features speakers from Microsoft (Alex Weinert, Markus Vilcinskas, and Tomasz Onyszko for ILM/FIM 2010, Dean Wells, Nathan Muggli, and Brett Shirley for DS, and Matt Steele for Geneva), as well as notable MVPs Guido Grillenmeier, Jorge de Almeida-Pinto, and Brian Desmond.
TEC/Exchange includes Ross Smith IV, Greg Taylor, and Brett Shirley from Microsoft, as well as Exchange MVPs Ilse van Criekinge and Michael B. Smith.
You can see the entire TEC agenda at http://www.tec2009.com, and if you have any questions, contact me at gil.kirkpatrick@quest.com.
It would be great to see some of the activedir crowd at TEC this year. If you need to come up with a justification, well, listening to brettsh explain the innards of ESE is worth the price of admission right there.
-gil
I hope to see you there!

 


Follow me on twitter: @EssentialExch