As you are probably aware, Exchange 2010 service pack 1 was released last week, on the Internet (a so-called RTW [release to web] by Microsoft) at the Microsoft Exchange Team Blog. The release posting was The Future of Exchange Starts Here: Exchange Server 2010 SP 1 Is Now Available.

In my opinion, SP1 represents Exchange 2010 as it "should have been" at its original release. You can finally control RBAC (Role Based Access Control) in the GUI, Retention Policies now work properly and can be controlled in the GUI, common DAG options are now available in GUI, themes are available in OWA; plus a large number of bug fixes and other improved feature content. You can read the article about What's New in Exchange 2010 SP1.

However, along with this has come some controversy. You cannot install Exchange 2010 SP1 without first installing a handful of hotfixes. This is a hard-block in the Exchange setup program, which means that if the hotfixes are not detected, the setup program will abort. The list of hotfixes are available in the article Exchange 2010 SP1 Prerequisites and if you don't have them installed, the Exchange setup program also informs you of this and tells you where to get them.

Now, this is a first for Exchange setup. In the past, Exchange may have required a particular service pack and/or OS version, but not specific hotfixes. The issue with hotfixes are, as I see it, three:

[1] They must be individually requested,

[2] There is confusion about which specific hotfix to get (you will always need the x64 version, and if you are running on Windows Server 2008 SP2 then you get the Vista version; if you are running on Windows Server 2008 R2, then you get the Windows 7 version), and

[3] Hotfixes are not fully regression tested and should "only be installed on computers experiencing this particular problem".

It is this last one that makes people stop and say "whoa!" But it shouldn't.

Microsoft has required this configuration. Microsoft supports this configuration. Don't make it an issue, because it isn't.

Hotfixes are not fully regression tested because they are designed to fix a specific issue in a specific module of a specific piece of software. It does not make sense to go through a full service-pack set of testing for a single fix. Also, Microsoft has been running millions of mailboxes on this codebase (in their Live@Edu environment) for quite some time, as well as over half-million TAP customer mailboxes. These fixes have been tested in production environments and Microsoft does support them.

In my opinion, Microsoft should require these hotfixes. If Microsoft is aware of a particular problem and a fix is available for that problem - then I believe they should require that fix. I don't want an Exchange service pack released with known issues that are going to affect the end-user customer stability of the service pack.

There is some question about why these fixes aren't available on WSUS or Microsoft Update; and the answer is simple: these are not security related fixes, they are not Exchange fixes, and they will not affect every single Microsoft server installation. However, they are known to affect Exchange and thus need to be installed on Exchange servers.

My only complaint about the process is the naming of the hotfixes. Installing a "Windows 7" hotfix on a "Server 2008 R2" computer makes sense to me - but it doesn't to the average system administrator. The same is true for installing a "Vista" hotfix on a "Server 2008 Service Pack 2" computer.

So...feel safe. You have the warm-and-fuzzy you want. Microsoft fully supports the deployment of these hotfixes on an Exchange server.

Until next time...

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

Posted by michael | with no comments

A common request is to get a list of all distribution groups and the members contained in that distribution group. The question came up on a mailing list I frequent today, and my initial response was a PowerShell "one-liner" (actually five lines, but all a single PowerShell statement).

The one-liner worked, but it had a couple of limitations: it wasn't very pretty (that is, the output was formatted poorly) and if there were than one user set to manage the distribution group, that would be reportedly incorrectly. It was also slower than it had to be.

Note: a distribution group may also be a security group. From an Exchange Server perspective, the important thing is whether the group is mail-enabled or not.

So, I took that five-liner, cleaned it up, fixed the ManagedBy reporting bug, and sped it up (by using an embedded pipeline to report on the members contained in a distribution group). That turned it into a 50+ line script (which includes 10 lines of comments!). As I said on the mailing list - you can generate quick results with PowerShell. That's good enough for most admins. But if you want it pretty and "production quality" then it's going to take a little more time.

You can take this report and pipe it to out-string in order to save the output to a disk file. Then you can inspect the file later, email it, copy-n-paste it, whatever you want.

Here is the script:

##
## Report-DistributionGroupsAndMembers.ps1
## v1.1

##
## Michael B. Smith
## http://TheEssentialExchange.com
## August, 2010
##
## Requires Exchange Management Shell
## Should work with either PowerShell v1 or v2
## Tested on both Exchange 2007 and Exchange 2010

##

function formatManager($formatstring, $manager)
{
	$formatstring -f $manager.Name, ($manager.Parent.ToString() + "/" + $manager.RDN.ToString().SubString(3))
}

Get-DistributionGroup -ResultSize Unlimited |% {
	$group = $_
	"Group Name & Identity: {0}, {1}" -f $group.Name, $group.Identity
	$managedBy = $group.ManagedBy
	if( $managedBy -is [Microsoft.Exchange.Data.Directory.ADObjectId] )
	{
		formatManager "Group manager: {0}, {1}" $managedBy
	}
	elseif( $managedBy.Count -gt 1 )
	{
		[bool]$first = $true
		foreach( $manager in $managedBy ) 
		{
			if( $first ) 
			{
				formatManager "Group managers: {0}, {1}" $manager
				$first = $false
			}
			else
			{
				formatManager "                {0}, {1}" $manager
			}
		}
	}
	elseif( $managedBy.Count -gt 0 )
	{
		formatManager "Group manager: {0}, {1}" $managedBy[0]
	}

	"Members:"
	Get-DistributionGroupMember -Identity $group.Identity -ResultSize Unlimited |% {
		foreach( $member in $_ )
		{
			"`t$($member.Name)"
		}
	}
	"---"
}

Until next time...

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

--Edit on August 20, 2010
The original script would not display ManagedBy on Exchange 2007 (I'm running Exchange 2010). This is because ManagedBy in Exchange 2010 always returns an array-type object (it can contain multiple users). In Exchange 2007, the ManagedBy value is always a singleton. The required changes to the script are in red.

Michael B.

Posted by michael | with no comments

On a mailing list recently, SBS author and PowerShell MVP Charlie Russel posted how he used PowerShell to check whether a given PowerShell session was elevated. He also used that information to change the background color of the session (elevated shells are dangerous things!).

I took Charlie's code and expanded it a bit and "made it mine". I often need to know whether I'm running as an administrator, a server operator, and/or a backup operator. This is because I write lots of Exchange PowerShell scripts (which often require server operator or local administrator privileges) and backup PowerShell scripts (which require the user running the script to be a backup operator). The same technique Charlie used can also be used to determine those things. The key element here is the IsInRole() method of System.Security.Principal.WindowsPrincipal. For detailed information about that .Net class, google/bing for System.Security.Principal.WindowsPrincipal.

The IsInRole() method operates against a WindowsIdentity object. This is obtained from the current process.

The script is pretty self-explanatory. It is designed to be dot-sourced so the functions can be used within your current script. I've also included Charlie's functionality for changing the background of an elevated shell session.

Without further ado....

##
## IsProtectedRole.ps1
##
## Contains functions for identifying protected roles the current user has tokens for.
##
## Intended for dot-sourcing.
##
## based on code from Charlie Russell (www.scribes.com).
##

$identity  = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal( $identity )

##
## Starting with Vista/Server2008, if UAC is enabled, then a user who has either direct
## or indirect membership in the BuiltIn\Administrators group is assigned not one but
## TWO security tokens. One of those tokens has the administrator privilege, and one
## does not. In order for you to have administrator privilege in PowerShell, you must
## start the PowerShell session from: Angel another elevated shell (either PowerShell or
## cmd.exe), or Beer elevate the session when you start the shell (i.e., "Run As Administrator").
##

function IsAdministrator
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Administrator )
}

function IsUser
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::User )
}

function IsPowerUser
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::PowerUser )
}

function IsGuest
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Guest )
}

function IsAccountOperator
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::AccountOperator )
}

function IsSystemOperator
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::SystemOperator )
}

function IsPrintOperator
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::PrintOperator )
}

function IsBackupOperator
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::BackupOperator )
}

function IsReplicator
{
	$principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Replicator )
}

function MarkAdministratorShell
{
	If (IsAdministrator)
	{
		$script:effectivename = "Administrator"
		$host.UI.RawUI.Backgroundcolor = "DarkRed"
		$host.UI.RawUI.Foregroundcolor = "White"
	}
	else
	{
		$script:effectivename = $identity.name
		$host.UI.RawUI.Backgroundcolor = "White"
		$host.UI.RawUI.Foregroundcolor = "DarkBlue"
	}

	clear-host
}

# write-host 'Administrator' (IsAdministrator)
# write-host 'User' (IsUser)
# write-host 'PowerUser' (IsPowerUser)
# write-host 'Guest' (IsGuest)
# write-host 'AccountOperator' (IsAccountOperator)
# write-host 'SystemOperator' (IsSystemOperator)
# write-host 'PrintOperator' (IsPrintOperator)
# write-host 'BackupOperator' (IsBackupOperator)
# write-host 'Replicator' (IsReplicator)

Until next time...

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

Posted by michael | with no comments
Filed under: ,

I recently provided input to Marriette Knap, a Small Business Server MVP, in a blog posting of her's called How to install BlackBerry Enterprise Server Express on a SBS 2008.

I believe that this posting is the best resource for installing BES/X available right now; whether you are installing BES/X on an Exchange Server, on a Domain Controller/Group Catalog server, or on a standalone server.

If you are installing BES/X on a DC/GC or directly on an Exchange server, I highly recommend you follow the instructions contained in that blog post.

Until next time...

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

Posted by michael | with no comments
Filed under: , ,

Exchange has a very rich set of objects which are used and created by the various Exchange cmdlets. Unfortunately, these objects (excepting those used by Exchange Web Services) are poorly documented - and the documentation which is available is often incorrect or misleading.

I ran into that problem this past week.

To understand my particular issue, let's review a little history. Exchange 2000 and Exchange 2003 didn't have the concept of individual "accepted domains". Instead, you usually used Recipient Policies to identify the accepted domains. Recipient Policies were also used to create and manage Mailbox Manager policies.

In Exchange 2007, all of the capabilities of Recipient Policies were split into multiple features: Accepted Domains, E-mail Address Policies, and Managed Folder Mailbox policies. In Exchange 2010, Managed Folder Mailbox policies have been deprecated and replaced by Retention Policies.

However, some capabilities of these features are not available in the Exchange 2007/2010 GUI (but were in the Exchange 2000/2003 GUI). One of these hidden features is to have both enabled and disabled address templates in an E-Mail Address Policy. In the case of a disabled address template, a particular e-mail address domain can be manually assigned to a given Exchange object but that e-mail address domain will not be automatically assigned even if the object otherwise meets the requirements of the recipient filter assigned to the E-Mail Address Policy (such as a company name, a conditional attribute, group membership, etc.).

Whew.

So what happens when you need to disable an existing e-mail address template?

Well, you do it in PowerShell. (Long lead up to an obvious response, right?) And you do it with the obvious PowerShell cmdlet: Set-EmailAddressPolicy.

What would seem to make sense that the command would be something like:

Set-EmailAddressPolicy 'Default Policy' -Add -DisabledEmailAddressTemplates '@example.com'

...but that doesn't work. Aside from the fact that there is no Add or Remove parameter to Set-EmailAddressPolicy, you will eventually figure out that whatever value is provided to the EnabledEmailAddressTemplates and the DisabledEmailAddressTemplates parameters overwrite the values previously there. Well, ain't that a kick?

To change an address template collection, you must modify the existing collection and resubmit the modified value to Set-EmailAddressPolicy.

So what's the problem? The problem is the type of the required argument. The EnabledEmailAddressTemplates and DisabledEmailAddressTemplates parameters take a parameter of type Microsoft.Exchange.Data.ProxyAddressTemplateCollection. This is an enumerated collection (a fancy name for an array that you can use with a foreach(), or index it, or use the Item() method to access elements of the collection). Individual items of the collection are of type Microsoft.Exchange.Data.ProxyAddressTemplate or Microsoft.Exchange.Data.SmtpProxyAddressTemplate.

To add or remove items from the collection, you use the normal methods available to collections - that is, the Add() and Remove() methods. However, to use the Add() or Remove() methods, you need to have an object of type Microsoft.Exchange.Data.ProxyAddressTemplate or of type Microsoft.Exchange.Data.SmtpProxyAddressTemplate.

How the heck do you get one of those?

In general, with .Net objects, you create objects using a constructor. In PowerShell, that translates to the New-Object cmdlet. So my normal process is to begin by using Google/Bing to search for the object name. The first or second hit is usually the MSDN web page that describes the class. Then, I read the page, find the appropriate constructor and bam! I am done. This is the method I followed in an earlier article: More Multi-valued Parameters in PowerShell (SourceTransportServers in Set-SendConnector).

But it doesn't always work. I refer you back to the first paragraph. Many Exchange objects are poorly documented and the documentation that is present is often wrong. As an exercise, I invite you to Google/Bing the objects we are interested in today: Microsoft.Exchange.Data.ProxyAddressTemplate or Microsoft.Exchange.Data.SmtpProxyAddressTemplate. Read the MSDN web pages for the class, especially the page on constructors.

Once you've done that reading, you'll try this:

$template = New-Object Microsoft.Exchange.Data.ProxyAddressTemplate('smtp', '@example.com')

which will not work. Then you'll try:

$template = New-Object Microsoft.Exchange.Data.ProxyAddressTemplate('smtp:@example.com')

which also will not work. Then you'll try to get a raw object:

$template = New-Object Microsoft.Exchange.Data.ProxyAddressTemplate

which again will not work.

Then, if you are like me, you'll spend the next couple of hours banging your head against the wall getting nowhere. After sleeping on the problem, I did come up with a very unsatisfying work-around. Since, in this case, we are trying to take a "currently enabled" object and make it disabled, we should be able to use a reference to the existing object. That solution looks like the below:

	$template = '@example.com'

	$policy = Get-EmailAddressPolicy 'Default Policy'

	$EnabledAddresses  = $policy.EnabledEmailAddressTemplates
	$DisabledAddresses = $policy.DisabledEmailAddressTemplates

	$objTemplate = $null

	foreach ($addr in $EnabledAddresses)
	{
		if ( $addr.AddressTemplateString -ieq $template )
		{
			"found $template"
			$objTemplate = $addr
			break
		}
		"skipped " + $addr.AddressTemplateString
	}

	if ( $objTemplate )
	{
		$EnabledAddresses.Remove( $objTemplate )
		$DisabledAddresses.Add( $objTemplate )

		$policy |
			Set-EmailAddressPolicy -EnabledEmailAddressTemplates $EnabledAddresses `
					       -DisabledEmailAddressTemplates $DisabledAddresses
	}

That, however, is a kludgey hack. In the above solution, we are using a reference to an existing object to change the policies. That will not work in many use-cases; for example where you want to add a new address template, whether enabled or disabled. You will find that you can't modify the objects returned from Get-EmailAddressPolicy - they are marked read-only.

So, I asked some people who know a lot more about PowerShell than I. Shay Levy, a PowerShell MVP, pointed out that I was making it too hard and that this works:

$objTemplate = [Microsoft.Exchange.Data.ProxyAddressTemplate]'smtp:@example.com'

So, I slapped myself upside the head and said "Of course! ::Parse() is also a hidden constructor!" What that means, in English, is that the above statement is equivalent to:

$objTemplate = [Microsoft.Exchange.Data.ProxyAddressTemplate]::Parse( 'smtp:@example.com' )

and to:

$objTemplate = 'smtp:@example.com' -as [Microsoft.Exchange.Data.ProxyAddressTemplate]

PowerShell has a built-in capability, using a static method on an object, to take a string and attempt to coerce that string into the named object. In this case, a Microsoft.Exchange.Data.ProxyAddressTemplate. That static method must be named Parse() for PowerShell to automatically use the capability. You can see the static methods on this class using the below command:

[Microsoft.Exchange.Data.ProxyAddressTemplate] | Get-Member -static

Oisin Grehan, another PowerShell MVP, also pointed out that this is a perfect opportunity to use a filter. A filter is a PowerShell feature, somewhat uncommonly used, for processing objects in the pipeline. For example, to define a couple of useful filters:

	filter ConvertTo-ProxyAddressTemplate
	{
		$_ -as [Microsoft.Exchange.Data.ProxyAddressTemplate]
	}

	filter ConvertTo-SmtpProxyAddressTemplate
	{
		$_ -as [Microsoft.Exchange.Data.SmtpProxyAddressTemplate]
	}

These filters would take input from the pipeline and convert the pipeline input into the desired objects. For example:

	[PS] C:\S>'@example.com', '@sub1.example.com', '@sub2.example.com' | ConvertTo-SmtpProxyAddressTemplate

	AddressTemplateString      : @example.com
	ProxyAddressTemplateString : smtp:@example.com
	Prefix                     : SMTP
	IsPrimaryAddress           : False
	PrefixString               : smtp

	AddressTemplateString      : @sub1.example.com
	ProxyAddressTemplateString : smtp:@sub1.example.com
	Prefix                     : SMTP
	IsPrimaryAddress           : False
	PrefixString               : smtp

	AddressTemplateString      : @sub2.example.com
	ProxyAddressTemplateString : smtp:@sub2.example.com
	Prefix                     : SMTP
	IsPrimaryAddress           : False
	PrefixString               : smtp

So, given what we know now, what does the script look like? There are some similarities, but now it doesn't feel like a hack and it is much easier to read and understand:

	$objTemplate = '@example.com' | ConvertTo-SmtpProxyAddressTemplate

	$policy = Get-EmailAddressPolicy 'Default Policy'

	$EnabledAddresses  = $policy.EnabledEmailAddressTemplates
	$DisabledAddresses = $policy.DisabledEmailAddressTemplates

	$EnabledAddresses.Remove( $objTemplate )
	$DisabledAddresses.Add( $objTemplate )

	$policy |
		Set-EmailAddressPolicy -EnabledEmailAddressTemplates $EnabledAddresses `
				       -DisabledEmailAddressTemplates $DisabledAddresses

So, what is the lesson learned? Don't forget about ::Parse()! And, as almost always, in PowerShell there is often more than one way to skin a cat.

Until next time...

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

Posted by michael | with no comments

In December of 2007, I made two posts: File Level Antivirus for Exchange and Information Store Antivirus for Exchange.

Since that time, there is no question that with Exchange 2007 and Exchange 2010 (and the ongoing advancement of various Internet-based threats and malware), that the focus has moved to scanning incoming and outgoing emails with a gateway server and maintaining antivirus at the file level and not at the mailbox database (or Information Store) level.

Recently on Technet, Jeff Stokes, a PFE (Premiere Field Engineer) with Microsoft, published an updated list of ALL the antivirus exclusions that are current for various Microsoft products; both updating and going far beyond my posts in 2007.

The article is: Anti-Virus Exclusions and You.

If you are configuring any infrastructure server, you need to examine his list and ensure that your anti-virus adheres to the recommendations noted therein.

I did have minor input to this article for mentioning the relevant, current Exchange links.

An interesting possible project is to write a PowerShell script to check all of these various items for a given computer. That might be fun!

Until next time...

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

Microsoft has finally released detailed documentation regarding load balancing with both Exchange Server 2010 and OCS 2007 R2. There are three VERY interesting documents available that you should give a read.

They are:

Understanding Load Balancing in Exchange 2010
Load Balancing Requirements of Exchange Protocols
Microsoft Unified Communications Hardware Load Balancer Deployment

Enjoy!

Until next time...

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

***Edit on June 4, 2010 - correct spacing between articles in list.

Posted by michael | with no comments
Filed under:

You may - or may not - need to register Filter Packs after you install them.

As a prerequsite for installing Exchange 2010, you must install the Office 2007 Filter Packs. As noted in a recent blog post of mine, Office 2010 Filter Pack for Exchange 2007 and Exchange 2010, the Office 2010 Filter Pack was recently released. After installing Exchange 2010, you can upgrade to the Office 2010 version of the filter pack (and this is also supported for Exchange 2007).

It may be that Exchange 2010 SP1 will require the Office 2010 version of the filter pack.

However, it has recently come to my attention that installing the filter pack, regardless of the version, does not automatically cause the filter pack files to be registered to index the affected document types by Exchange server! After surveying several of my customer's Exchange servers, I determined that the set of documents that would be indexed by Exchange is dependent on what was being indexed by earlier versions of Exchange.

Therefore, after installing the filter pack, you still need to Make This Happen (tm) - i.e., ensure that the filter packs are registered. Smile

For Exchange 2010, the official Microsoft document is Register Filter Pack IFilters with Exchange 2010. An improved version of the script has been created by Pat Richard, and is available here. Pat's version also handles the required registration for the Adobe PDF iFilter pack, if you have downloaded and installed it.

Until next time...

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

Posted by michael | with no comments

One of the prerequisites for both Exchange 2007 and Exchange 2010 is to install the "filter packs". The filter packs are responsible for "filtering" the content of Microsoft Office documents and passing those contents back to the Indexing Service (ok, it's not called the Indexing Service anymore - it's Microsoft Search - and Exchange 2010 uses a slightly customized version of Microsoft Search called "Microsoft Search (Exchange)").

In Exchange 2003 and before, creating a full-text search index was optional - and "expensive".  Beginning with Exchange 2007, it became cheap (due to Exchange's use of the much improved Microsoft Search engine) and required. All Outlook Web App (Outlook Web Access) searches and all Outlook (online) searches use that index.

So, with the release of Office 2010, Microsoft has also released an update to the filter packs that support Office 2010 (plus a few other document types). The new filter packs can be installed on Exchange 2007 and Exchange 2010 (and it seems likely that they will be required for Exchange 2010 SP1). Filters are built-in for the following formats:

Legacy Office Filter (97-2003; .doc, .ppt, .xls)
Metro Office Filter (2007 and 2010; .docx, .pptx, .xlsx)
Zip Filter
OneNote filter
Visio Filter
Publisher Filter
Open Document Format Filter

You can download the Microsoft Office 2010 Filter Packs here

However, you may notice that a common file format is missing! PDF. In order to scan and index PDFs, you need to install a filter available from Adobe, the Adobe PDF iFilter 9 for 64-bit platforms. That will work with both Exchange 2007 and Exchange 2010.

Bharat Suneja, an ex-Exchange MVP who is now working for Microsoft, provides additional information about index generation and scanning on his blog Exchangepedia.(He gave you some stuff I didn't - and I've given you some stuff he didn't. It all works out!) :-)

Until next time...

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

Posted by michael | 1 comment(s)

Another Exchange MVP, Pat Richard, posted on Facebook yesterday: It would be nice to be able to import a list of all Exchange related PowerShell cmdlets into the Word and Outlook dictionaries.

Being the helpful guy that I am, I responded: You can do that in one-line of PowerShell.

(That being a famous quote from Jeff Snover, one of the main men behind PowerShell at Microsoft.)

Then I started thinking about it. And well, you CAN do it in one line of PowerShell. It just depends on how clever you want to get and how long a line you are willing to type! Regardless, you should only have to do it once.

Now, be aware, I'm running Office 2010 - file locations might be somewhat different for you.

Let's build that one liner.

First thing was to find where Word 2010 stored custom words added to the dictionary. F1 (Help) in Word gave me that - CUSTOM.DIC.

Second was to locate the physical file:

PS C:\Users\Administrator> cd \
PS C:\> cmd /c dir custom.dic /s
 Volume in drive C has no label.
 Volume Serial Number is D888-0076

 Directory of C:\Users\Administrator\AppData\Roaming\Microsoft\UProof

05/10/2010  08:50 AM            11,902 CUSTOM.DIC
               1 File(s)         11,902 bytes

     Total Files Listed:
               1 File(s)         11,902 bytes
               0 Dir(s)  77,055,504,384 bytes free
PS C:\>

Ah...good. So the filename is:

join-path C:\Users\Administrator\AppData\Roaming\Microsoft\UProof CUSTOM.DIC

or just

C:\Users\Administrator\AppData\Roaming\Microsoft\UProof\CUSTOM.DIC

If we examine the file, we see it's a simple Unicode file containing one token/symbol per line.  

How do we get the list of Exchange commands? Get-ExCommand, of course. But...if you aren't running the command on an Exchange server or on a workstation where the Exchange Management Tools are installed (e.g., you are using remote PowerShell to connect to an Exchange server), you won't have Get-ExCommand. It's a non-exported function. However, you WILL have the Exchange module you can use. So, to get the Exchange commands:

Get-Command -module $global:importresults

Note that $global:importresults can potentially contain more than just the Exchange module. It is a list of all imported modules.

We notice that this returns more than we want. We really just want imported functions and cmdlets that have valid names. So:

Get-Command -module $global:importresults | ?
    { ($_.CommandType -eq "cmdlet" -or $_.CommandType -eq "function") -and $_.Name -notmatch ":" }

That gives us our valid Exchange cmdlets and functions! However, all we are interested in is the name of the function; not all the other garbage:

Get-Command -module $global:importresults | ?
    { ($_.CommandType -eq "cmdlet" -or $_.CommandType -eq "function") -and $_.Name -notmatch ":" } |
    Select Name

Excellent. Now we have the names we want and we know where we want them to go! So let's save them:

Get-Command -module $global:importresults | ?
    { ($_.CommandType -eq "cmdlet" -or $_.CommandType -eq "function") -and $_.Name -notmatch ":" } |
    Select Name |
    Out-File -Append C:\Users\Administrator\AppData\Roaming\Microsoft\UProof\CUSTOM.DIC

or, since we know that's the per-user AppData folder: 

PS C:\> $env:AppData
C:\Users\Administrator\AppData\Roaming
PS C:\>

we can also say:

Get-Command -module $global:importresults | ?
    { ($_.CommandType -eq "cmdlet" -or $_.CommandType -eq "function") -and $_.Name -notmatch ":" } |
    Select Name |
    Out-File -Append (join-path $env:AppData Microsoft\UProof\CUSTOM.DIC)

And there you have it. A LONG one-liner, but a one-liner nonetheless.

This does assume that you are running this on the same workstation where the custom dictionary resides.

This procedure should be generalizable (is that a word?) for any module.

Pat and I both worked on this going back and forth on FB. An interesting way to develop a script!

Until next time...

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

P.S. Pat went on and further developed the script to automatically choose an Exchange server, connect to it, and update the CUSTOM.DIC. To see that, visit his website.

Posted by michael | 3 comment(s)
Filed under: , ,

Public Folder Contacts Can't Replicate

If you have a public folder contact that includes an e-mail address, there is also an e-mail address type field that is associated with every e-mail address.

Note: You cannot see this e-mail address type field by default - but it's still there. To view it, go to a Contacts folder in Outlook and create a custom view. In that view, add "Full Name", then select "E-Mail Address Fields" and add "E-Mail Address" and "E-Mail Address Type". Now, examine the Contacts in the folder using the custom view. You'll see e-mail address types such as "SMTP" for external Internet contacts, "EX" for internal organization e-mail contacts, "FAX" if you have a fax connector installed, etc.

Now, Exchange 2003 (and perhaps Exchange 2007 - I have not checked in my lab) allowed two differerent e-mail address types to indicate "SMTP". I believe this to be a hold-over from Outlook 97, although I have no documented proof of that (but in Outlook 97 we had "Internet mode" and "Corporate or Workgroup mode" - so it makes sense). The two different types are "SMTP" and "POP3/INTERNET".

"POP3/INTERNET" is not valid in Exchange 2010. If you attempt to replicate a public folders to Exchange 2010 that contains this e-mail address type, the replication will abort. Thankfully, you do receive an event log error message that provides SOME clues about this occurring. The error looks like this:

Event Type:      Error
Event Source:    MSExchange Store Driver
Event Category:  (1)
Event ID:        1020
Date:            5/11/2010
Time:            10:00:43 AM
User:            N/A
Computer:        exchmb2.example.com
Description:
The store driver couldn't deliver the public folder replication message "Folder Content Backfill Response (exchmb1-IS@example.com)" because the following error occurred: Property validation failed. Property = [{00062004-0000-0000-c000-000000000046}:0x8082] Email1AddrType Error = The length of the property is too long. The maximum length is 9 and the length of the value provided is 13... For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

What you will have to do is, using Outlook, obtain a list of all the affected contacts as described above, and also using Outlook, change the address type to SMTP.

In Outlook 2010, you can directly edit this field. In Outlook 2007, you will need to open each contact, right click on the e-mail address in the default display, and select Properties. Then, for the Address Type field, click the "Internet" button (this changes the e-mail address type to "SMTP" - the button will now display "Custom"). Then "Save and Close" the updated contact.

In my migrations, I've only had a maximum of about 150 of these. If you have thousands, you will probably need to consider writing a webdav application/script to run against the Exchange 2003 server(s).

Until next time...

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

The TrustedInstaller - Isn't.

Generally, when applying patches (whether service packs or hotfixes or rollups), the installation process will automatically acquire all the necessary permissions - if the user executing the process CAN acquire those permissions. This is especially relevant under Server 2008 and Server 2008 R2, where an interactive logged in user has their access token artificially limited, even if UAC is disabled.

However, the Exchange 2010 update installer either drops administrative permissions too early or never acquires all of the permissions that are necessary. When applying update rollups, binaries are updated just fine - but OWA source files are not.

This commonly leads to a patch application that appears successful - but it isn't. When testing OWA after an update-rollup appliction, a common error is "syntax error in flogon.js at 1, 1." This is an indication that the patch was NOT installed with administrative permissions.

Reapply the patch with administrative permissions.

Note: I have heard reports that this begins to affect Exchange 2007 AFTER the application of service pack 2, when Exchange 2007 is installed on Windows Server 2008.

This has (at this writing) been seen to affect Exchange 2010 UR1, UR2, and UR3.

To properly ensure that an application of an update-rollup has adequate permissions, do one of the following:

  • Right-click on the patch (filename.msp) and click on "Run as Administrator"
  • Open an elevated command prompt and then start the patch (just enter filename.msp). To open an elevated command prompt, click Start, then enter "cmd" into the search area, right click on the cmd.exe that appears in the results area and click on "Run as Administrator".
  • Open an elevated PowerShell session and then invoke the patch (enter "ii filename.msp"). The open an elevated PowerShell session, click Start, then enter "PowerShell" into the search area, right click on the "Windows PowerShell" that appears in the results area and click on "Run as Administrator".

Until next time...

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

Incoming e-mail CAN'T come in!

This issue is not exclusive to Exchange 2010 - it also exists in Exchange 2007.

The default receive connector created by the Exchange setup process does not include permissions to include "Anonymous users" on the default server permission group. Microsoft assumes that you will be using their Edge Server product (which isn't Anonymous, but Authenticated).

Of course, most people (? - at least my customers!) will not be using the Microsft Edge Server product, but some other gateway e-mail product.

Therefore, you will need to set the "Anonymous users" permission on the default server permission group.

Otherwise - incoming Internet e-mail will bounce!

Until next time...

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

[Edit on April 15, 2010 to spell "Authenticated" correctly.]

In last week's blog post, Exchange 2010 Gotcha  - #1, I use the Add-AdPermission PowerShell cmdlet to add a set of permissions to each mailbox database. One of those permissions was the ms-Exch-Store-Admin permission.

If you've ever installed any BlackBerry software before, or ever installed Cisco's Unity line of products before, you've seen this permission mentioned. This permission was introduced in Exchange 2000 and provides the capability of "administering an information store". That's a pretty non-specific permission, but that's really all the available Microsoft documentation says about it. See, for example, here.

It's worthwhile to note that, by itself, this permission does little. But when combined with the View-Only Organization Management permission (for example) and the Send-As permission, it allows a grantee the capability of doing a Send-As for any user within an information store for which they have that right - without knowing the password of the user who owns the mailbox. When combined with the Modify Permission permission, it allows the grantee to change Full Control assignments for a given mailbox, etc. When combined with the Server Administrator permission, it allows the grantee the capability of mounting and dismounting databases, moving mailboxes, etc.

That is, the ms-Exch-Store-Admin permission has some pretty hefty powers and should not be granted lightly.

Unfortunately, there is no available public documentation that fully defines this permission and/or what capabilities it may allow. Suffice it to say that you should carefully consider whether granting this permission is an appropriate choice for any arbitrary user account or group. The answer is "probably not".

Thanks to Ross Smith, IV and Bill Long for answering a question of mine that led to this post.

Until next time...

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

Posted by michael | 8 comment(s)
Filed under: ,

As I work through migrations to Exchange Server 2010 with my various clients, I'm developing a list of Exchange 2010 "gotchas". Not necessarily things that are earth-shattering, but do have the potential to be surprising to administrators.

Gotcha #1 revolves around the fact that an Offline Address Book is no longer automatically specified for a new mailbox database. Surprise!

So, just like most other things that require specific processes in Exchange, you should develop a list of items to be performed when you create a new mailbox database, whether you use the Exchange Management Console or the Exchanage Management Shell. Consider this as a list:

  1. Create the database, specifying an individual folder for the database (e.g., E:\DB-Zippy\Zippy.edb) and an individual folder for the log files (e.g., E:\Logs-Zippy). If you are using the EMC, and you do not want to take the default paths, you will have to type the entire path into the path fields (there is no browse button). As a suggestion, using Windows Explorer, first browse to the path where you want to put the files, and copy them into the clipboard, then you can paste them directly into the fields in EMC (or into the EMS, where you always have to enter the entire path).
  2. Set the Offline Address Book (OAB). If you are using the EMS, you can specify the OAB (e.g., "\Default Offline Address Book") when you create the database. When using EMC, you'll have to open the property sheet for the mailbox database, click the Client Settings tab, and select the address book via the Browse... button. You should also take this opportunity to verify that the Public Folder database is a valid public folder database (if you still have public folders in your environment).
  3. Set the Journal Recipient. You cannot specify the Journal Recipient during creation when using either EMC or EMS. With EMC, you'll have to open the property sheet for the mailbox database, click the Maintenance tab, check the box to select the journal recipient, and browse for the particular user.
  4. Set external permissions. If you are using Cisco Unity or RIM's BlackBerry Server (Enterprise, Professional, or Enterprise Express), then you'll need to set additional permissions to allow those software packages to access mailboxes contained in this mailbox database. There is no mechanism for performing this operation using the EMC. For BlackBerry, this is the relevant command, executed from the EMS (assuming that your BlackBerry administrative user is named BESAdmin):

Get-MailboxDatabase | Add-ADPermission -User BESAdmin -AccessRights ExtendedRight -ExtendedRights Receive-As, Send-As, ms-Exch-Store-Admin

Obviously, there are additional parameters that can be set. However, these four meet the needs of all my clients and probably will work for you too!

Watch this blog for more "Exchange 2010 gotchas".

Until next time...

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

More Posts Next page »