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 atgil.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

MAPI in the registry (Or, setting the “use HTTP first” boxes via VBScript)

First off, I didn’t use PowerShell because I use the script in this article as a login script – and while I’m sure that the Windows Scripting Host is installed everywhere, I can’t be sure that PowerShell is installed everywhere. It will be a couple of years before I can do that.

Beginning with Exchange 2003 and Outlook 2003 (when running on Windows 2003), you could use RPC/HTTP (the feature was renamed to Outlook Anywhere with Exchange 2007). RPC/HTTP provides the capability of encapsulating the native protocol that Outlook uses to communicate with Exchange Server with a secure HTTP tunnel. Long story short, it’s a way of safely getting around firewalls.

The name of the protocol that Outlook prefers to use with Exchange Server is called MAPI – Message Applications Programming Interface. Let’s suffice it to say that MAPI is very powerful, very extensible, allows you to do darn near anything with a message or with Exchange that you might ever want to, and as is typical with all that power – MAPI is complicated to use. And you won’t be using MAPI in a script!

I can’t give MAPI justice in a blog post. But let’s talk about a few basic MAPI concepts.

Providers – a MAPI provider is some type of a server process (note: this does NOT mean that it has to run on a server, just that it provides services to clients). A few sample MAPI providers include the Address Book provider, the Spooler (which queues and sends email to Exchange), and the Profile provider (which is responsible for managing Mail profiles on a per-user basis on a client workstation).

Profile – a MAPI profile defines everything that a messaging client (such as Outlook) may need to know about communicating with an email server. A single profile may contain information about all of the various providers available to the client, both locally and on remote servers.

Property – a MAPI property is no different than any other type of property. It contains a value that is relevant to a specific provider and that value is contained within a specific profile. In general, a MAPI property is referred to via something called a PROP_TAG (a property tag) which is a 32 bit binary value. The first 16 bits represent the type of the property (such as integer or string, etc.) plus a special flag bit or two. The second 16 bits of a property is the specific property identifier.

The profile provider, which can be accessed via raw MAPI (of course), uses the system registry to store information about MAPI properties, profiles, providers, and other MAPI objects. Officially, these items are opaque. However, they have been the same since Windows 95, so it’s unlikely that they will be changing any time soon.

In the registry, each MAPI service associated with a particular profile has a GUID, which is a string. The particular GUID is assigned to the MAPI service, and is consistent across all MAPI installations. Each MAPI service has a series of property values assigned to it, and those property tags are represented by an 8-character value which is the hexidecimal representation of the binary property tag.

Today, we are particular interested in a single MAPI service – the Exchange Server Details MAPI service. This service is responsible for the connectivity to an Exchange server via the MAPI protocol. It has the following GUID:

Const ExchDetails = “13dbb0c8aa05101a9bb000aa002fc45a

EVERY MAPI profile which connects to an Exchange server will have that MAPI service defined and its property values populated. The particular MAPI property we are interested in is named PR_ROH_FLAGS. It looks like this:

Const PT_LONG = “0003” ‘ hex MAPI type for a 4-byte binary value

PR_ROH_FLAGS = PT_LONG & “6623” ‘ PROP_TAG (PT_LONG, 0x6623)

therefore the resultant value for PR_ROH_FLAGS is 00036623. Any MAPI profile which currently (or ever) was using RPC/HTTP (Outlook Anywhere) will have this property present. This flags value contains a number of documented (and conceivably undocumented) flags. They are:

” Connect to my Exchange mailbox by using HTTP.
Const ROHFLAGS_USE_ROH = &h1

” Connect by using SSL only.
Const ROHFLAGS_SSL_ONLY = &h2

” Mutually authenticate the session when connecting by using SSL.
Const ROHFLAGS_MUTUAL_AUTH = &h4

” On fast networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_FAST = &h8

” On slow networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_SLOW = &h20

You should note that the first flag (ROHFALGS_USE_ROH) indicates whether or not RPC over HTTP is enabled within this profile. The second flag (ROHFLAGS_SSL_ONLY) will always be set in the current implementation. If the ROHFLAGS_MUTUAL_AUTH flag is set, it means that both the client and the server will authenticate to each other.

The last two flags are the ones we are interested in… ROHFLAGS_HTTP_FIRST_ON_FAST and ROHFLAGS_HTTP_FIRST_ON_SLOW. They define whether HTTP is tried first, or TCP is tried first. If the boxes are checked, the flags are set. Conversely, if the flags are set, the boxes will be checked. Therefore, our goal is to make sure those flags are set.

Especially if you are using Basic authentication, having these flags set can result in faster Outlook startup time.

By default, Autodiscover in Exchange 2007 and above will set the ROHFLAGS_HTTP_FIRST_ON_SLOW flag, but not set the ROHFLAGS_HTTP_FIRST_ON_FAST flag. There is no way to change this behavior.

The following script is much longer than an equivalent script in PowerShell would be, and it’s longer than it has to be in VBScript, but I wanted to make it easy to understand and provide resources for other scripters wanting to make profile modifications.

Obligatory disclaimers:

Modifying your registry can break your computer.

Modifying MAPI properties by modifying the registry may not be supported, I really don’t know. It works for me and my clients, but I can’t guarantee that it will for you.

On to the script:

''
'' httpFirstAlways.vbs
''
''
'' Michael B. Smith
'' michael@TheEssentialExchange.com
'' July, 2009
''
'' No warranties express or implied are available.
'' Use at your own risk - but it works for me!
''
'' This routine, for the current user, scans through all Exchange profiles. If
'' the RPC over HTTP (Outlook Anywhere) flags value is present, this means that
'' RPC over HTTP is configured for the profile. If the flag is present, this
'' routine will force "On fast networks, connect using HTTP first" and "On slow
'' networks, connect using HTTP first" to be checked.
''
Const ProfilePath = "Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles"
Const ExchDetails = "13dbb0c8aa05101a9bb000aa002fc45a" ' Exchange Server Details MAPI service

Const PT_LONG     = "0003" ' hex MAPI type for a 4-byte binary value
COnst PT_UNICODE  = "001f" ' hex MAPI type for a Unicode string

' Used to configure cached mode, but not used in this script

Dim PR_CONFIG_FLAGS          ' configuration flags for the Exchange Server Details MAPI service

PR_CONFIG_FLAGS          = PT_LONG    & "6601" ' PROP_TAG (PT_LONG, 0x6601)

'' MAPI properties for RPC over HTTP
'' From KB 898835 - MAPI properties for RPC over HTTP settings
'' http://support.microsoft.com/kb/898835

Dim PR_ROH_FLAGS             ' RPC over HTTP flags
Dim PR_ROH_PROXY_SERVER      ' RPC over HTTP proxy server
Dim PR_ROH_PRINCIPAL_NAME    ' Primary name on the SSL certificate 
Dim PR_ROH_PROXY_AUTH_SCHEME ' Basic or NTLM

PR_ROH_FLAGS             = PT_LONG    & "6623" ' PROP_TAG (PT_LONG,    0x6623)
PR_ROH_PROXY_SERVER      = PT_UNICODE & "6622" ' PROP_TAG (PT_UNICODE, 0x6622)
PR_ROH_PRINCIPAL_NAME    = PT_UNICODE & "6625" ' PROP_TAG (PT_UNICODE, 0x6625)
PR_ROH_PROXY_AUTH_SCHEME = PT_LONG    & "6627" ' PROP_TAG (PT_LONG,    0x6627)

'' Flags that are used in PR_ROH_FLAGS
'' Connect to my Exchange mailbox by using HTTP.
Const ROHFLAGS_USE_ROH            = &h1
'' Connect by using SSL only.
Const ROHFLAGS_SSL_ONLY           = &h2
'' Mutually authenticate the session when connecting by using SSL.
Const ROHFLAGS_MUTUAL_AUTH        = &h4
'' On fast networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_FAST = &h8
'' On slow networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_SLOW = &h20

'' Values that are used in PR_ROH_PROXY_AUTH_SCHEME
'' Basic authentication
Const ROHAUTH_BASIC               = &h1
'' NTLM authentication
Const ROHAUTH_NTLM                = &h2

Const HKEY_CURRENT_USER = &h80000001

Set objReg = GetObject ("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")

objReg.EnumKey HKEY_CURRENT_USER, ProfilePath, arrSubKeys

For Each objSubKey in arrSubKeys

	strMAPIpropKey   = ProfilePath  & "\" & _
                           objSubKey    & "\" & _
                           ExchDetails

        strMAPIpropValue = PR_ROH_FLAGS

	e "Checking profile named " & objSubKey

	On Error Resume Next
	Err.Clear

	objReg.GetBinaryValue HKEY_CURRENT_USER, strMAPIpropKey, strMAPIpropValue, arrBinary

	On Error Goto 0

	If IsNull (arrBinary) Or Err Then
		e "...property value not found"
	Else
		'e "Values (" & UBound (arrBinary) & ")"
		'For i = 0 To UBound (arrBinary)
		'	e i & " " & arrBinary(i) & " " & Hex(arrBinary (i))
		'Next

		val = arrBinary (0)
		If (val and ROHFLAGS_HTTP_FIRST_ON_FAST) = ROHFLAGS_HTTP_FIRST_ON_FAST Then
			e "...On fast networks, connect using HTTP first --- IS SET"
		End If

		If (val and ROHFLAGS_HTTP_FIRST_ON_SLOW) = ROHFLAGS_HTTP_FIRST_ON_SLOW Then
			e "...On slow networks, connect using HTTP first --- IS SET"
		End If

		bitVal = ROHFLAGS_HTTP_FIRST_ON_SLOW Or ROHFLAGS_HTTP_FIRST_ON_FAST

		If (val and bitVal) = bitVal Then
			e "...Desired options are already set"
		Else
			e "...Updating"
			arrBinary(0) = arrBinary(0) Or bitVal
			result = objReg.SetBinaryValue (HKEY_CURRENT_USER, strMAPIpropKey, strMAPIpropValue, arrBinary)
			If result <> 0 Then
				e "...Could not update HTTP network value (" & result & ") !!!"
			Else
				e "...update complete"
			End If
		End If

	End If
Next

e "Done"

Set objReg = Nothing

Sub e(str)
	WScript.Echo str
End Sub

Until next time…

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

P.S. Thanks to Diane Poremsky and John Fullbright for assistance in creating this article.


Follow me on twitter: @EssentialExch

MAPI in the registry (Or, setting the “use HTTP first” boxes via VBScript)

First off, I didn’t use PowerShell because I use the script in this article as a login script – and while I’m sure that the Windows Scripting Host is installed everywhere, I can’t be sure that PowerShell is installed everywhere. It will be a couple of years before I can do that.

Beginning with Exchange 2003 and Outlook 2003 (when running on Windows 2003), you could use RPC/HTTP (the feature was renamed to Outlook Anywhere with Exchange 2007). RPC/HTTP provides the capability of encapsulating the native protocol that Outlook uses to communicate with Exchange Server with a secure HTTP tunnel. Long story short, it’s a way of safely getting around firewalls.

The name of the protocol that Outlook prefers to use with Exchange Server is called MAPI – Message Applications Programming Interface. Let’s suffice it to say that MAPI is very powerful, very extensible, allows you to do darn near anything with a message or with Exchange that you might ever want to, and as is typical with all that power – MAPI is complicated to use. And you won’t be using MAPI in a script!

I can’t give MAPI justice in a blog post. But let’s talk about a few basic MAPI concepts.

Providers – a MAPI provider is some type of a server process (note: this does NOT mean that it has to run on a server, just that it provides services to clients). A few sample MAPI providers include the Address Book provider, the Spooler (which queues and sends email to Exchange), and the Profile provider (which is responsible for managing Mail profiles on a per-user basis on a client workstation).

Profile – a MAPI profile defines everything that a messaging client (such as Outlook) may need to know about communicating with an email server. A single profile may contain information about all of the various providers available to the client, both locally and on remote servers.

Property – a MAPI property is no different than any other type of property. It contains a value that is relevant to a specific provider and that value is contained within a specific profile. In general, a MAPI property is referred to via something called a PROP_TAG (a property tag) which is a 32 bit binary value. The first 16 bits represent the type of the property (such as integer or string, etc.) plus a special flag bit or two. The second 16 bits of a property is the specific property identifier.

The profile provider, which can be accessed via raw MAPI (of course), uses the system registry to store information about MAPI properties, profiles, providers, and other MAPI objects. Officially, these items are opaque. However, they have been the same since Windows 95, so it’s unlikely that they will be changing any time soon.

In the registry, each MAPI service associated with a particular profile has a GUID, which is a string. The particular GUID is assigned to the MAPI service, and is consistent across all MAPI installations. Each MAPI service has a series of property values assigned to it, and those property tags are represented by an 8-character value which is the hexidecimal representation of the binary property tag.

Today, we are particular interested in a single MAPI service – the Exchange Server Details MAPI service. This service is responsible for the connectivity to an Exchange server via the MAPI protocol. It has the following GUID:

Const ExchDetails = “13dbb0c8aa05101a9bb000aa002fc45a

EVERY MAPI profile which connects to an Exchange server will have that MAPI service defined and its property values populated. The particular MAPI property we are interested in is named PR_ROH_FLAGS. It looks like this:

Const PT_LONG = “0003” ‘ hex MAPI type for a 4-byte binary value

PR_ROH_FLAGS = PT_LONG & “6623” ‘ PROP_TAG (PT_LONG, 0x6623)

therefore the resultant value for PR_ROH_FLAGS is 00036623. Any MAPI profile which currently (or ever) was using RPC/HTTP (Outlook Anywhere) will have this property present. This flags value contains a number of documented (and conceivably undocumented) flags. They are:

” Connect to my Exchange mailbox by using HTTP.
Const ROHFLAGS_USE_ROH = &h1

” Connect by using SSL only.
Const ROHFLAGS_SSL_ONLY = &h2

” Mutually authenticate the session when connecting by using SSL.
Const ROHFLAGS_MUTUAL_AUTH = &h4

” On fast networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_FAST = &h8

” On slow networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_SLOW = &h20

You should note that the first flag (ROHFALGS_USE_ROH) indicates whether or not RPC over HTTP is enabled within this profile. The second flag (ROHFLAGS_SSL_ONLY) will always be set in the current implementation. If the ROHFLAGS_MUTUAL_AUTH flag is set, it means that both the client and the server will authenticate to each other.

The last two flags are the ones we are interested in… ROHFLAGS_HTTP_FIRST_ON_FAST and ROHFLAGS_HTTP_FIRST_ON_SLOW. They define whether HTTP is tried first, or TCP is tried first. If the boxes are checked, the flags are set. Conversely, if the flags are set, the boxes will be checked. Therefore, our goal is to make sure those flags are set.

Especially if you are using Basic authentication, having these flags set can result in faster Outlook startup time.

By default, Autodiscover in Exchange 2007 and above will set the ROHFLAGS_HTTP_FIRST_ON_SLOW flag, but not set the ROHFLAGS_HTTP_FIRST_ON_FAST flag. There is no way to change this behavior.

The following script is much longer than an equivalent script in PowerShell would be, and it’s longer than it has to be in VBScript, but I wanted to make it easy to understand and provide resources for other scripters wanting to make profile modifications.

Obligatory disclaimers:

Modifying your registry can break your computer.

Modifying MAPI properties by modifying the registry may not be supported, I really don’t know. It works for me and my clients, but I can’t guarantee that it will for you.

On to the script:

''
'' httpFirstAlways.vbs
''
''
'' Michael B. Smith
'' michael@TheEssentialExchange.com
'' July, 2009
''
'' No warranties express or implied are available.
'' Use at your own risk - but it works for me!
''
'' This routine, for the current user, scans through all Exchange profiles. If
'' the RPC over HTTP (Outlook Anywhere) flags value is present, this means that
'' RPC over HTTP is configured for the profile. If the flag is present, this
'' routine will force "On fast networks, connect using HTTP first" and "On slow
'' networks, connect using HTTP first" to be checked.
''
Const ProfilePath = "Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles"
Const ExchDetails = "13dbb0c8aa05101a9bb000aa002fc45a" ' Exchange Server Details MAPI service

Const PT_LONG     = "0003" ' hex MAPI type for a 4-byte binary value
COnst PT_UNICODE  = "001f" ' hex MAPI type for a Unicode string

' Used to configure cached mode, but not used in this script

Dim PR_CONFIG_FLAGS          ' configuration flags for the Exchange Server Details MAPI service

PR_CONFIG_FLAGS          = PT_LONG    & "6601" ' PROP_TAG (PT_LONG, 0x6601)

'' MAPI properties for RPC over HTTP
'' From KB 898835 - MAPI properties for RPC over HTTP settings
'' http://support.microsoft.com/kb/898835

Dim PR_ROH_FLAGS             ' RPC over HTTP flags
Dim PR_ROH_PROXY_SERVER      ' RPC over HTTP proxy server
Dim PR_ROH_PRINCIPAL_NAME    ' Primary name on the SSL certificate 
Dim PR_ROH_PROXY_AUTH_SCHEME ' Basic or NTLM

PR_ROH_FLAGS             = PT_LONG    & "6623" ' PROP_TAG (PT_LONG,    0x6623)
PR_ROH_PROXY_SERVER      = PT_UNICODE & "6622" ' PROP_TAG (PT_UNICODE, 0x6622)
PR_ROH_PRINCIPAL_NAME    = PT_UNICODE & "6625" ' PROP_TAG (PT_UNICODE, 0x6625)
PR_ROH_PROXY_AUTH_SCHEME = PT_LONG    & "6627" ' PROP_TAG (PT_LONG,    0x6627)

'' Flags that are used in PR_ROH_FLAGS
'' Connect to my Exchange mailbox by using HTTP.
Const ROHFLAGS_USE_ROH            = &h1
'' Connect by using SSL only.
Const ROHFLAGS_SSL_ONLY           = &h2
'' Mutually authenticate the session when connecting by using SSL.
Const ROHFLAGS_MUTUAL_AUTH        = &h4
'' On fast networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_FAST = &h8
'' On slow networks, connect by using HTTP first. Then, connect by using TCP/IP.
Const ROHFLAGS_HTTP_FIRST_ON_SLOW = &h20

'' Values that are used in PR_ROH_PROXY_AUTH_SCHEME
'' Basic authentication
Const ROHAUTH_BASIC               = &h1
'' NTLM authentication
Const ROHAUTH_NTLM                = &h2

Const HKEY_CURRENT_USER = &h80000001

Set objReg = GetObject ("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")

objReg.EnumKey HKEY_CURRENT_USER, ProfilePath, arrSubKeys

For Each objSubKey in arrSubKeys

	strMAPIpropKey   = ProfilePath  & "\" & _
                           objSubKey    & "\" & _
                           ExchDetails

        strMAPIpropValue = PR_ROH_FLAGS

	e "Checking profile named " & objSubKey

	On Error Resume Next
	Err.Clear

	objReg.GetBinaryValue HKEY_CURRENT_USER, strMAPIpropKey, strMAPIpropValue, arrBinary

	On Error Goto 0

	If IsNull (arrBinary) Or Err Then
		e "...property value not found"
	Else
		'e "Values (" & UBound (arrBinary) & ")"
		'For i = 0 To UBound (arrBinary)
		'	e i & " " & arrBinary(i) & " " & Hex(arrBinary (i))
		'Next

		val = arrBinary (0)
		If (val and ROHFLAGS_HTTP_FIRST_ON_FAST) = ROHFLAGS_HTTP_FIRST_ON_FAST Then
			e "...On fast networks, connect using HTTP first --- IS SET"
		End If

		If (val and ROHFLAGS_HTTP_FIRST_ON_SLOW) = ROHFLAGS_HTTP_FIRST_ON_SLOW Then
			e "...On slow networks, connect using HTTP first --- IS SET"
		End If

		bitVal = ROHFLAGS_HTTP_FIRST_ON_SLOW Or ROHFLAGS_HTTP_FIRST_ON_FAST

		If (val and bitVal) = bitVal Then
			e "...Desired options are already set"
		Else
			e "...Updating"
			arrBinary(0) = arrBinary(0) Or bitVal
			result = objReg.SetBinaryValue (HKEY_CURRENT_USER, strMAPIpropKey, strMAPIpropValue, arrBinary)
			If result <> 0 Then
				e "...Could not update HTTP network value (" & result & ") !!!"
			Else
				e "...update complete"
			End If
		End If

	End If
Next

e "Done"

Set objReg = Nothing

Sub e(str)
	WScript.Echo str
End Sub

Until next time…

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

P.S. Thanks to Diane Poremsky and John Fullbright for assistance in creating this article.


Follow me on twitter: @EssentialExch

msExchTargetBridgeheadServersDN and msExchSourceBridgeheadServersDN Errors with Routing Group Connectors

I’m working with a client this week on migrating them from Exchange Server 2003 to Exchange Server 2007.

Their Exchange 2003 environment is a weird one – a A/A/A/P/P cluster. That’s right – Active/Active/Active/Passive/Passive. Whoever sold them that solution made some serious money! 🙂

I’m moving them to a more standard solution – dual front end servers (CAS + HT) and a CCR backend for the mailboxes. Obviously, we are going to have coexistence for some period of time – there are thousands of mailboxes involved.

One of the standard steps in a co-existence scenario is to create a inter-server-version (InterOp) routing group connector (RGC) which allows the Exchange 2003 environment to communicate (i.e., transfer email) with the Exchange 2007 environment.

Today I finished setting up the CAS+HT servers and the CCR servers and began to set up the InterOp RGC. I used the same syntax I’ve used with dozens of other migrations:

New-RoutingGroupConnector -name Interop-RGC -SourceTransportServers server1, server2, server3 -TargetTransportServers ExchCas1, ExchCas2 -BiDirectional $true -PublicFolderReferralsEnabled $true

And… crap. It bombs.

WTF?!?!?

New-RoutingGroupConnector : Active Directory operation failed on dc3.example.com. This error is not retriable. Additional information: The name reference is invalid.
This may be caused by replication latency between Active Directory domain controllers.
Active directory response: 000020B5: AtrErr: DSID-03152392, #1:
0: 000020B5: DSID-03152392, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 315c30e2 (msExchTargetBridgeheadServersDN)
At line:1 char:26
+ new-routinggroupconnector <<<< -domaincontroller dc3.example.com -identity "Exchange Administrative Group (FYDIBOHF23SPDLT)\Exchange Routing Group (DWBGZMFD01QNBJR)\InterOp-RGC" -sourcetransportservers server1, server2, server3 -targettransportservers ExchCas1, ExchCas2 -BiDirectional $true -PublicFolderReferralsEnabled $true

Well, it has nothing whatsoever to do with “replication latency between Active Directory domain controllers”. I spent far too long heading down that path before I started googling around to see what else might be on the “Interwebs” about this issue.

It turns out that Exchange 2003 assigns indexes to each SMTP Virtual Server on a computer. The New-RoutingGroupConnector command will only connect to SMTP Virtual Servers that have a index value of 1. Is this a bug? Absolutely. Has it been reported? Absolutely. Has it been fixed? Absolutely not. Note that the same error can occur with the attribute msExchangeSourceBridgeheadServersDN – depends on how you construct the cmdlet.

As of the Exchange 2010 beta, this issue still appears.

In a A/A/A/P/P cluster, the indexes for the SMTP Virtual Servers go 1, 2, 3. This means that you can only specify a single SMTP Virtual Server in the New-RoutingGroupConnector cmdlet, and you can only specify the SMTP Virtual Server for the Exchange Server that has the index of 1. This ALSO means that, for the InterOp-RGC to work, that the Exchange 2003 server with the index of 1 must be the routing group master!

Finding those things out cost me several hours of my life. I hope this blog post helps you avoid the same timesink I had!

Until next time…

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


Follow me on twitter: @EssentialExch

Removing Old Emailaddresses/ProxyAddresses in Exchange 2007

I have a client who has multiple business units. Each business unit is placed into its own Organizational Unit (OU) in Active Directory and the business unit has its own unique Email Address Policy (EAP) that (using a RecipientFilter) assigns specific email addresses to all users in that OU.

The client has changed the name of one of their business units, and as of today, the old email addresses were to “go away”.

There is no in-built functionality for automatically removing email addresses. You can modify the EAP to specify a new address template as the default, but even when you remove the old address template, that email address is not removed from the user object (Exchange has worked this way since at least Exchange 2000, Exchange 5.5 used a somewhat different model).

What do you do? Well, you use PowerShell of course! This “one-liner” does the job:

get-mailbox -organizationalunit ad.example.com/BusinessUnitOU/users |% { $a = $_.emailaddresses; $b = $_.emailaddresses; foreach($e in $a) { if ($e.tostring() -match "old-email-domain") { $b -= $e; } } ; $_ | set-mailbox -emailaddresses $b }

You can enter this as a single line (and I did). However, the command is easier to understand when you separate it out, as shown below:

get-mailbox -organizationalunit ad.example.com/BusinessUnitOU/users |% 
    { 
        $a = $_.emailaddresses; 
        $b = $_.emailaddresses; 
        foreach ($e in $a) 
        { 
            if ($e.tostring() -match "old-email-domain") 
            { 
                $b -= $e; 
            } 
         }

         $_ | set-mailbox -emailaddresses $b 
    }


The Get-Mailbox command specifies the unique organizational unit that we are looking at. In this case, Get-Mailbox will return a mailbox object for all mailboxes that exist within the ad.example.com/BusinessUnitOU/users organizational unit. Note that I have not used the distinguishedName format for the organizational unit, but instead the X.500 format. Either is acceptable. However, the X.500 format only requires quoting when you have spaces in your organizational unit names. The distinguishedName format must always be quoted or escaped due to its use of commas (and how PowerShell interprets commas in open text strings).

The “|%” syntax indicates that the output of the Get-Mailbox command is to be evaluated, for each object returned, based on the following script block. In this case, the script block begins on the next line with “{” in column 4 and terminates 12 lines later with a “}” in column 4.

First, we make two copies of the email address collection for the mailbox that is returned. Note that setting “$b = $a” would have a different behavior! Instead of creating a copy of the collection, it would create a reference – that is, $b would point to the same collection as $a, and the following loop would not work properly, as described in the next paragraph.

Next, we evaluate each email address contained within the email address collection ($a). If a particular email address matches the old email address domain, we remove that particular email address from the second collection ($b). If we removed it from the original collection ($a), then the foreach loop would fail. Not a good thing to have happen – which is why we have two collections!

After examining all of the email addresses in the collection, we pipe the mailbox object to Set-Mailbox using the new/adjusted collection. And we are done!

But you don’t need to understand the script in order to use the script. To use the script, replace the -organizationalunit parameter with the one you are wanting to change (or completely remove the parameter if you want to affect all mailboxes). Next, replace old-email-domain with the domain you are want to remove. That’s all it takes.

Until next time…

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

[Edit March 29, 2012]

Mike Crowley, another Exchange MVP, makes the following comment that I thought I would share:

Michael, this was very helpful, thanks again! I
needed to do this for one of my customers, however the address that needed to
‘go away’ was a parent domain of one that needed to stay (e.g. ‘domain.com’
needed to go away, but ‘subdomain.domain.com’ needed to stay). I modified
your script and ran the below:

get-mailbox -organizationalunit
ad.domain.com/BusinessUnitOU/users | % { $a = $_.emailaddresses; $b =
$_.emailaddresses; foreach($e in $a) { if (($e.tostring() -match
“domain.com”) -and ($e.tostring() -notmatch
“subdomain.domain.com”)) { $b -= $e; } } ; $_ | set-mailbox
-emailaddresses $b }

-Mike


Follow me on twitter: @EssentialExch

Removing Old Emailaddresses/ProxyAddresses in Exchange 2007

I have a client who has multiple business units. Each business unit is placed into its own Organizational Unit (OU) in Active Directory and the business unit has its own unique Email Address Policy (EAP) that (using a RecipientFilter) assigns specific email addresses to all users in that OU.

The client has changed the name of one of their business units, and as of today, the old email addresses were to “go away”.

There is no in-built functionality for automatically removing email addresses. You can modify the EAP to specify a new address template as the default, but even when you remove the old address template, that email address is not removed from the user object (Exchange has worked this way since at least Exchange 2000, Exchange 5.5 used a somewhat different model).

What do you do? Well, you use PowerShell of course! This “one-liner” does the job:

get-mailbox -organizationalunit ad.example.com/BusinessUnitOU/users |% { $a = $_.emailaddresses; $b = $_.emailaddresses; foreach($e in $a) { if ($e.tostring() -match "old-email-domain") { $b -= $e; } } ; $_ | set-mailbox -emailaddresses $b }

You can enter this as a single line (and I did). However, the command is easier to understand when you separate it out, as shown below:

get-mailbox -organizationalunit ad.example.com/BusinessUnitOU/users |% 
    { 
        $a = $_.emailaddresses; 
        $b = $_.emailaddresses; 
        foreach ($e in $a) 
        { 
            if ($e.tostring() -match "old-email-domain") 
            { 
                $b -= $e; 
            } 
         }

         $_ | set-mailbox -emailaddresses $b 
    }


The Get-Mailbox command specifies the unique organizational unit that we are looking at. In this case, Get-Mailbox will return a mailbox object for all mailboxes that exist within the ad.example.com/BusinessUnitOU/users organizational unit. Note that I have not used the distinguishedName format for the organizational unit, but instead the X.500 format. Either is acceptable. However, the X.500 format only requires quoting when you have spaces in your organizational unit names. The distinguishedName format must always be quoted or escaped due to its use of commas (and how PowerShell interprets commas in open text strings).

The “|%” syntax indicates that the output of the Get-Mailbox command is to be evaluated, for each object returned, based on the following script block. In this case, the script block begins on the next line with “{” in column 4 and terminates 12 lines later with a “}” in column 4.

First, we make two copies of the email address collection for the mailbox that is returned. Note that setting “$b = $a” would have a different behavior! Instead of creating a copy of the collection, it would create a reference – that is, $b would point to the same collection as $a, and the following loop would not work properly, as described in the next paragraph.

Next, we evaluate each email address contained within the email address collection ($a). If a particular email address matches the old email address domain, we remove that particular email address from the second collection ($b). If we removed it from the original collection ($a), then the foreach loop would fail. Not a good thing to have happen – which is why we have two collections!

After examining all of the email addresses in the collection, we pipe the mailbox object to Set-Mailbox using the new/adjusted collection. And we are done!

But you don’t need to understand the script in order to use the script. To use the script, replace the -organizationalunit parameter with the one you are wanting to change (or completely remove the parameter if you want to affect all mailboxes). Next, replace old-email-domain with the domain you are want to remove. That’s all it takes.

Until next time…

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

[Edit March 29, 2012]

Mike Crowley, another Exchange MVP, makes the following comment that I thought I would share:

Michael, this was very helpful, thanks again! I
needed to do this for one of my customers, however the address that needed to
‘go away’ was a parent domain of one that needed to stay (e.g. ‘domain.com’
needed to go away, but ‘subdomain.domain.com’ needed to stay). I modified
your script and ran the below:

get-mailbox -organizationalunit
ad.domain.com/BusinessUnitOU/users | % { $a = $_.emailaddresses; $b =
$_.emailaddresses; foreach($e in $a) { if (($e.tostring() -match
“domain.com”) -and ($e.tostring() -notmatch
“subdomain.domain.com”)) { $b -= $e; } } ; $_ | set-mailbox
-emailaddresses $b }

-Mike


Follow me on twitter: @EssentialExch

Getting the Contents of an Active Directory Integrated DNS Zone

Microsoft has long offered (where “long” means “since Windows Server 2003”) the “dnscmd.exe” program to control the actions of a DNS server. And, if you installed the adminpack (RSAT-Tools-DNS on Vista/Server 2008 and above) you could perform this control on a remote workstation/server as well.

However, the output of dnscmd leaves….something…. to be desired, especially when you want to format and manipulate that output.

Using a prior blog post of mine (Hex and Decimal Output In PowerShell), I more-or-less reverse-engineered the format of how Active Directory Integrated zones (DNS domains) are stored in Active Directory. For all of my personal testing, the program below was successful at displaying the output and decoding it properly. However, that doesn’t mean I decode everything!!! Just those things I could test.

A key desire of mine was to be able to get the contents of an ADI zone into a CSV (comma-separated-value) format; so of course that option is present. I also wanted proper timeout values for aging/scavenging, so of course that happens by default (which dnscmd can’t do at all).

If the script below doesn’t work for you, I’d be interested in hearing about it, and why it doesn’t work. Of course, I make no promises, but I’ll probably fix those issues. 🙂

Without further ado…

##
## dns-dump.ps1
##
## Michael B. Smith
## michael at smithcons dot com
## http://TheEssentialExchange.com/blogs/michael
## May/June, 2009
##
## Use as you wish, no warranties expressed, implied or explicit.
## Works for me, but it may not for you.
## If you use this, I would appreciate an attribution.
##

Param(
	[string]$zone,
	[string]$dc,
	[switch]$csv,
	[switch]$help
)

function dumpByteArray([System.Byte[]]$array, [int]$width = 9)
{
	## this is only used if we run into a record format
	## we don't understand.

	$hex = ""
	$chr = ""
	$int = ""

	$i = $array.Count
	"Array contains {0} elements" -f $i
	$index = 0
	$count = 0
	while ($i-- -gt 0)
	{
		$val = $array[$index++]

		$hex += ("{0} " -f $val.ToString("x2"))

		if ([char]::IsLetterOrDigit($val) -or 
		    [char]::IsPunctuation($val)   -or 
		   ([char]$val -eq " "))
		{
			$chr += [char]$val
		}
		else
		{
			$chr += "."
		}

		$int += "{0,4:N0}" -f $val

		$count++
		if ($count -ge $width)
		{
			"$hex $chr $int"
			$hex = ""
			$chr = ""
			$int = ""
			$count = 0
		}		
	}

	if ($count -gt 0)
	{
		if ($count -lt $width)
		{
			$hex += (" " * (3 * ($width - $count)))
			$chr += (" " * (1 * ($width - $count)))
			$int += (" " * (4 * ($width - $count)))
		}

		"$hex $chr $int"
	}
}

function dword([System.Byte[]]$arr, [int]$startIndex)
{
	## convert four consecutive bytes in $arr into a
	## 32-bit integer value... if I had bit-manipulation
	## primitives in PowerShell, I'd use them instead
	## of the multiply operator.

	$res = $arr[$startIndex]
	$res = ($res * 256) + $arr[$startIndex + 1]
	$res = ($res * 256) + $arr[$startIndex + 2]
	$res = ($res * 256) + $arr[$startIndex + 3]

	return $res
}

function analyzeArray([System.Byte[]]$arr, [System.Object]$var)
{
	$nameArray = $var.distinguishedname.ToString().Split(",")
	$name = $nameArray[0].SubString(3)

	## AGE is stored backwards. The most-significant-byte comes
	## last, instead of first, unlike all the other 32-bit values.
	$age = $arr[23]
	$age = ($age * 256) + $arr[22]
	$age = ($age * 256) + $arr[21]
	$age = ($age * 256) + $arr[20]
	if ($age -ne 0)
	{
		## hours since January 1, 1601 (start of Windows epoch)
		## there is a long-and-dreary way to do this manually,
		## but get-date makes it trivial to do the conversion.
		$timestamp = (get-date -year 1601 -month 1 -day 1 -hour 0 -minute 0 -second 0).AddHours($age)
	}

	$ttl = dword $arr 12

	if ($arr[0] -eq 4 -and $arr[1] -eq 0)
	{
		# "A" record
		$ip = "{0}.{1}.{2}.{3}" -f $arr[24], $arr[25], $arr[26], $arr[27]

		if ($csv)
		{
			$formatstring = "{0},{1},{2},{3},{4}"
		}
		else
		{
			$formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}"
		}

		if ($age -eq 0)
		{
			$formatstring -f $name, "[static]", $ttl, "A", $ip
		}
		else
		{
			$formatstring -f $name, ("[" + $timestamp.ToString() + "]"), $ttl, "A", $ip
		}
	}
	elseif ((($arr[0] -eq 80) -or ($arr[0] -eq 82)) -and $arr[1] -eq 0)
	{
		# "SOA" record
		# "Start-Of-Authority"

		$nslen = $arr[44]
		$segments = $arr[45]
		$index = 46
		$nsname = ""
		while ($segments-- -gt 0)
		{
			$segmentlength = $arr[$index++]
			while ($segmentlength-- -gt 0)
			{
				$nsname += [char]$arr[$index++]
			}
			if ($segments -gt 0) { $nsname += "." }
		}
		$priserver = $nsname
		# "Primary server: $nsname"

		$index += 1
		$nslen = $arr[$index++]
		$segments = $arr[$index++]

		$nsname = ""
		while ($segments-- -gt 0)
		{
			$segmentlength = $arr[$index++]
			while ($segmentlength-- -gt 0)
			{
				$nsname += [char]$arr[$index++]
			}
			if ($segments -gt 0) { $nsname += "." }
		}
		# "Responsible party: $nsname"
		$resparty = $nsname

		#"TTL: $ttl"
		# "Age: $age"

####		$unk1 = dword $arr 16
####		"Unknown1: $unk1"

		$serial = dword $arr 24
		# "Serial: $serial"

		$refresh = dword $arr 28
		# "Refresh: $refresh"

		$retry = dword $arr 32
		# "Retry: $retry"

		$expires = dword $arr 36
		# "Expires: $expires"

		$minttl = dword $arr 40
		# "Minimum TTL: $minttl"

		if ($age -eq 0)
		{
			$agestr = "[static]"
		}
		else
		{
			$agestr = "[" + $timestamp.ToString() + "]"
		}

		if ($csv)
		{
			$formatstring = "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}"

			$formatstring -f $name, $agestr, $ttl, `
				"SOA", $priserver, $resparty, `
				$serial, $refresh, $retry, `
				$expires, $minttl
		}
		else
		{
			$formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}"

			$formatstring -f $name, $agestr, $ttl, "SOA"
			(" " * 32) + "Primary server: $priserver"
			(" " * 32) + "Responsible party: $resparty"
			(" " * 32) + "Serial: $serial"
			(" " * 32) + "TTL: $ttl"
			(" " * 32) + "Refresh: $refresh"
			(" " * 32) + "Retry: $retry"
			(" " * 32) + "Expires: $expires"
			(" " * 32) + "Minimum TTL (default): $minttl"
		}

		#### dumpByteArray $arr
	}
	elseif ((($arr[0] -eq 32) -or ($arr[0] -eq 30)) -and $arr[1] -eq 0)
	{
		# "NS" record
		$nslen = $arr[24]
		$segments = $arr[25]
		$index = 26
		$nsname = ""
		while ($segments-- -gt 0)
		{
			$segmentlength = $arr[$index++]
			while ($segmentlength-- -gt 0)
			{
				$nsname += [char]$arr[$index++]
			}
			if ($segments -gt 0) { $nsname += "." }
		}

		if ($csv)
		{
			$formatstring = "{0},{1},{2},{3},{4}"
		}
		else
		{
			$formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}"
		}

		if ($age -eq 0)
		{
			$formatstring -f $name, "[static]", $ttl, "NS", $nsname
		}
		else
		{
			$formatstring -f $name, ("[" + $timestamp.ToString() + "]"), $ttl, "NS", $nsname
		}
	}
	elseif ((($arr[0] -eq 36) -or ($arr[0] -eq 38)) -and $arr[1] -eq 0)
	{
		# "SRV" record

		$port = $arr[28]
		$port = ($port * 256) + $arr[29]

		$weight = $arr[26]
		$weight = ($weight * 256) + $arr[27]

		$pri = $arr[24]
		$pri = ($pri * 256) + $arr[25]

		$nslen = $arr[30]
		$segments = $arr[31]
		$index = 32
		$nsname = ""
		while ($segments-- -gt 0)
		{
			$segmentlength = $arr[$index++]
			while ($segmentlength-- -gt 0)
			{
				$nsname += [char]$arr[$index++]
			}
			if ($segments -gt 0) { $nsname += "." }
		}

		if ($csv)
		{
			$formatstring = "{0},{1},{2},{3},{4},{5}"
		}
		else
		{
			$formatstring = "{0,-30}`t{1,-24}`t{2}`t{3} {4} {5}"
		}

		if ($age -eq 0)
		{
			$formatstring -f `
				$name, "[static]", `
				$ttl, "SRV", `
				("[" + $pri.ToString() + "][" + $weight.ToString() + "][" + $port.ToString() + "]"), `
				$nsname
		}
		else
		{
			$formatstring -f `
				$name, ("[" + $timestamp.ToString() + "]"), `
				$ttl, "SRV", `
				("[" + $pri.ToString() + "][" + $weight.ToString() + "][" + $port.ToString() + "]"), `
				$nsname
		}

	}
	else
	{
		$name
		$var.distinguishedname.ToString()
		dumpByteArray $arr
	}

}

function processAttribute([string]$attrName, [System.Object]$var)
{
	$array = $var.$attrName.Value
####	"{0} contains {1} rows of type {2} from {3}" -f $attrName, $array.Count, $array.GetType(), $var.distinguishedName.ToString()

	if ($array -is [System.Byte[]])
	{
####		dumpByteArray $array
####		" "
		analyzeArray $array $var
####		" "
	}
	else
	{
		for ($i = 0; $i -lt $array.Count; $i++)
		{
####			dumpByteArray $array[$i]
####			" "
			analyzeArray $array[$i] $var
####			" "
		}
	}
}

function usage
{
"
.\dns-dump -zone  [-dc ] [-csv] |
	   -help

dns-dump will dump, from Active Directory, a particular named zone. 
The zone named must be Active Directory integrated.

Zone contents can vary depending on domain controller (in regards
to replication and the serial number of the SOA record). By using
the -dc parameter, you can specify the desired DC to use. Otherwise,
dns-dump uses the default DC.

Usually, output is formatted for display on a workstation. If you
want CSV (comma-separated-value) output, specify the -csv parameter.
Use out-file in the pipeline to save the output to a file.

Finally, to produce this helpful output, you can specify the -help
parameter.

This command is basically equivalent to (but better than) the:

	dnscmd /zoneprint 
or
	dnscmd /enumrecords  '@'

commands.

Example 1:

	.\dns-dump -zone essential.local -dc win2008-dc-3

Example 2:

	.\dns-dump -help

Example 3:

	.\dns-dump -zone essential.local -csv |
            out-file essential.txt -encoding ascii

	Note: the '-encoding ascii' is important if you want to
	work with the file within the old cmd.exe shell. Otherwise,
	you can usually leave that off.
"
}

	##
	## Main
	##

	if ($help)
	{
		usage
		return
	}

	if ($args.Length -gt 0)
	{
		write-error "Invalid parameter specified"
		usage
		return
	}

	if (!$zone)
	{
		throw "must specify zone name"
		return
	}

	$root = [ADSI]"LDAP://RootDSE"
	$defaultNC = $root.defaultNamingContext

	$dn = "LDAP://"
	if ($dc) { $dn += $dc + "/" }
	$dn += "DC=" + $zone + ",CN=MicrosoftDNS,CN=System," + $defaultNC

	$obj = [ADSI]$dn
	if ($obj.name)
	{
		if ($csv)
		{
			"Name,Timestamp,TTL,RecordType,Param1,Param2"
		}

		#### dNSProperty has a different format than dNSRecord
		#### processAttribute "dNSProperty" $obj

		foreach ($record in $obj.psbase.Children)
		{
			####	if ($record.dNSProperty) { processAttribute "dNSProperty" $record }
			if ($record.dnsRecord)   { processAttribute "dNSRecord"   $record }
		}
	}
	else
	{
		write-error "Can't open $dn"
	}

	$obj = $null

Until next time…

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


Follow me on twitter: @EssentialExch

Named Properties, What Lies Ahead…

Microsoft is preparing to make a potentially breaking change that deals with how Exchange Server 2007 handles named properties. This change is scheduled to be a part of Service Pack 2 and of Exchange Server 2010 at RTM.

This change involves the promotion (i.e., the visibility to non-MAPI clients) of X-* headers in incoming e-mail messages. This can potentially affect the operation of e-mail clients that depend on POP-3 and IMAP (and even Outlook, if you are using rules that look at headers).

Unlike certain earlier versions of Exchange, Exchange 2007 and above do not have a STM file – this means that incoming Internet e-mail is ALWAYS translated to MAPI format and that all headers are not necessarily retained – especially if they are X-* headers. This can cause a fidelity issue (i.e., you can’t reproduce EXACTLY what you received). For probably 99.999% of customers – this isn’t an issue. At least, that’s the opinion.

So…. Microsoft is looking for input and they’ve asked for Exchange MVPs to help get the word out.

To express your opinion, see Named Properties, Round 2: What lies Ahead.

Until next time…

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


Follow me on twitter: @EssentialExch

Named Properties, What Lies Ahead…

Microsoft is preparing to make a potentially breaking change that deals with how Exchange Server 2007 handles named properties. This change is scheduled to be a part of Service Pack 2 and of Exchange Server 2010 at RTM.

This change involves the promotion (i.e., the visibility to non-MAPI clients) of X-* headers in incoming e-mail messages. This can potentially affect the operation of e-mail clients that depend on POP-3 and IMAP (and even Outlook, if you are using rules that look at headers).

Unlike certain earlier versions of Exchange, Exchange 2007 and above do not have a STM file – this means that incoming Internet e-mail is ALWAYS translated to MAPI format and that all headers are not necessarily retained – especially if they are X-* headers. This can cause a fidelity issue (i.e., you can’t reproduce EXACTLY what you received). For probably 99.999% of customers – this isn’t an issue. At least, that’s the opinion.

So…. Microsoft is looking for input and they’ve asked for Exchange MVPs to help get the word out.

To express your opinion, see Named Properties, Round 2: What lies Ahead.

Until next time…

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


Follow me on twitter: @EssentialExch