Friday, 24 February 2012

VMWare - Lots of disk activity after you shut down a virtual machine


Somehow I always found VMWare easier to use than HyperV, except for one minor detail: after I shut down a virtual machine, the VMWare host starts accessing my local harddisk like a maniac. This heavy disk load can take up to 20 minutes, in which I'm
  • unable to boot another VM. Well technically I can boot another one, but it's so slow you can't do a thing in it.
  • unable to shut down my laptop ... Windows waits until the VMWare host has finished his frantic disk rampage.
I came to live with these terms, so usually I shut down my virtual machine 30 minutes before my shift ends (lets hope my boss doesn't read this blog). Still, it's a pain in the **** if you have another meeting elsewhere and you're stuck staring at a Windows is shutting down message because VMWare has still business to do on your disk.

Luckily, lately I got a nice solution from one of my new colleagues.
If you add mainMem.useNamedFile = "FALSE" to your vm's configuration file, the post-shutdown disk activity stops.

Step-by-step
  1. Make sure your VM is turned off.
  2. Open the virtual machine's VMX file in notepad.
  3. Append mainMem.useNamedFile = "FALSE" at the bottom of your vmx file.
  4. Save your changes.
  5. Boot your VM again.
We're not sure what this setting actually does, but at least it works :)
I've been testing it for the past month on all my VM's and I must say, I haven't had any problems. The virtual machines still work, their performance hasn't dropped, but now they shut down in a few seconds and without any increased disk activity afterwards.

Friday, 17 February 2012

Search result URLs not correctly mapped to the current zone when the content is crawled in a different farm

This one is just a nice-to-know. I didn't know about this setting, so I'll briefly document it here for future reference.

Situation
The set-up consists of a typical enterprise services farm (http://technet.microsoft.com/en-us/library/cc560988.aspx#Enterprise):

  • Farm A hosts the collaboration web-applications, in which the users upload their documents and other content.
  • Farm B is a remote services farm and hosts the cross-farm service applications. This includes all of our search service application(s). These service applications are published and then consumed on Farm A. 
Farm B crawls farm A using the web-application's default zone url, e.g. http://sps2010. When a user performs a search in farm A on the default zone, all of his search results link back to the default zone. So far so good.

The problem
Now lets say we add a new Intranet AAM zone on farm A's web-applications, which points to http://sps2010intra. When a user performs a search from that zone, all of his search results will again refer to the default zone!

The explanation
This is actually pretty logical: the content is crawled on farm B, using the default zone's URL.
In a single farm setup, the search service application will have access to the crawled web-application's alternate access mappings and will take those into account when a query is done on the crawled content.
Here however, the search service application is hosted on a different farm (farm B) and that search service application doesn't know how the alternate access mapping is set up on farm A!

The solution
When you google this problem, you will get a lot of posts saying that you need to use the Server Name Mapping setting on your search service application. There's a good overview of this setting here.

Personally I would not recommend using that approach, as it only works for one AAM zone.  There's no way to map multiple zones (intranet AND internet for instance) using the server name mapping route.

A much more flexible solution is to mimic the crawled content farm's alternate access mappings on the remote services farm. If you did set-up server name mappings to fix this: delete them and recrawl! ;)
  1. Open the central admin site on your remote services farm (farm B).
  2. Click on Application Management and select Configure alternate access mappings (in the web applications section).
  3. Now click on Map to external resource in the toolbar. This little bugger allows you to create an alternate access mapping for resources that aren't in the farm.
  4. Give it a meaningful name, like Collab {Name of the crawled webApp}. Enter the crawled web-application's default zone URL in the URL protocol, host and port field.
  5. Click on Save to create the external resource AAM. Now you can set this resource up as if it were a web-application on your remote services farm!
  6. Use Edit Public URLs and Add Internal URLs to configure the external resource's AAM just like it is set up on your crawled farm (farm A).
That's it ... no need to recrawl or anything. If all was done well, search results on the content farm (Farm A) will now be correctly translated according to their zone, even while the content was indexed on another farm.





SP2010 - Search Crawl not working on remote services farm


A quick post on two issues I've encountered lately. Both are related to SharePoint 2010 Search crawls that suddenly stopped working.
I'm just going to post the symptoms and what worked as solution for me.

The SharePoint set-up consists of two farms:
  • Farm A hosts the collaboration web-applications, in which the users upload their documents
  • Farm B hosts most of the service applications, including the search service application(s). These search service applications crawl content from Farm A. Furthermore, these service applications are consumed on Farm A (so people on that farm can search the crawled content).

Issue #1
The crawl log was filled with the following errors when crawling farm A:
The SharePoint item being crawled returned an error when requesting data from the web service. ( Error from SharePoint site: Value does not fall within the expected range. ).

Solution
Apparently there was an issue with the web-apps that were being crawled. In order to get them crawled again, I
  1. Detached the content databases linked to the web-apps on farm A
  2. Removed the web-app (make sure to delete the related IIS site)
  3. Recreate the web-app
  4. Attach the old content db again
Simple as that.

Issue #2
Some weeks later, my crawl log got filled with this lovely message:
The SharePoint item being crawled returned an error when requesting the data from the web service. ( Error from SharePoint site: *** Could not find part of the path 'c:\TEMP\gthrsvc_OSearch14\{random-text}.tmp' 

Solution
Despite the error, that path was indeed present on all my machines in farm B. I tried fiddling with the permissions on that folder, but nothing helped.

In the end I just:
  1. Removed the consumed search service applications on farm A.
  2. Removed the search service application(s) on farm B
  3. Recreated the search service application(s) on farm B. Luckily for me, I had already scripted this procedure in PowerShell.
  4. Publishd the service apps again on farm B and consume them on farm A.
  5. Recrawled all content
Conclusion: if some part of SharePoint is fucked up, you can usually fix it by removing & recreating it :)

Friday, 19 August 2011

SP 2010 user profile service app - ILM Database could not be created

This week I've been battling with the following error during a SharePoint 2010 user profile service app setup: ILM Database could not be created.

I had created some powershell scripts to set up the user profile service applications automatically on multiple farms. These scripts worked like a charm on most farms, except one.
The service application itself was correctly created, but when the script tried to start the User Profile Sync Service it failed. The service itself briefly went to a Started state, but reverted back to its Stopped state after a few minutes.

A glance in the event logs revealed a number of warnings, that were logged at the time the service started:

Warning 1
ILM SQL Configuration file could not be read: 
Error Parcing the SQL File: C:\Program Files\Microsoft Office Servers\14.0\Sql\StoredProcedures.sql  

Exception: Conversion of one or more characters from XML to target collation impossible

Warning 2
ILM Database could not be created: Error sent to Windows Event Log running : 
-- Proc Name:	GetObjectDisplayNames
-- Purpose:		to get display names for objects
-- Input:		@ObjectIDs, which we suppose to take in an array of Guids, in the format of:
--				(?B553ECA-F359-11DB-AABA-D0C055D89593? ... , �A5B106DC-F359-11DB-9C44-1EC155D89593?
-- Exception:
--				If failed to find certain object id, SQL error occurs
CREATE PROCEDURE [dbo].[GetObjectDisplayNames]
@ObjectIDs     NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Sql	NVARCHAR(MAX)
SET @Sql =
N' SELECT ValueString FROM ObjectValues ' +
N' WHERE AttributeName = N''DisplayName'' ' +
N' AND ObjectID IN ' + @ObjectIDs
EXEC sp_executesql @Sql
END
: Conversion of one or more characters from XML to target collation impossible

These warnings repeated themselves a few times and then it said: Sync service stopped succesfully. Just brilliant, ain't it?
As I couldn't find any clues online, I presumed I was the first to get this kind of exotic crap.
So in the next days I tried to fix it ... to no avail however. I tried:
  • rerunning the script (who knows, maybe it was a one-time glitch)
  • installing the user profile service app manually
  • reinstalling sharepoint completely
  • installing SP1
  • formatting and reinstalling all servers in that $*@! farm

But finally I found a workaround. It involves tampering with some out-of-the-box installation files, so it will void your warranty/support from Microsoft. Proceed with caution! ;)

Solution
I noticed there was a strange token in the message displayed in the second warning in the event-log.

Could it be that easy? Was this token the source of all that "Conversion of one or more characters from XML to target collation impossible" crap?
I was pretty desperate at that time, so I was willing to give it a shot. Here's what I did:

  1. Open an explorer window and go to C:\Program Files\Microsoft Office Servers\14.0\Sql
  2. Locate the file StoredProcedures.sql, this contains the stored proc that was mentioned in the error.
  3. Take a backup of this file as we'll have to modify it. Always take a backup if you're going to tinker with ootb SharePoint files ;)
  4. Open the file in notepad and look for the entry CREATE PROCEDURE [dbo].[GetObjectDisplayNames]
  5. If you scroll up a bit, you should notice a line mentioning "... , ’A5B106DC-F359-11DB-9C44-1EC155D89593?"
  6. Now replace that sign in front of the A5B106DC-F359-11DB-9C44-1EC155D89593 guid with a ? char.
  7. Save the file again

After that I reran the script and a miracle happened (*insert praise for whatever deity you believe in*): everything worked like a charm. The sync service started properly after a few minutes and the user profile service app synced properly.

I'm still not sure why this error only occurred on one farm, while all the others installed without any hassle. All I know is that this fixed it for me. Feel free to leave your ideas on why it works in the comments :)


So there you have it, another dirty fix for a SharePoint 2010 issue. Maybe I should rename this blog to Dirty solutions for exotic sharepoint issues ... :/

Note
Either way, I've also opened a case with Microsoft Support themselves, so hopefully they'll fix this in a future update or provide a better solution. Once I get more info from them, I'll let you know.

Tuesday, 16 August 2011

VMWare on a machine hosting the Hyper-V role

A quick trick if you want to run VMWare workstation on a machine that's running Hyper-V.
Normally you can't run VMWare when the Hyper-V role is enabled on a machine, as the hypervisor will claim some resources upon startup ... which are also needed by VMWare to run its virtual machines.

But I found the following trick to work like a charm:
  1. Open a command prompt and type the following:
    bcdedit /copy {default} /d "Windows Without Hypervisor"
    The above command should say: The entry was successfully copied to {guid}.
  2. Copy that guid to the clipboard (including the curly braces!).
  3. Finally, execute the following command in the command prompt:
    bcdedit /set {guid} hypervisorlaunchtype off
    Replace {guid} with the GUID that you copied in step 2.

This will create a new entry in your boot menu, called "Windows Without Hypervisor".
  • If you want to run VMWare, just boot your machine using that entry. You won't be able to host HyperV machines, but VMWare will run just fine.
  • If you want to use HyperV, boot your machine using the default boot entry. This will allow you to run Hyper-V machines, but no VMWare.

Yes, the workaround doesn't fix the fact that VMWare vm's can't coexist with HyperV machines ... but for now that's just fine for me. At least now I can run both vm's on my laptop :)

This workaround was also mentioned here, but the original blogpost is no longer available so I just reposted it here. (All credits go to the original poster of course)

Wednesday, 20 July 2011

Update - Creating SharePoint 2010 user profile exclusion filters using Powershell

This post is actually a follow-up on my previous post on user profile exclusion filters.

A quick recap of that post:
  • I needed to script the creation of a user profile service application, along with some profile exclusion filters.
  • Unfortunately I couldn't find any existing reference on script the creation of user profile exclusion filters (only guides on how to do this in the central admin pages).
  • Took a peek at the code SharePoint uses to create exclusion filters in central admin, only to see they use internal/hidden classes & methods to create these filters.
  • In the end I couldn't find any public API/powershell cmdlet to create these filters, so ended up writing my own workaround: a small .NET 3.5 project that will invoke the internal/hidden SharePoint 2010 methods using reflection.

Still, for me there was something missing: an easy way to use this workaround in PowerShell. Cause we all know: Powershell rocks! :p

First attempt
Originally I wanted to translate the complete .NET 3.5 workaround into one (or more) ps1 scripts. This would eliminate all need for external assemblies and is just easier to read/fix should something happen.
I managed to translate 99% of the .NET code, which was actually easier than I thought it would be. It was a nice exercise in the usage of reflection and generics within PowerShell 2, so I might make a specific blogpost on this subject later on :)

However, 99% is not 100%. I just couldn't get the final step working: how to assign the FilterSet objects (the actual filters) to the user profile connection.
Internally SharePoint 2010 uses the SetExclusionFilters method - something I had to call using reflection. This worked like a charm from within a .NET project, but I couldn't get PowerShell to cast the objects to the correct internal classes (despite the fact that Get-Member did mention it was casted correctly).
As a result, I kept getting the following error ... no matter what I tried

Exception calling "Invoke" with "2" argument(s): "Parameter count mismatch."
At Functions.ps1:96 char:35
+ $setExclusionFiltersMI.Invoke <<<< ($connection, @($filterSetList)) + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException
When I find the time, I'll post the ps1 scripts too ... maybe one of you can solve this mystery.

First Second attempt
Soooo translating it all into ps1 scripts didn't work, but no worries! Time for a workaround .. for a workaround?!
This time I just wrote another .NET project, which defines some custom cmdlets. These cmdlets will then invoke the .NET code that I described in my previous post. On the bright side: the cmdlets can be easily used from within PowerShall and it actually works! :)
(But you're still stuck with a bunch of .NET assemblies that you cannot change on the fly ... can't have it all, I guess.)

Before I start a lengthy description of the cmdlets I developed, I'll post provide a quick overview. Use these links if you can't be a***d / bothered to read manuals :)

The project defines these cmdlets
  • Get-FilterSetCondition
  • Get-FilterSet
  • Set-ExclusionFilter
  • Get-FilterAttributes
  • Get-FilterSetConditionOperators
  • Get-FilterSetOperators

Get-FilterSetCondition
Use this cmdlet to create a new condition for your exclusion filter. These conditions are internally stored as FilterSetCondition objects, hence the naming. You can find an example of this in Example1.ps1 and Example2.ps1
Syntax:
Get-FilterSetCondition -Connection $connection `
                       -ConditionType "user" `
                       -Attribute "userAccountControl" `
                       -Operator "Bit_on" `
                       -Value "2"
Parameters:
  • Connection
    The connection on which you want to create an exclusion filter (Microsoft.Office.Server.UserProfiles.Connection object)
  • ConditionType
    Whether this condition applies to a user-related attribute or a group-related attribute (just like you have separate windows in the UI to specify user and group filters)
  • Attribute
    The attribute on which you want to filter, e.g. company or userAccountControl. Use the Get-FilterAttributes cmdlet to get an overview of all available attributes.
  • Operator
    The operator that you want to use in this condition, e.g. "Bit_on" or "Equals". Use the Get-FilterSetConditionOperators cmdlet to get an overview of all available operators.
  • Value
    The string-value you want to use in this condition.

Get-FilterSet
Use this cmdlet to create a new exclusion filter, based on conditions you've created using the Get-FilterSetCondition cmdlet. Do not mix user-based conditions with group-based conditions though! You can find an example of this in Example1.ps1 and Example2.ps1
Syntax:
Get-FilterSet -FilterType "user" `
              -Operator "or" `
              -Conditions $userConditions
Parameters:
  • FilterType
    Whether this filter contains user-related conditions or a group-related condition
  • Operator
    The operator that you want to use to combine the conditions. Either supply "or" or "and" as value.
  • Conditions
    An array of FilterSetCondition objects (which you created using the Get-FilterSetCondition cmdlet)

Set-ExclusionFilter
Use this cmdlet to assign exclusion filters (created using the Get-FilterSet cmdlet) to an existing user profile connection. You can either assign a user-based exclusion filter, a group-based exclusion filter or both.
You cannot however assign multiple user-based exclusion filters or multiple group-based exclusion filters. You'll have to combine these into a single user-based exclusion filter or a single group-based exclusion filter. An example of this cmdlet can be found in Example1.ps1 and Example2.ps1
Syntax:
Set-ExclusionFilter -Connection $connection `
                    -UserFilter $userFilter `
                    -GroupFilter $groupFilter
Parameters:
  • Connection
    A reference to a user profile connection (Microsoft.Office.Server.UserProfiles.Connection object)
  • UserFilter
    The user-related exclusion filter you want to set on the connection. Don't supply this parameter if you don't want to set a user-related exclusion filter.
  • GroupFilter
    The group-related exclusion filter you want to set on the connection. Don't supply this parameter if you don't want to set a group-related exclusion filter.

Get-FilterAttributes
Use this cmdlet to get a list of all available user-related or group-related attributes on a given user profile connection. Each of the returned strings can be used in the Get-FilterSetCondition cmdlet to create a condition on. You can find an example of this in Example3.ps1
Syntax:
Get-FilterAttributes -Connection $connection -User
Get-FilterAttributes -Connection $connection -Group
Parameters:
  • Connection
    A reference to a user profile connection (Microsoft.Office.Server.UserProfiles.Connection object)
  • User
    A switch parameter. Supply this if you want to list all user-related attributes on the connection. Cannot be used in combination with the -Group switch
  • Group
    A switch parameter. Supply this if you want to list all group-related attributes on the connection. Cannot be used in combination with the -User switch

Get-FilterSetConditionOperators
Lists all available operators that you can use in Get-FilterSetCondition

Get-FilterSetOperators
Lists all available operators that you can use in Get-FilterSet


That's brilliant ... but how do I use this in SharePoint's management shell?!
  1. Download either the complete package or the standalone archive. Look in the download section for a link ...
    • The standalone package only contains the necessary assemblies and some example scripts.
    • The complete package also contains the source-code I used, so you could make your own modifications. The source-code is located in the src folder, but I've also copied the necessary assemblies and examples in the distribution folder for easy access
  2. Whatever you downloaded, copy the VNTG.UserProfileFilters.PowerShell.dll and VNTG.UserProfileFilters.dll assemblies to a folder on your SharePoint 2010 server.
  3. Open a new SharePoint 2010 Management Shell on that server
  4. Load the cmdlets using Import-Module ".\VNTG.UserProfileFilters.PowerShell.dll"
  5. That's it! You can now use the cmdlets to create exclusion filters. Don't forget to check out the Example*.ps1 scripts that are also present in the downloaded packages. These scripts provide you with some real-life scenarios to create exclusion filters using the new cmdlets.


Disclaimer and downloads
Like I mentioned in the first part of this post: the workaround is based on reflection, in order to call the internal classes/methods that SharePoint 2010 uses in its Central Admin pages.
I'm sure they're hidden for a reason, so this workaround is definately not officially supported!

USE IT AT YOUR OWN RISK!
Neither I nor my bosses at Ventigrate can be held reliable if something goes wrong!
(The download is hosted at their codeplex project, so they forced me to add this disclaimer ... :p)

If you're interested in the complete source: use this download. This zip-archive contains the source of both the .NET workaround mentioned in the first part, as well as the powershell cmdlets. It also comes with additional examples to clarify its usage.

If you're not interested in any source code, but just want to use this workaround in your SharePoint 2010 Management Shell: use this download. This archive only contains the necessary .NET assemblies and some example ps1 scripts on how to use the cmdlets in your Powershell 2 console.


Examples
I'll list the same example that was mentioned in my original post - only this time using the custom cmdlets. This example is also available as Example1.ps1 in the downloads.
This code will create a user-attribute based exclusion filter and a group-attribute based exclusion filter and assign it to a user profile connection.
  • The user-attrib exclusion filter will filter on "userAccountControl bit on equals 2" or "company equals ventigrate"
  • The group-attrib exclusion filter will filter on "name equals testgroup"
Check the Example2.ps1 script if you want an example that only adds a user-attribute based filter.

#region STEP 1: load the necessary references
#--------------------------------------------
# Load the sharepoint powershell cmdlets
Add-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue | Out-Null

# Load the custom userprofile cmdlet dll
Import-Module ".\VNTG.UserProfileFilters.PowerShell.dll"
#endregion

#region STEP 2: get the user profile connection on which you want to create exclusion filters
#--------------------------------------------------------------------------------------------
# Get a reference to the service application called 'User Profile Service Application' (very original)
# and a connection called Example 1.
# Change these parameters to whatever user profile service app & connection you want to change on 
# your environment...
$serviceAppName = "User Profile Service Application"
$connectionName = "Example 1"

$profileApp = Get-SPServiceApplication | ? {$_.DisplayName -eq $serviceAppName}
$profileContext = [Microsoft.SharePoint.SPServiceContext]::GetContext(
          $profileApp.ServiceApplicationProxyGroup, 
          [Microsoft.SharePoint.SPSiteSubscriptionIdentifier]::Default)
          
# Get a new UserProfileConfigManager
$configManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($profileContext)

# Get the connection called "Example 1" in the service application
$connection = $configManager.ConnectionManager | ? {$_.DisplayName -eq $connectionName}

#endregion

#region STEP 3: create the conditions and bind them into an user-filter and a group-filter
#-----------------------------------------------------------------------------------------
# Execute Create-FilterSetCondition to create 2 user-related conditions and 1 group-related condition
# - userCondition1 = userAccountControl bit on equals 2
# - userCondition2 = company equals ventigrate
# - groupCondition = name equals testgroup
$userCondition1 = Get-FilterSetCondition -Connection $connection -ConditionType "user" -Attribute "userAccountControl" -Operator "Bit_on" -Value "2"
$userCondition2 = Get-FilterSetCondition -Connection $connection -ConditionType "user" -Attribute "company" -Operator "Equals" -Value "ventigrate"
$groupCondition = Get-FilterSetCondition -Connection $connection -ConditionType "group" -Attribute "name" -Operator "Equals" -Value "testgroup"

# Combine the user-related conditions in a single user FilterSet, using an OR operand
$userConditions = @($userCondition1, $userCondition2)
$userFilter = Get-FilterSet -FilterType "user" -Operator "or" -Conditions $userConditions

# "Combine" the group condition in another group FilterSet, using an AND operand (though it doesn't matter as it's a single condition ;))
$groupConditions = @($groupCondition)
$groupFilter = Get-FilterSet -FilterType "group" -Operator "and" -Conditions $groupConditions

#endregion

#region STEP 4: assign the filters to the connection
#---------------------------------------------------
# Assign the newly created filters to the connection
Set-ExclusionFilter -Connection $connection -UserFilter $userFilter -GroupFilter $groupFilter

#endregion

I think this code is pretty self-explaining (lots of comments in there).
As always: leave a comment if something isn't 100% clear ... or if you just want to post some constructive feedback :)

Monday, 11 July 2011

SharePoint 2010 - Creating user profile exclusion filters using code

Ah, user profiles in SharePoint 2010. When I initially read about them, I was really impressed by the concept: no more SSP, nicely separated service applications, etc. But now that I had the honour to experience them first-hand in some real-life scenarios … well, let’s say they sometimes want me to unleash my inner hulk.

Anyway I digress. In this post I’d like to cover a little black spot in the user-profile API. As you might know, SharePoint 2010 ships with a ****load of PowerShell commands and API functions that allow you to script your entire farm set-up. If you Google around, you’ll find plenty of blogs on how to script various pieces of your SharePoint farm.
Nevertheless there’s something I just can’t seem to find any info on: how to script the creation of user profile connection exclusion filters. There’s even no mention of them in the TechNet resources. Plenty of guides on how to set them up using the UI though, but nothing scriptable.

Setting up filters in the SP2010 UI - Step1

Setting up filters in the SP2010 UI - Step 2
Unfortunately my current employer wants to script everything regarding their farm installation, so I had to get my own hands dirty.

Fire up the disassemblers!
This time I didn’t rely on my old companion Reflector (see my earlier post for more details), but I tried out three other products that attempt to fill the void that Reflector left:
  • ILSpy
  • JetBrains dotPeek
  • Telerik JustDecompile
I’ll probably post a small review on all three later on. Using these babies I loaded up the code-behind from the /_layouts/editconnectionfilters.aspx page, which is used to set the exclusion filters with the UI. Its code behind is the Microsoft.SharePoint.Portal.UserProfiles.AdminUI.EditConnectionFilters class in the Microsoft.SharePoint.Portal assembly.

<insert random curse />

Apparently Microsoft uses internal classes to create these exclusion filters. There goes plan A: call the undocumented public API to create the filters. Also, it doesn’t seem like these internal classes are referenced in any existing SharePoint PowerShell cmdlets, so out went plan B too: call some undocumented cmdlets.

… and in comes plan C
So with all easy options gone, it’s time to bring out the big guns: use reflection to invoke the internal classes in a similar way as it’s done in the UI’s code-behind (of course they just reference the objects without reflection).

Warning: calling internal classes with reflection is certainly not supported in any way! I’m sure Microsoft had their reasons to keep them internal. Or maybe they were just too busy trying to get everything working before the product’s shipping date & forgot to write a public API for these filters. :)
In any case: use the following code at your own risk. I’m not sure if Microsoft will void your warranty for this, but a warned man/woman …
I'm not going to delve too deep into the internal workings of SharePoint and how they handle the creation of exclusion filters ... but briefly summarized they use the following internal classes:
  • FilterSetCondition (located in the Microsoft.Office.Server.UserProfiles.Synchronization assembly)
    This class represents a single “rule” in the filter, e.g. [userAccountControl bit on equals‘2’]
  • FilterSet (also located in the Microsoft.Office.Server.UserProfiles.Synchronization assembly)
    This represents a set of rules (FilterSetCondition objects), combined together using either an “All apply” operator (AND) or an “Any apply” operator (OR) [just like the UI]. User-related rules are combined in one FilterSet, group-related rules are combined in another FilterSet object.
  • Both FilterSet objects are then assigned to the user profile sync connection using the internal SetExclusionFilters method on the connection object.
I'm in a lazy mood today, so I suggest you browse the internal classes yourselves to get a clear view on how it all ties together. (+ I don't want to anger Microsoft too much by pasting bits of their code)


Reflection workaround

You can find the complete source of this workaround on codeplex. The code should also give you some clues on how things are handled internally in SharePoint. The codeplex download consists of two VS2010 projects:
  • a class library project with the actual workaround: VNTG.UserProfileFilters
  • a console application with some examples on calling the class library objects: VNTG.UserProfileFilters.Examples
I tried to properly document everything in that codeplex project, so I hope it's all self-explanatory.
UPDATE: This solution is mainly aimed at Visual Studio 2010 (.NET) projects. If you're looking for a PowerShell-friendly implementation of this workaround, check out part 2 of this post

Once again, use this code at your own risk as it's not officially supported! 
just covering my ass ;)



Just as a teaser I'll also paste one of the examples here. It should give you an idea on how easy it is to add those exclusion filters with the workaround.

Example 1
In this example we’ll add 2 user-based exclusion filters (any apply) and a single group-based exclusion filter to a user profile connection called Local AD:
  • User exclusion filter: ["userAccountControl bit on equals '2'" OR "company equals 'ventigrate'"]
  • Group exclusion filter: ["name equals 'test'"]

// Get a reference to the first available user profile service app
SPFarm localFarm = SPFarm.Local;
var upServiceproxy = SPFarm.Local.Services.Where(s => s.GetType().Name.Contains("UserProfileService")).FirstOrDefault();

if (upServiceproxy != null)
{
 var upServiceApp = upServiceproxy.Applications.OfType<SPIisWebServiceApplication>().FirstOrDefault();
 if (upServiceApp == null)
 {
  Console.WriteLine("No User profile svc app found");
  return;
 }

 SPServiceContext ctx = SPServiceContext.GetContext(upServiceApp.ServiceApplicationProxyGroup, SPSiteSubscriptionIdentifier.Default);
 Microsoft.Office.Server.UserProfiles.UserProfileConfigManager upConfigManager = new Microsoft.Office.Server.UserProfiles.UserProfileConfigManager(ctx);

 // Let's create some user filters for the sync connection called "Local AD"
 var connection = upConfigManager.ConnectionManager["Local AD"];                
 if (connection != null)
 {
  //----------------------------------------------
  // Create a set of user-related rules, any apply
  //----------------------------------------------
  Filters userFilter = new Filters("user");
  var fsCondition1 = userFilter.CreateFilterSetCondition(connection, "userAccountControl", Filters.FilterOperator.Bit_on, "2");
  var fsCondition2 = userFilter.CreateFilterSetCondition(connection, "company", Filters.FilterOperator.Equals, "ventigrate");

  // get the actual filterset based on an OR between all conditions
  List<object> conditions = new List<object>();
  conditions.Add(fsCondition1);
  conditions.Add(fsCondition2);
  var userFilterSet = userFilter.GenerateFilterSetForConditions(conditions, Filters.FilterSetOperator.Or);

  //------------------------------------------------------
  // Create a set of group-related rules, all apply (=AND)
  //------------------------------------------------------
  Filters groupFilter = new Filters("group");
  var groupCondition1 = groupFilter.CreateFilterSetCondition(connection, "name", Filters.FilterOperator.Equals, "test");

  conditions = new List<object>();
  conditions.Add(groupCondition1);
  var groupFilterSet = groupFilter.GenerateFilterSetForConditions(conditions, Filters.FilterSetOperator.And);

  //---------------------------------------------
  // Add the created filtersets to the connection
  //---------------------------------------------
  Filters helper = new Filters("");
  helper.SetExclusionFilters(connection, userFilterSet, groupFilterSet);
 }
}
Example code

Lets dissect this example. First part of the code is to retrieve the user profile connection itself. This is done using the public API of SharePoint 2010 (no reflection at all).
SPFarm localFarm = SPFarm.Local;
var upServiceproxy = SPFarm.Local.Services.Where(s => s.GetType().Name.Contains("UserProfileService")).FirstOrDefault();

if (upServiceproxy != null)
{
 var upServiceApp = upServiceproxy.Applications.OfType<SPIisWebServiceApplication>().FirstOrDefault();
 if (upServiceApp == null)
 {
  Console.WriteLine("No User profile svc app found");
  return;
 }

 SPServiceContext ctx = SPServiceContext.GetContext(upServiceApp.ServiceApplicationProxyGroup, SPSiteSubscriptionIdentifier.Default);
 Microsoft.Office.Server.UserProfiles.UserProfileConfigManager upConfigManager = new Microsoft.Office.Server.UserProfiles.UserProfileConfigManager(ctx);

 // Let's create some user filters for the sync connection called "Local AD"
 var connection = upConfigManager.ConnectionManager["Local AD"];                
 

Then we’ll create the necessary FilterSetCondition objects. This is done with reflection in the CreateFilterSetCondition method of the Filters helper class. You need to specify in the Filters constructor whether you want to build  exclusion rules for user-related attributes (using the "user" parameter) or whether you want to build exclusion rules for group-related attributes (using the "group" parameter).
Finally we group the different user-related and group-related rules together in their own FilterSet objects by calling the GenerateFilterSetForConditions method. In this method you also specify if you want the different rules to be combined as "All apply" (=AND) or as "Any apply" (=OR).

//----------------------------------------------
// Create a set of user-related rules, any apply
//----------------------------------------------
Filters userFilter = new Filters("user");
var fsCondition1 = userFilter.CreateFilterSetCondition(connection, "userAccountControl", Filters.FilterOperator.Bit_on, "2");
var fsCondition2 = userFilter.CreateFilterSetCondition(connection, "company", Filters.FilterOperator.Equals, "ventigrate");

// get the actual filterset based on an OR between all conditions
List<object> conditions = new List<object>();
conditions.Add(fsCondition1);
conditions.Add(fsCondition2);
var userFilterSet = userFilter.GenerateFilterSetForConditions(conditions, Filters.FilterSetOperator.Or);

//------------------------------------------------------
// Create a set of group-related rules, all apply (=AND)
//------------------------------------------------------
Filters groupFilter = new Filters("group");
var groupCondition1 = groupFilter.CreateFilterSetCondition(connection, "name", Filters.FilterOperator.Equals, "test");

conditions = new List<object>();
conditions.Add(groupCondition1);
var groupFilterSet = groupFilter.GenerateFilterSetForConditions(conditions, Filters.FilterSetOperator.And);

Finally, we’ll add both FilterSet objects to the connection that we fetched in the first step.
Filters helper = new Filters("");
helper.SetExclusionFilters(connection, userFilterSet, groupFilterSet);

Result in SharePoint UI

As you can see, the rules provided in the code are now listed on the user profile connection page in the UI. For me this worked like a charm on my dev-machines, I hope it does the same on yours.
Once again: feel free to post a comment if you do have questions on using this...

Download link to the .NET code
Once again: if you're looking for a Powershell-friendly solution -> check my update

[UPDATED: moved the inline code to a codeplex project & rewritten some bits for extra clarity]