Get-FrameworkVersion 1.6

In early December of 2017 I posted Get-FrameworkVersion 1.5. The cmdlet’s job was to examine a particular computer for all versions of the Windows .NET Framework installed on that computer. It has the capability of scanning the local computer or a remote computer. Remote computers can be accessed using CIM (with or without DCOM) or Windows RPC (also known as the Remote Registry Service).

This new version adds checking and reporting for BlockNetFramework registry key values. Each version of the .NET Framework can be blocked by creating a registry key value named BlockNetFramework<version> and setting the value data to a non-zero number. For example, BlockNetFramework472 with a value data of DWORD 1 would suppress the installation of .NET Framework 4.7.2. See the image below:

regedit image

Get the script on the TechNet Gallery: Display a list of all .NET Framework Versions installed on a computer.


Follow me on twitter! @EssentialExch

Get-FrameworkVersion

Get-FrameworkVersion.ps1 displays a list of all .NET Framework versions installed on a computer. While other scripts perform similar functionality, those I found were not well-behaved when they found an unknown version. Thus, the genesis for this version. Operation is simple:

PS C:\scripts> .\Get-FrameworkVersion.ps1
v2.0.50727       2.0.50727.4927   SP2
v3.0             3.0.30729.4926   SP2
v3.5             3.5.30729.4926   SP1
v4
		Client           4.7.02556
		Full             4.7.02556
v4.0
		Client           4.0.0.0

Current .NET Framework version: 4.7.1 on Windows 10 (4.7.02556 = release 461308)
PS C:\scripts>

I have posted the script on the TechNet Gallery: Display a list of all .NET Framework Versions installed on a computer.

The script is small enough that you can easily use it inside an Invoke-Command to execute on another computer. I will add native remote functionality in the next version.


Follow me on twitter! @EssentialExch

Determining the Exchange Version – without using Get-ExchangeServer – Update 2013

This is an update of my post Determining the Exchange Version – without using Get-ExchangeServer, from April 25, 2012. Since then, Exchange 2013 has been released! I've had several requests from people who are not PowerShell scripters to update that function. So here it is. I have repeated the text from the prior below.

If you write lots of Exchange scripts (as I do), and have several different versions of Exchange on which you need to run those scripts (as I do); you soon see the need for being able to determine what version of Exchange a particular server is running – and you may need to do this outside of the Exchange Management Shell.

This may be necessary because of different behaviors that are required with the Exchange Management Shell depending on the version of Exchange. It also may be required because the version of Exchange pre-dates the Exchange Management Shell (i.e., Exchange 2003). As an additional rationale, your script may need to load the Exchange Management Shell and cannot do that properly without knowing the version of Exchange that is being targeted (the process differs between Exchange 2007 and Exchange 2010 and Exchange 2013).

Thus, I've written a couple of functions that I wrap in a script to give me that information. Get-ExchangeServer, the function presented in this post, returns a simple object containing the name of the server examined, plus the version of Exchange that is running on that server. That is, in C pseudo-syntax:

struct result {
string Name;
string Version;
}

If the result of the function is $null, then the version could not be determined and the server targeted either does not exist or is (very likely) not running Exchange. If the server does not exist (or the firewall on the server prevents remote management via WMI) then an error is displayed.

The version information about Exchange is stored in the registry of each Exchange server. The script below shows some techniques for accessing the registry to obtain string (reg_sz) and 32-bit short integer (reg_dword) values while using PowerShell. Note that PowerShell has multiple mechanisms for accessing this information, including the so-called Registry Provider. I personally find using the WMI functions to be a bit easier to handle.

You can include these functions directly into your PowerShell profile, or you can dot-source the function on an as-needed basis.

Without further ado, enjoy!

###
### Get-ExchangeVersion
###
### Return the version of the specified Exchange server
###
### 2013-04-11
###	Updated to support Exchange Server 2013 and E16
###

Set-StrictMode -Version 2.0

$HKCR = 2147483648
$HKCU = 2147483649
$HKLM = 2147483650

function RegRead
{
	Param(
		[string]$computer,
		[int64] $hive,
		[string]$keyName,
		[string]$valueName,
		[ref]   $value,
		[string]$type = 'reg_sz'
	)

	[string]$fn = "RegRead:" ## function name

	try {
		$wmi = [wmiclass]"\\$computer\root\default:StdRegProv"
		if( $wmi -eq $null )
		{
			return 1
		}
	}
	catch {
		$error[0]
		write-error "$fn Could not open WMI access to $computer"
		return 1
	}

	switch ( $type )
	{
		'reg_sz'
			{
				$r = $wmi.GetStringValue( $hive, $keyName, $valueName )
				$value.Value = $r.sValue
			}
		'reg_dword'
			{
				$r = $wmi.GetDWORDValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		default
			{
				write-error "$fn Unsupported type: $type"
			}
	}

	$wmi = $null

	return $r.ReturnValue
}

function Get-ExchangeVersion
{
	Param(
		[string]$computer = '.'
	)

	[string]$fn = "Get-ExchangeVersion:" ## function name

	if( $computer -eq '.' -or [String]::IsNullOrEmpty( $computer ) )
	{
		$computer = $env:ComputerName
	}

	## Exchange E16 (assumption!)
	## HKLM\Software\Microsoft\ExchangeServer\v16\Setup
	## MsiProductMajor (DWORD 16)

	## Exchange 2013
	## HKLM\Software\Microsoft\ExchangeServer\v15\Setup
	## MsiProductMajor (DWORD 15)

	## Exchange 2010
	## HKLM\Software\Microsoft\ExchangeServer\v14\Setup
	## MsiProductMajor (DWORD 14)

	## Exchange 2007
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## MsiProductMajor (DWORD 8)

	## Exchange 2003
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## Services Version (DWORD 65)

	$v = 0

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v16\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 16 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = 'E16'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v15\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 15 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2013'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v14\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 14 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2010'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 8 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2007'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'Services Version' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 65 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2003'
		return $obj
	}

	### almost certainly not an Exchange server

	return $null
}

Please follow me on Twitter, @essentialexch

 

Determining the Exchange Version – without using Get-ExchangeServer

If you write lots of Exchange scripts (as I do), and have several different versions of Exchange on which you need to run those scripts (as I do); you soon see the need for being able to determine what version of Exchange a particular server is running – and you may need to do this outside of the Exchange Management Shell.

This may be necessary because of different behaviors that are required with the Exchange Management Shell depending on the version of Exchange. It also may be required because the version of Exchange pre-dates the Exchange Management Shell (i.e., Exchange 2003). As an additional rationale, your script may need to load the Exchange Management Shell and cannot do that properly without knowing the version of Exchange that is being targeted (the process differs between Exchange 2007 and Exchange 2010).

Thus, I've written a couple of functions that I wrap in a script to give me that information. Get-ExchangeServer, the function, returns a simple object containing the name of the server examined, plus the version of Exchange that is running on that server. That is:

struct result {

string Name;

string Version;

}

If the result of the function is $null, then the version could not be determined and the server targeted either does not exist or is (very likely) not running Exchange. If the server does not exist (or the firewall on the server prevents remote management via WMI) then an error is displayed.

The version information about Exchange is stored in the registry of each Exchange server. The script below shows some techniques for accessing the registry to obtain string (reg_sz) and short integer (reg_dword) values while using PowerShell. Note that PowerShell has multiple mechanisms for accessing this information, including the so-called Registry Provider. I personally find using the WMI functions to be a bit easier to handle.

You can include these functions directly into your PowerShell profile, or you can dot-source the function on an as-needed basis.

Without further ado, enjoy!


###
### Get-ExchangeVersion
###
### Return the version of the specified Exchange server
###

Set-StrictMode -Version 2.0

$HKCR = 2147483648
$HKCU = 2147483649
$HKLM = 2147483650

function RegRead
{
	Param(
		[string]$computer,
		[int64] $hive,
		[string]$keyName,
		[string]$valueName,
		[ref]   $value,
		[string]$type = 'reg_sz'
	)

	try {
		$wmi = [wmiclass]"\\$computer\root\default:StdRegProv"
		if( $wmi -eq $null )
		{
			return 1
		}
	}
	catch {
		##$error[0]
		write-error "Could not open WMI access to $computer"
		return 1
	}

	switch ( $type )
	{
		'reg_sz'
			{
				$r = $wmi.GetStringValue( $hive, $keyName, $valueName )
				$value.Value = $r.sValue
			}
		'reg_dword'
			{
				$r = $wmi.GetDWORDValue( $hive, $keyName, $valueName )
				$value.Value = $r.uValue
			}
		default
			{
				write-error "Unsupported type: $type"
			}
	}

	$wmi = $null

	return $r.ReturnValue
}

function Get-ExchangeVersion
{
	Param(
		[string]$computer = '.'
	)

	if( $computer -eq '.' )
	{
		$computer = $env:ComputerName
	}

	## Exchange vNext (assumption!)
	## HKLM\Software\Microsoft\ExchangeServer\v15\Setup
	## MsiProductMajor (DWORD 15)

	## Exchange 2010
	## HKLM\Software\Microsoft\ExchangeServer\v14\Setup
	## MsiProductMajor (DWORD 14)

	## Exchange 2007
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## MsiProductMajor (DWORD 8)

	## Exchange 2003
	## HKLM\SOFTWARE\Microsoft\Exchange\Setup
	## Services Version (DWORD 65)

	$v = 0

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v15\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 15 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = 'vNext'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\ExchangeServer\v14\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 14 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2010'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'MsiProductMajor' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 8 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2007'
		return $obj
	}

	$i = RegRead $computer $HKLM 'Software\Microsoft\Exchange\Setup' 'Services Version' ( [ref] $v ) 'reg_dword'
	if( ( $i -eq 0 ) -and ( $v -eq 65 ) )
	{
		$obj = "" | Select Name, Version
		$obj.Name = $computer
		$obj.Version = '2003'
		return $obj
	}

	### almost certainly not an Exchange server

	return $null
}

 

Exchange 2003 FE Servers vs. Server 2008 Active Directory

I spent eight hours over the last two days figuring out an interesting – but weird – problem. Once I figured out the problem, correcting it was a simple matter.

I’ll just give you the problem description and then the solution. Hopefully it will save you more than a little time in the future.

I have a client (one of several in this particular situation) who has an Exchange 2003 Front-End server located in their DMZ. Yes, that’s right, their DMZ. In the “long-ago time”, this was considered to be a favored practice by Microsoft. That, of course, has changed over the years. Now, in 2011, we do not want any domain member servers located in the DMZ. Why is that? Because to install a domain member in a DMZ basically means that you have to make “swiss cheese” out of your firewall. And, effectively, if a DMZ server is compromised it means that your entire Active Directory can be compromised. The entire list of potentially affected ports is shown in KB 832017, and at the end of the day – it basically says that you have to open everything above port 1024 to make it all work.

With all that being said, if you are using Exchange 2003 with Server 2003 Active Directory (by which I mean an Active Directory domain controller hosted on a Windows Server 2003 server), you could get by with a much more limited set of ports (although it still isn’t small!):

DNS (TCP and UDP 53)
Kerberos (TCP and UDP 88)
RPC Endpoint Mapper (TCP 135)
NetBIOS Name Service (TCP 137)
NetBIOS Datagram Service (TCP 138)
NetBIOS Session Service (TCP 139)
LDAP (TCP and UDP 389)
Directory Services (TCP 445)
LDAP Global Catalog (TCP 3268)
Remote Desktop Protocol (TCP 3389)
Active Directory RPC End-Point (AD-RPC-EP)

You should notice that all of these have defined ports except for the last! By definition, the AD-RPC-EP is a random high port.

However, a somewhat surprising fact is that when you run Active Directory on Windows Server 2003 (in other words, you have a domain controller on Server 2003), the AD-RPC-EP is always either port 1025 or 1026!

Starting with Server 2008, this is no longer true. “Port randomization” ensures that ports are allocated at truly random locations.

So… I have a particular client who, as part of their Exchange 2003 to Exchange 2010 upgrade effort decided that they first wanted to upgrade their Active Directory. That was fine by me – there are no documented restrictions regarding the operating system level of domain controllers to be used by Exchange 2003.

The day we changed the last domain controller from Windows Server 2003 to Windows Server 2008 R2, the Exchange FE server stopped working. Oh, $%&#.

Thankfully, the FE server was only being used for Outlook Web Access (OWA). SMTP was injected into the Exchange environment via a Barracuda cluster directly into the internal back-end server environment.

I’m rather hardheaded. So I wanted to figure this out. And, quite frankly, it took awhile. Eventually, it took an examination using portqry and rpcping (you can find out lots of information about these utilities by using Google or Bing and searching for “portqry site:support.microsoft.com” and “rpcping site:support.microsoft.com”). Comparing the results of those to the “access control lists” from the firewall showed me that the firewall always expected a port to be open at TCP 1025 and/or TCP 1026. Neither of these ports were open on Server 2008 R2!

I went back and investigate some other customers who were running Active Directory on Server 2003. On every single one of their servers, the process lsass.exe had either TCP 1025 or TCP 1026 (or both) open.

A google here, a bing there, and I was led to Active Directory Replication Over Firewalls and KB 224196: Restricting Active Directory replication traffic and client RPC traffic to a specific port.

These led me to understand that on Server 2008 and above AD-RPC-EP could happen at any random port – but that there is a way to specify the port that will be used. YAY!

For the following

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters
Registry value: TCP/IP Port
Value type: REG_DWORD
Value data: (available port)

specifying a “value data” of either 1025 or 1026 ensures that Server 2008 and Server 2008 R2 operate as did Server 2003. After setting this value, it does take a reboot of the affected DC/server for the value to take effect.

Once this was done, my client was happy again! I hope this helps you in your migrations…

Until next time…

As always, if there are items you would like me to talk about, please drop me a line and let me know!


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